<?xml version="1.0" encoding="UTF-8"?>

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns="http://purl.org/rss/1.0/"
>

<channel rdf:about="http://memory.empressia.jp/">
<title>すふぃあの記憶</title>
<link>http://memory.empressia.jp/</link>
<description>すふぃあの感性のままに書きつづる記憶</description>
<dc:language>ja</dc:language>
<admin:generatorAgent rdf:resource="http://blog.sakura.ne.jp/" />
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://memory.empressia.jp/article/186866284.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/181920810.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/181735475.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/178003039.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/168997728.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/167469186.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/107610366.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/107606370.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/107602460.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/107596261.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/107593138.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/107581178.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/60610228.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/58268386.html" />
<rdf:li rdf:resource="http://memory.empressia.jp/article/54628506.html" />
</rdf:Seq>
</items>
</channel>

<item rdf:about="http://memory.empressia.jp/article/186866284.html">
<link>http://memory.empressia.jp/article/186866284.html</link>
<title>Visual Studio CodeでParaya MicroのWebアプリケーションを作る準備をする</title>
<description>☆☆☆はじめに☆☆☆Java AdventCalendar 2019 8日目の記事だよ。☆☆☆この記事のゴール☆☆☆この記事では、Visual Studio Codeで、Payara Microを使うWebアプリケーションを快適に作る環境を紹介することにしたよ。ほら、なんか、Jakarta EE 8？って言うのが出たみたいだし。というわけで、使うのはこの辺。・Visual Studio Code（以降、VSCode） ・Java11 ・Payara Micro ・Gralde..</description>
<dc:subject>プログラム</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2019-12-08T00:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<h2>☆☆☆はじめに☆☆☆</h2>
<p>Java AdventCalendar 2019 8日目の記事だよ。</p>
<h2>☆☆☆この記事のゴール☆☆☆</h2>
<p>この記事では、Visual Studio Codeで、<br />Payara Microを使うWebアプリケーションを快適に作る環境を紹介することにしたよ。<br />ほら、なんか、Jakarta EE 8？って言うのが出たみたいだし。</p>
<p>というわけで、使うのはこの辺。</p>
<blockquote>・Visual Studio Code（以降、VSCode）<br /> ・Java11<br /> ・Payara Micro<br /> ・Gralde</blockquote>
<p>Windows10の環境で作るけど、<br />後で出てくるrobocopyコマンドをrsyncで置き換えれば、<br />他の環境でも動くんじゃないかなぁ。</p>
<p>あ、作った結果は、<a href="https://github.com/Empressia/PayaraMicroSample/" target="_blank">ここ（GitHub）</a>にあるよ。</p>
<h2>☆☆☆目標☆☆☆</h2>
<p>で、まず、快適ってなんだろう？<br />ってことで、今回達成したい目標を上げておくね。<br />目標１．Gradleでビルドできること。<br />目標２．Gradleから実行できること。<br />目標３．VSCodeでビルドできること。<br />目標４．VSCodeから実行・デバッグできること。<br />目標５．VSCodeで起動したままreloadできること。<br />目標６．VSCodeで上の作業が楽にできること。</p>
<p>ほら、ソース書き換えて、すぐにコンパイルできて、<br />reloadできないとやってられないでしょ？<br />そこまではやらないとね！</p>
<h2>☆☆☆本編☆☆☆</h2>
<h3>☆☆手順～概要～☆☆</h3>
<p>さて、最終的なものもあるし、<br />VSCodeにJava11とJavaの拡張機能の設定まで終わっているって言う前提で、<br />記事としては細かい設定付近がメインに書いていくよ。それ以外はスルーで。</p>
<p>とはいえ、目標達成のための基本的な手順はこんなかんじ。<br />手順１．VSCodeのワークスペースを作ってGitリポジトリを初期化する。<br />手順２．Gradleの基本的な設定をする（目標１，２、３）。<br />手順３．GradleにVSCodeでのWebアプリ用の追加設定をする。<br />手順４．VSCodeのWebアプリ起動用の設定をする（目標４）。<br />手順５．VSCodeのWebアプリ更新用の設定をする（目標５）。<br />手順６．快適にするための設定をする（目標６）。</p>
<p>うん！簡単！って言うか、まぁ、ただの設定だしね。<br />GitHubのコミットは大隊この手順でコミットしてあるから参考にしてね。</p>
<h3>☆☆手順１．VSCodeのワークスペースを作ってGitリポジトリを初期化する☆☆</h3>
<p>ここで気をつけるのは、<br />.code-workspaceのファイルをプロジェクトのファイルと同じディレクトリに置くことかな。</p>
<p>RedHatの提供しているJavaの拡張機能のせいだと思うけど、<br />VSCodeのマルチワークスペースにうまく対応していないみたいで、<br />階層構造に制限があるみたいなんだよね。</p>
<p>実際、Javaのプロジェクト複数同じVSCodeのWindowで開くとうまく動かなくなったりするし。<br />あとは、.gitingnoreに、諸々の自動生成されるファイルを指定すれば完了！<br />この辺かな。<br />・Gradleの出力先のbuildディレクトリ。<br />・VSCodeの出力先のbinディレクトリ。<br />・Javaの拡張機能が自動生成する.project、.classpath、.settings。<br />・Gradleの本体の.gradleとローカル設定のgradle.properties。</p>
<h3>☆☆手順２．Gradleの基本的な設定をする（目標１，２、３）☆☆</h3>
<p>さて、実際に、Graldeの設定ファイルを置くよ。<br />まだ中身はGradle用のを書いているだけだからふつーだから、特筆するようなことは無いよ。<br />とりあえず、こんなかんじ。</p>
<pre class="prettyprint">plugins {<br />	id "java";<br />	id "war";<br />}<br /><br />ext.moduleName = project.name;<br /><br />repositories {<br />	jcenter();<br />}<br /><br />dependencies {<br />	// use Payara Micro.<br />	providedRuntime group:"fish.payara.extras", name:"payara-micro", version:"5.194";<br /><br />	// use JUnit test framework.<br />	testImplementation "org.hamcrest:hamcrest:2.2";<br />	testImplementation "org.junit.jupiter:junit-jupiter-api:5.5.2";<br />	testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.5.2";<br /><br />}<br /><br />tasks.withType(JavaCompile) {<br />	options.encoding "UTF-8";<br />	options.compilerArgs &lt;&lt; "-parameters";<br />}<br /><br />test {<br />	useJUnitPlatform();<br />}<br /><br />war {<br />	baseName moduleName;<br />	manifest {<br />		attributes (<br />			"Automatic-Module-Name": moduleName<br />		);<br />	}<br />}<br /><br />javadoc {<br />	source = sourceSets.main.allJava;<br />	options.charSet "UTF-8";<br />	options.encoding "UTF-8";<br />	options.addStringOption("Xdoclint:none", "-quiet");<br />}<br />task javadocJar(type: Jar) {<br />	baseName war.baseName;<br />	classifier "javadoc";<br />	from javadoc.destinationDir;<br />}<br />javadocJar.dependsOn(tasks.javadoc);<br />artifacts {<br />	archives javadocJar;<br />}<br /><br />task start(type: JavaExec) {<br />	main = "-jar";<br />	args = [configurations.providedRuntime.find { it.name.contains("payara-micro-") }.absolutePath, "--deploy", "\"${buildDir}/libs/${moduleName}.war\""];<br />}<br />start.dependsOn(tasks.war);</pre>
<p>Gradleの設定を一通り置いたら、<br />『./gradlew build』とか、『./graldew start』とかで、<br />ビルドと実行の確認ができるはず！</p>
<p>あと、code-workspaceのsettingsに、</p>
<pre class="prettyprint">		"java.configuration.updateBuildConfiguration": "automatic"</pre>
<p>を追加することをおすすめするよ。<br />VSCodeで、build.gradleを変更するたびに右下に更新する？って聞かれるから、<br />自動反映するならこの設定だね。別に聞かれて、Alwaysって選んでも同じ設定入るけど。</p>
<p>これで、.classpathが作られれば、VSCodeで、補完、コンパイルあたりができるようになるから、<br />目標１と目標２と目標３は達成だね！</p>
<h3>☆☆手順３．GradleにVSCodeでのWebアプリ用の追加設定をする☆☆</h3>
<p>さて、ここからが本番だよね。どうやって統合していくのかってところ。<br />ポイントになるのは、この辺。</p>
<p>・VSCodeのJava拡張がWebアプリに対応していない。<br />　→Graldeのプラグインで設定されるprovided～の設定がimplmentionとかruntime扱いになる。<br />　　→VSCodeで実行するには、ライブラリを明示的に指定しないとダメ。<br />　→webappが出力先にコピーされない。<br />・build.gradleでライブラリを追加とかしたときには、update projectしないと補完機能に反映されない。<br />　→でも、反映するときには、.classpathが上書きされて出力先がbin/mainに固定されてしまう。<br />　　→でも、クラスファイルは『デプロイ先の』WEB-INF/classesに無いとクラスローダーに『正しく』読み込まれない。<br />　　　※そうしなくても、一見動くけど、CDIとか（Java EE Security APIとかJerseyの実装とか）さわり始めると悲惨なことになる。<br />・JavaのDebugプラグインがWebアプリに対応していない。<br />　→Gradleのプラグインで設定されるprovided～の設定がimplmentionとかruntime扱いになる。<br />　　→勝手に実行時のクラスパスに追加される。<br />・JavaのDebugプラグインで起動時に指定するmainClassはclasspathRuntime上に存在しないと起動できない。<br />　→Gradleみたいに、mainClassを『-jar』とか指定できない。<br />・GradleでDLされたライブラリのパスはとてもわかりにくい。<br />　→明確なパスが得られないと困る。</p>
<p>このパズルを解けばok！</p>
<p>このために、Gradleさんには、実行時に必要なライブラリをコピーしてもらうことにするの。<br />copyRuntimeタスクとcopyLibrariesタスクを用意するよ。</p>
<p>copyRuntimeは、payara-microと、他に必要なものがあればって感じ。<br />DB接続用のjarとかはWEB-INF/libの方に入れてってどこかに書いてあった気がするから、<br />payara-micro-*.jarくらいな気がするけどね。だから、こんなかんじ。</p>
<pre class="prettyprint">task copyRuntime(type: Copy) {<br />	into "${buildDir}/runtime/";<br />	from configurations.providedRuntime - configurations.providedCompile;<br />}</pre>
<p>copyLibrariesは、warを作るときに、warの中に入るライブラリを用意するよ。<br />implementationとruntimeが対象かな。と言っても、直接は取り出せないから、こんなかんじで……。</p>
<pre class="prettyprint">def libraries = configurations.runtimeClasspath - configurations.providedRuntime;<br />task copyLibraries(type: Copy) {<br />	into "${buildDir}/libraries/";<br />	from libraries;<br />}</pre>
<p>で、<br />Javaのデバッグプラグインがクラスパスの自動生成をするのを抑制しなきゃいけない<br />＆<br />上でコピーしたものをクラスパスに入れないといけないんだけど、<br />Javaのデバッグプラグインだと、ワイルドカードが使えないんだよね。<br />だから、ファイルに設定を書き出して、それをクラスパスに指定することで、両方の問題を解決するよ。<br />そうすると、タスクはこんなかんじ。</p>
<pre class="prettyprint">task createArgfile {<br />	def argfile = file("${buildDir}/argfile/argfile.txt");<br />	def runtimeDirectory = file("${buildDir}/runtime/");<br />	def librariesDirectory = file("${buildDir}/libraries/");<br />	runtimeDirectory.mkdirs();<br />	inputs.dir(runtimeDirectory);<br />	if(libraries.isEmpty() == false) {<br />		librariesDirectory.mkdirs();<br />		inputs.dir(librariesDirectory);<br />	}<br />	outputs.files(argfile);<br />	doLast {<br />		argfile.parentFile.mkdirs();<br />		argfile.write("\"" + (runtimeDirectory.listFiles() + librariesDirectory.listFiles()).join(";").replaceAll("\\\\", "\\\\\\\\") + "\"", "UTF-8");<br />	}<br />}</pre>
<p>これは、コピーの後に実施しなとダメだから依存関係をつけるよ。</p>
<pre class="prettyprint">createArgfile.dependsOn(copyRuntime);<br />createArgfile.dependsOn(copyLibraries);</pre>
<p>Gradle側の設定はこれでおしまい。</p>
<h3>☆☆手順４．VSCodeのWebアプリ起動用の設定をする（目標４）☆☆</h3>
<p>続けて、これをVSCode側で受け取りながら実行できるようにするよ。<br />最終的に動かす構成としては、こんな配置にするね。</p>
<p>・/bin/main ← VSCodeがコンパイルしたクラスファイルとリソースが入る。<br />・/bin/application ← デプロイ先（名前はわたしが勝手に決めた）。<br />・/bin/server ← PayaraMicroのサーバーの場所（わたしが勝手に決めた）。</p>
<p>このために必要な設定は、以下の４つ。<br />・設定１．/bin/mainを/bin/application/WEB-INF/classesの下に配置するタスクを作る。<br />・設定２．/src/main/webappを/bin/applicationの下に配置するタスクを作る。<br />・設定３．GradleのVSCode用タスクと、設定１のタスクと設定２タスクを呼ぶタスクを作る。<br />・設定４．起動する設定を作る。</p>
<h4>☆設定１．/bin/mainを/bin/application/WEB-INF/classesの下に配置するタスクを作る☆</h4>
<p>/.vscode/tasks.jsonに、こんなかんじで作るよ。</p>
<pre class="prettyprint">		{<br />			"label": "CopyBinToApplication",<br />			"type": "shell",<br />			"command": "robocopy \"\"\"${workspaceFolder}/bin/main/\"\"\" \"\"\"${workspaceFolder}/bin/application/WEB-INF/classes/\"\"\" /e /purge /xo /nfl /ndl /np /njh /njs /r:1 /w:1; exit $(if($LASTEXITCODE -le 8) { 0 } elseif($LASTEXITCODE -eq 16) { if(Test-Path \"\"\"${workspaceFolder}/bin/application/WEB-INF/classes/\"\"\") { 0 } else { New-Item -Path \"\"\"${workspaceFolder}/bin/application/WEB-iNF/classes/\"\"\" -ItemType Directory } } else { $LASTEXITCODE })"<br />		}</pre>
<p>これでディレクトリが同期できる。</p>
<h4>☆設定２．/src/main/webappを/bin/applicationの下に配置するタスクを作る☆</h4>
<p>設定１と同じ感じでこんなかんじ。</p>
<pre class="prettyprint">		{<br />			"label": "CopyWebappToApplication",<br />			"type": "shell",<br />			"command": "robocopy \"\"\"${workspaceFolder}/src/main/webapp/\"\"\" \"\"\"${workspaceFolder}/bin/application/\"\"\" /e /purge /xo /nfl /ndl /np /njh /njs /r:1 /w:1 /xd WEB-INF /xf .reload; exit $(if($LASTEXITCODE -le 8) { 0 } elseif($LASTEXITCODE -eq 16) { if(Test-Path \"\"\"${workspaceFolder}/bin/application/\"\"\") { 0 } else { New-Item -Path \"\"\"${workspaceFolder}/bin/application/\"\"\" -ItemType Directory } } else { $LASTEXITCODE })"<br />		}</pre>
<p>でも、クラスファイルが入るWEB-INディレクトリFと、この後作る.reloadファイルを除外しているよ。<br />webappの中に、除外したディレクトリとファイルは作れなくなるけど、まぁ、作らないよね。<br />一応、リポジトリには、WEB-INFのディレクトリを同期するタスクもCopyWEB-INFToApplicationって名前で追加してあるよ。</p>
<h4>☆設定３．GradleのVSCode用タスクと、設定１のタスクと設定２タスクを呼ぶタスクを作る☆</h4>
<p>ここは迷うことなくこんなかんじ。</p>
<pre class="prettyprint">		{<br />			"label": "PrepareLaunchApplication",<br />			"type": "shell",<br />			"dependsOn": ["CopyWebappToApplication", "CopyWEB-INFToApplication", "CopyBinToApplication"],<br />			"command": "./gradlew copyRuntime copyLibraries createArgfile"<br />		}</pre>
<h4>☆設定４．起動する設定を作る☆</h4>
<p>ここからはlaunch.json。</p>
<pre class="prettyprint">		{<br />			"type": "java",<br />			"name": "Debug (Launch) - PayaraMicro",<br />			"request": "launch",<br />			"classPaths": ["@${workspaceFolder}/build/argfile/argfile.txt"],<br />			"mainClass": "fish.payara.micro.PayaraMicro",<br />			"vmArgs": "--add-modules java.se --add-exports java.base/jdk.internal.ref=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.management/sun.management=ALL-UNNAMED --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.util.stream=ALL-UNNAMED",<br />			"args": "--rootdir \"${workspaceFolder}/bin/server/\" --deploy \"${workspaceFolder}/bin/application/;\"",<br />			"preLaunchTask": "PrepareLaunchApplication"<br />		}</pre>
<p>classPathsにGradleで作った引数のファイルを指定して、<br />JavaDebugの拡張機能のクラスパスの自動生成を抑制しながらクラスパスを指定するよ。<br />mainClassには、Payara Microを-jar指定したときに呼ばれるクラス名を指定する。<br />あとは、rootdirとdeployを指定すればokだね。<br />あ、deployは;を最後につけてルートコンテキストに配置されるようにしているよ。</p>
<p>と、ここまでで起動できるはず。VSCodeでF5でデバッグ起動してみてね。</p>
<p>これで目標の４を達成だね。</p>
<h4>☆手順５．VSCodeのWebアプリ更新用の設定をする（目標５）☆</h4>
<p>ここまできたら後は簡単。Payara Microのreloadの設定をするよ。<br />デプロイ先に.reloadファイルを作れば良いから、tasks.jsonにこんなかんじに作るよ。</p>
<pre class="prettyprint">		{<br />			"label": "ReloadApplication",<br />			"type": "shell",<br />			"dependsOn": ["CopyWebappToApplication", "CopyWEB-INFToApplication", "CopyBinToApplication"],<br />			"command": "if(Test-Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\") { Set-ItemProperty -Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\" -Name LastWriteTime -Value $(Get-Date) } else { New-Item -Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\" }"<br />		}</pre>
<p>うん、これだけ。<br />サーバーを起動してある状態でこのタスクを動かすと、再配置されるのがログに出るはず。<br />これで目標５も達成だね。</p>
<h4>☆目標６．VSCodeで上の作業が楽にできること☆</h4>
<p>最後に、いろいろ楽にできるように設定しようね。</p>
<p>実行は、F5でできるから、reloadも簡単にできるようにしたいよね。<br />というわけで、VSCodeでのビルドタスクにreloadを設定するよ。こんなかんじ。</p>
<pre class="prettyprint">		{<br />			"label": "ReloadApplication",<br />			"type": "shell",<br />			"group": {<br />				"kind": "build",<br />				"isDefault": true<br />			},<br />			"dependsOn": ["CopyWebappToApplication", "CopyWEB-INFToApplication", "CopyBinToApplication"],<br />			"command": "if(Test-Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\") { Set-ItemProperty -Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\" -Name LastWriteTime -Value $(Get-Date) } else { New-Item -Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\" }"<br />		}</pre>
<p>デフォルトのビルドに設定したから、Ctrl+Shift+Bでいけるようになるよ。<br />webappだけにしたいなら、Reloadapplicationタスクじゃなくて、CopyWebappToApplicaitonタスクをビルドタスクにすれば良いと思う。<br />他のキーとかに割り当てたければ、keybindingsとかに設定するのもありかもね。</p>
<p>後はログの出し方かな。<br />ここまでの状態だと、タスクのログが前に出てきて、サーバーのログが見にくいよね。</p>
<p>だから、タスクのログをエラーの時だけ出すようにして、<br />サーバーのログをTerminalじゃなくてDebug Console側に出すようにするよ。<br />PayaraはVT100風のログを書くみたいだから、そっちの方が見やすいしね。</p>
<p>まず、タスクのログをエラーの時だけ出すようにするには、こんなかんじにするよ。</p>
<pre class="prettyprint">		{<br />			"label": "ReloadApplication",<br />			"type": "shell",<br />			"presentation": {<br />				"reveal": "silent"<br />			},<br />			"group": {<br />				"kind": "build",<br />				"isDefault": true<br />			},<br />			"dependsOn": ["CopyWebappToApplication", "CopyWEB-INFToApplication", "CopyBinToApplication"],<br />			"command": "if(Test-Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\") { Set-ItemProperty -Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\" -Name LastWriteTime -Value $(Get-Date) } else { New-Item -Path \"\"\"${workspaceFolder}/bin/application/.reload\"\"\" }"<br />		}</pre>
<p>これは、ReloadAppliactionの例だけど、<br />普段は見なくてもいいタスク全部に対して、presentation.revealをsilentに設定するよ。</p>
<p>これで、普段はログがでないけど、エラーの時は表示されるよ。<br />どうせ最終的にはサーバーのログでデプロイまで終わったか見るだろうから、これで良いと思うの。</p>
<p>で、あとは、サーバーのログだね。<br />launch.jsonでこんなかんじにするよ。</p>
<pre class="prettyprint">		{<br />			"type": "java",<br />			"name": "Debug (Launch) - PayaraMicro",<br />			"request": "launch",<br />			"console": "internalConsole",<br />			"classPaths": ["@${workspaceFolder}/build/argfile/argfile.txt"],<br />			"mainClass": "fish.payara.micro.PayaraMicro",<br />			"vmArgs": "--add-modules java.se --add-exports java.base/jdk.internal.ref=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.management/sun.management=ALL-UNNAMED --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.util.stream=ALL-UNNAMED",<br />			"args": "--rootdir \"${workspaceFolder}/bin/server/\" --deploy \"${workspaceFolder}/bin/application/;\"",<br />			"preLaunchTask": "PrepareLaunchApplication"<br />		}</pre>
<p>consoleをinternalConsoleにするだけだね。これで、ログがTerminalからDebug Consoleに移って、<br />色もつくはず。まぁ、この辺は好みかもしれないけどね。</p>
<p>別に、どっちでも、ブレークポイントとかはちゃんと止まるから、<br />標準入力をどうしたいかとかの好みで設定しても良いかもね。</p>
<p>ってことで、ここまでで、開発環境によらずに、Gradleでのビルドと実行をできるようにしながら、<br />VSCodeで簡単なキーバインドで起動と更新ができて、ログも確認しやすい状況が完成だよ！</p>
<p>全目標達成かな？（゜▽、゜</p>
<p>ここまででプロジェクトの名前が入っているのは、<br />ルートとプロジェクトのディレクトリ名と、<br />settings.gradleとcode-workspaceくらいかな。</p>
<p>他のOS版とかカスタマイズも、ここまで来れば結構簡単にできそうだよね。<br />robocopyをrsyncにするくらいかな？</p>
<p>以上！お疲れ様でしたなのだ！('-^*/</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/181920810.html">
<link>http://memory.empressia.jp/article/181920810.html</link>
<title>スパゲッティとライフサイクルとドメイン</title>
<description>この記事は、〇〇勉強してみた Advent Calendar 2017の23日目（12/23）の記事だよ。次の記事はtosssaurusさんの『canvasを1ヶ月勉強してみた』みたい。☆☆☆スパゲッティコードとの出会い☆☆☆最近は、Javaじゃなくて、C#とかTypeScriptとか使ってるんだよね。ほら、最近のアプリケーションって非同期処理が必須じゃない？そうすると、スパゲッティなコードを割と自然に解消できるasync/awaitってすごいなぁって思うんだよね。と言うか、..</description>
<dc:subject>プログラム</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2017-12-23T11:35:00+09:00</dc:date>
<content:encoded><![CDATA[
<p>この記事は、〇〇勉強してみた Advent Calendar 2017の23日目（12/23）の記事だよ。<br />次の記事は<a href="https://qiita.com/tosssaurus" target="_blank">tosssaurus</a>さんの『canvasを1ヶ月勉強してみた』みたい。</p>
<h2>☆☆☆スパゲッティコードとの出会い☆☆☆</h2>
<p>最近は、Javaじゃなくて、C#とかTypeScriptとか使ってるんだよね。<br />ほら、最近のアプリケーションって非同期処理が必須じゃない？<br />そうすると、スパゲッティなコードを割と自然に解消できるasync/awaitってすごいなぁって思うんだよね。</p>
<p>と言うか、ここのところ、いろんな人のコードをいっぱい見る機会があって、<br />async/awaitじゃなくても、『そういった構成』にしてないからスパゲッティになっているってコードを結構見かけて、<br />スパゲッティなコードってこういう風に作られていくんだなぁって思ったから、まとめてみようかなって。</p>
<p>Wikipediaとかみても、</p>
<blockquote>・goto文の濫用<br /> ・多重継承の濫用<br /> ・スコープの拡大<br /> ・技術不足</blockquote>
<p>とかしか書いてないし、<br />これって、特に最後の技術不足って、わかりません。って書いてあるようにしか思えないしね。<br />少しでも、スパゲッティなコードの理解と回避につながればいいなって思うの。</p>
<h2>☆☆☆スパゲッティの概念的な定義☆☆☆</h2>
<p>いつものことだけど、わたしの勝手な考えだから、鵜呑みにしておかしなことになっても知らないよ！</p>
<p>ってことで、いきなりわたしの答えから書いちゃうと、<br />次の２種類の書き方をしているプログラムをスパゲッティって言うんだと思う。<br />A. ライフサイクルに関する処理をライフサイクルの層から外してプログラムを構成する。<br />B. ドメイン重視のコンポーネント単位ではなく、アクション（１連の処理の流れ）としてプログラムを構成する。</p>
<p>ちなみに、これはだめな書き方だから、じゃぁ、どう書けば良いの？って観点で行くと、<br />『ライフサイクルやドメインにそった開発をする』っていうことかな。<br />そういう意味では、DDDな書き方している人はスパゲッティなコード書くことないのかもしれないね。</p>
<p>まぁ、どう書くべきか、みたいなのは、時間とともに変わっていくと思うけど。</p>
<h2>☆☆☆スパゲッティの具体な成長例☆☆☆</h2>
<p>じゃ、わかりやすい例を。<br />例だから、現実的かどうかとかは大目に見てもらえると助かる感じ？<br />わかりやすさ重視で！</p>
<p>一番わかりやすいのはこう言うのかな……。<br />『画面のボタンを押して、処理が終わるまで押せなくする』みたいな。<br />あんまりしない気もするけど例だから気にしないっ。</p>
<p>１.処理の配置間違えた。</p>
<pre class="prettyprint">private void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	this.doSomething();<br />}<br /><br />private void doSomething() {<br />	// なが～いなが～い処理。<br />	Thread.Sleep(2000);<br />	this.Button.IsEnabled = true;<br />}</pre>
<p>あ、えっと、一応、UIにボタン（Button）があって、押すとButton_Clickが呼ばれるってことで。<br />ここの例は、VisualStudioでWPFのプロジェクトとテンプレで作って、MainWindowにButtonを配置してイベントを作っただけだよ。</p>
<p>さて、こんなコード書かないよ！って？いや、まぁ、普通はそうだとわたしも思うんだけど。<br />と言うか、そもそもボタンの切り替え動かないし……っていうのは置いといて。<br />特に、メソッドを切り出すような処理がなければ、こういう書き方することはあり得ないね。</p>
<p>これは、定義のAになっちゃっているのがよくわかると思うんだよね。<br />切り替えている処理が別々のメソッドに分散しちゃってるから。</p>
<p>じゃ、次はもうちょっと、書きそうな例ってことでちゃんとボタンが切り替わるように非同期にするよ。</p>
<p>２.非同期にした（非同期場所間違えた）。</p>
<pre class="prettyprint">private void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	this.doSomething();<br />}<br /><br />private void doSomething() {<br />	// なが～いなが～い非同期処理。<br />	Task.Run(() =&gt; {<br />		Thread.Sleep(2000);<br />		this.Dispatcher.Invoke(() =&gt; {<br />			this.Button.IsEnabled = true;<br />		});<br />	});<br />}</pre>
<p>このくらいになると、書く人がちょっとだけいそうな気がする。</p>
<p>え？あ、うん、いや、まぁ、元の問題と対応があってないのはわかってるんだけど、<br />この流れだと、こう考えて書く人多そうだなってことでよろしく！<br />JavaScriptなWebだと、元に戻すのにわざわざDispatcherとかの行はいらないから、シンプルになって、余計に書く人多そう。</p>
<p>＃１で書かないって人は、もちろん、＃２も書かないと思うんだけど。<br />じゃぁ、次でやっとUIの処理まとめる気になったことにする。<br />実際は、doSomethingとかがさらに複雑になるとか。呼び出し元の処理が複雑になると気づくはずだよね。</p>
<p>３.UIの記述場所をまとめたつもり。</p>
<pre class="prettyprint">private void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	this.doSomething(() =&gt; {<br />		this.Button.IsEnabled = true;<br />	});<br />}<br /><br />private void doSomething(Action callback) {<br />	// なが～いなが～い非同期処理。<br />	Task.Run(() =&gt; {<br />		Thread.Sleep(2000);<br />		this.Dispatcher.Invoke(callback);<br />	});<br />}</pre>
<p>さて、これは、さらに書く人多そうな形だと思うんだよね。<br />コールバックっていうんだっけ。なんかすごく好きな人がいるみたいだよね。<br />わたしは、あまり使うの好きくないんだけど。</p>
<p>でも、doSomethingのメソッドがUIでの後処理を意識したメソッドになってしまっているね。<br />もうちょっと、ちゃんとUIを寄せてみようかな。</p>
<p>４.コールバックでUIと分ける。</p>
<pre class="prettyprint">private void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	this.doSomething(() =&gt; {<br />		this.Dispatcher.Invoke(() =&gt; {<br />			this.Button.IsEnabled = true;<br />		});<br />	});<br />}<br /><br />private void doSomething(Action callback) {<br />	// なが～いなが～い非同期処理。<br />	Task.Run(() =&gt; {<br />		Thread.Sleep(2000);<br />		callback();<br />	});<br />}</pre>
<p>うん、まぁ、何となく良くなった感じ？<br />じゃぁ、ここで、なが～いなが～い処理、ずっと適当にSleepとか書いてきたんだけど、<br />まぁ、普通は通信処理とかになったりするわけで、当然例外とかあり得るわけだよね。<br />そうしたらどうなるかなぁって。</p>
<p>はっ、＃２でおかしい対応していたことがばれてしまう！？（いまさら</p>
<p>５.コールバックでUIと分ける。</p>
<pre class="prettyprint">private void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	this.doSomething(() =&gt; {<br />		this.doSomething(<br />			() =&gt; {<br />				this.Dispatcher.Invoke(() =&gt; {<br />					this.Button.IsEnabled = true;<br />				});<br />			},<br />			(message) =&gt; {<br />				this.Dispatcher.Invoke(() =&gt; {<br />					MessageBox.Show(message);<br />				});<br />			}<br />		);<br />	});<br />}<br /><br />private void doSomething(Action callback, Action errorHandler) {<br />	// なが～いなが～い非同期処理。<br />	Task.Run(() =&gt; {<br />		try {<br />			Thread.Sleep(2000);<br />			// throw new Exception("hoge");<br />		} catch(Exception ex) {<br />			errorHandler(ex.Message);<br />		} finally {<br />			callback();<br />		}<br />	});<br />}</pre>
<p>もうすでに、これはない……って感じになってきているわけだけど、<br />たぶん、少しずつ『処理を追加していってる』とか、『全体の構成じゃなくて一部だけ見直している』とかだと、<br />こうなっちゃう人、割といるんじゃないかなぁ？<br />ここでは、finallyにcallback置いたけど、finallyじゃなければ、Button_Clickの実装はまた変わってくるよね。<br />doSomethingの作りに非常に強く影響受けている感じ。<br />ほんと、ちょっとした変更も一蓮托生で、動き全部把握しておかないと即死って感じ？（物騒</p>
<p>まぁ、もうこのくらいでいいかな。<br />というわけで（？）、こういう風にコードが成長していくとスパゲッティになっていくって言うのは伝わったかなって。</p>
<p>じゃぁ、どうすればいいのかっていうと、次のような感じ。<br />まずは、Taskを戻すよ。</p>
<p>６．制御を戻す。</p>
<pre class="prettyprint">private void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	Task task = this.doSomething();<br />	Task wait = Task.Run(() =&gt; {<br />		try {<br />			task.Wait();<br />		} catch(AggregateException exs) {<br />			this.Dispatcher.Invoke(() =&gt; {<br />				exs.Handle((ex) =&gt; {<br />					MessageBox.Show(ex.Message);<br />					return true;<br />				});<br />			});<br />		} finally {<br />			this.Dispatcher.Invoke(() =&gt; {<br />				this.Button.IsEnabled = true;<br />			});<br />		}<br />	});<br />}<br /><br />private Task doSomething() {<br />	// なが～いなが～い非同期処理。<br />	Task task =  Task.Run(() =&gt; {<br />		Thread.Sleep(2000);<br />		// throw new Exception("hoge");<br />	});<br />	return task;<br />}</pre>
<p>スパゲッティになった状態ではっきりわかる最大の問題は、<br />『非同期の制御を元に戻していない』ことだから、そこを直した感じ。</p>
<p>Taskを作ってる箇所が複数になって、もう＃２あたりでのこともはっきり目に見えちゃうね。</p>
<p>じゃ、ちょっと、見にくいから、async/await使うよ。</p>
<p>７．async/awaitで。</p>
<pre class="prettyprint">private async void Button_Click(object sender, RoutedEventArgs e) {<br />	this.Button.IsEnabled = false;<br />	try {<br />		await this.doSomething();<br />	} catch(Exception ex) {<br />		MessageBox.Show(ex.Message);<br />	} finally {<br />		this.Button.IsEnabled = true;<br />	}<br />}<br /><br />private Task doSomething() {<br />	// なが～いなが～い非同期処理。<br />	Task task =  Task.Run(() =&gt; {<br />		Thread.Sleep(2000);<br />		// throw new Exception("hoge");<br />	});<br />	return task;<br />}</pre>
<p>どうかな～。だいぶわかりやすくなったかな～。<br />async/awaitを使うと、非同期関係のところは、正しいライフサイクルに導いてくれる気がするんだよね。<br />補助してくれるというか。</p>
<p>まぁ、別にasync/await使おうってことじゃなくて、<br />非同期はTaskを戻すのがライフサイクルとかドメインを考える上で重要ってこと。<br />そして、ライフサイクルとかドメインを考えないとスパゲッティになっていくってこと。</p>
<p>どんどん処理を追加していくんじゃなくて、<br />ちゃんと、ライフサイクルとかドメインを考えて構成しないと、スパゲッティになるよってことっ。</p>
<p>たぶん、スパゲッティコードが生まれる理由は、<br />コンポーネント単位でプログラムを書かないで、<br />アクション単位でコードをがんがん追加しちゃうのが原因じゃないかな。<br />言葉の定義怪しいけど。</p>
<p>今回も、Button_ClickにUIに関する制御＆表現を、doSomethingに実際に実行＆実現したいことを、って、<br />分けていれば、最初から＃６とか＃７の方向に進んでいくはずだしね。</p>
<p>ただ、ライフサイクルは、割と簡単だけど、<br />実際には、ドメインにそって適宜？分割していくのはすごく難しいと思う。</p>
<p>個人で開発していると、ドメインが1個に見えてきちゃうし、<br />チームで開発していると、ドメインの内部の関係性を把握しにくい感じがする。</p>
<h2>☆☆☆スパゲッティを探す☆☆☆</h2>
<p>さて、視点をちょっと変えて、すでにスパゲッティになっているのをどう見つけるか、ちょっと考えてみる。<br />・引数を、使わずに次のメソッドにただ渡している。<br />　使われているところから発生源を追うんじゃなくて、変数がnewとかされてから、いったいどこまでそれが持ち運ばれていくかってみていく。<br />　そうすると、だいたい、さっきみたいにUIの最後の処理をするためーとかで、UIそのものをドメイン側の処理に横流ししているとか見つけることが……ある？<br />　まぁ、あったのっ。すっごく驚いたけど。<br />・フィールド経由でたまたまそこにある呼び出し先になるメソッドに値を渡す。<br />　たまに見かけたコードだと、ドメインのメソッド、さっきの例だとdoSomethingは別のクラスとかに分けたりするわけだけど、<br />　省略のために規模が小さければクラス分けないこともあると思うんだよね。<br />　と言うか、最初は小さいから分けないことも多いと思うの。</p>
<p>　で、引数で渡すのはおかしい！みたいなことをすると、フィールド経由で渡したりして……、もちろん、これもアウトだよね。<br />　あとで、クラス分けるとき困っちゃう。</p>
<p>　他のケースだと、全く別のメソッド呼び出していって、<br />　コールバックで戻ってきたときに使うから、と言う理由で、フィールドに保存しておくみたいな考えっぽいもの。<br />　これも結構すごかった。</p>
<p>　かければ何でもいいとはわたしは思わないのだ。</p>
<p>・クラスに関係ないインターフェースを実装している。<br />　これも結構あるかな。さっきの引数で持ち回るとき用でやっていたりするコードを見かけることがあるよ。<br />　たぶん、ラムダ式とかがわからなかったり、クラスの責任とかを考えなかったり、っていうことだと思うけど。<br />　かるく、同じアプリケーションコードの中でVisitorパターン発生してるよねきっと。</p>
<p>　インターフェースをクラスに実装している例がいっぱいネット上に転がっているからなのかな？関係ない？</p>
<p>・Task、Future、Promiseを作って、returnしていない（コントロールが手放されている）。<br />　定番でわかりやすいよね！例としても使わせてもらったし！<br />　ちなみに、UIとかのもともと戻り値の型がvoidなところで保持する必要がある場合は、<br />　フィールドに維持して管理するしかない……よね。</p>
<h2>☆☆☆まとめ☆☆☆</h2>
<p>長々書いてきたけど、結局、最初に書いた定義のAとBによってスパゲッティはできていくと思うの。<br />ここまで来たら、定義のAとBの違いは、もうわかっちゃっていると思うんだけど、<br />プラットフォームとかシステムに依存した仕組みとか制限によるものか、<br />アプリケーションのドメインによる表現とかによるものかの違いかな。</p>
<p>Aがわかるけど、Bがわからないひとは、BをAに強引に当てはめて恐ろしいスパゲッティを作ることが多い気がする。<br />両方わからない人はいろいろ警戒するから、そこまで元々巨大なスパゲッティは積極的には作らないかな。</p>
<p>つまり、自分がいったいどんなドメインを扱っているのかしっかりとらえるのが重要ってこと。<br />プログラムは人によって違う書き方になるっていうけど、わたしはあまりそう思わない。</p>
<p>ドメインの設計は人によって結構大きく変わるけど、そこが落ち着いたら、<br />それを表現する最適なコードは割と誰が書いても同じになると思っているよ。<br />まぁ、設計っていうか構成を考えるのとプログラムを一緒にすると、人によって違うっていえるかもだけど。</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/181735475.html">
<link>http://memory.empressia.jp/article/181735475.html</link>
<title>コメントの書き方～すふぃあ流コメント術～</title>
<description>☆☆☆はじめに☆☆☆この記事は、個人開発 Advent Calendar 2017の2日目（12/02）の記事だよ。前の記事は、kappy0322さんの『個人開発における開発プロセスを公開してみる (2017年冬)』だよ。次の記事はturanukimaruさんの『ファイアーエムブレムヒーローズの戦闘結果計算ツールをKotlinでDDD的に作ってみた』みたい。☆☆☆経緯とか☆☆☆kappy0322さんから、Advent Calendarの招待を受けて、あれ、なんで招待されたのか..</description>
<dc:subject>プログラム</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2017-12-02T12:10:00+09:00</dc:date>
<content:encoded><![CDATA[
<h2>☆☆☆はじめに☆☆☆</h2>
<p>この記事は、個人開発 Advent Calendar 2017の2日目（12/02）の記事だよ。<br />前の記事は、<a href="https://qiita.com/kappy0322" target="_blank">kappy0322さん</a>の『<a href="https://qiita.com/kappy0322/items/3b8301da2e889e962e5e" target="_blank">個人開発における開発プロセスを公開してみる (2017年冬)</a>』だよ。<br />次の記事は<a href="https://qiita.com/turanukimaru" target="_blank">turanukimaru</a>さんの『ファイアーエムブレムヒーローズの戦闘結果計算ツールをKotlinでDDD的に作ってみた』みたい。</p>
<h2>☆☆☆経緯とか☆☆☆</h2>
<p>kappy0322さんから、Advent Calendarの招待を受けて、あれ、なんで招待されたのかな～、とか戸惑いながら、<br />そういえば、前から書こうかなーって思っていた、コメントの書き方、この際だから書こうかな！と思い立って書くことにしたの。<br />ここで言うコメントって言うのは、ドキュメントコメントではないコメントのことね。</p>
<p>わたしは、プロの教育とか受けてないから、コメントの書き方も、当然、自己流なんだけど、<br />最近、いろんな人のソースコードを見る機会があって、<br />『何でこんなコメント書いてるの？』って思うのをいっぱい見てびっくりしたんだよね。たぶん、プロの人のも結構あったと思うんだけど……。</p>
<h2>☆☆☆コメントの書き方の問題☆☆☆</h2>
<p>で、どうも教育を受けたことがある人に聞くと、<br />『そのコードを書いた理由をコメントで書きなさい』<br />って、教えられているみたい。</p>
<p>実際に、ググってみると、本当に、理由を書きなさいみたいなのがいっぱい出てくる。<br />うん、いや、そうなんだけどさ、なんでそんな、てけとーな教え方してるの……？って思っちゃった。<br />いや、だってさ、こういうコメント書かれても困るでしょ。</p>
<pre class="prettyprint">// データが複数あるのでfor文で回す。<br />for(int i = 0; i &lt; elements.length; ++i) {<br />	// 略<br />}</pre>
<pre class="prettyprint">// 処理を分けるために分岐する。<br />if(something.getID() &gt; 0) {<br />	// 略<br />}</pre>
<p>極端な例ではあるけど、教えられたとおりに、『理由』を書いているよね。<br />でも、良いコメントだね！……ってならないよね？<br />だから、『理由を書く』っていうのは不十分だと思うの。</p>
<p>って、こういうところから、何が問題かとか、分析していってもいいんだけど、<br />わかっている人にはわかっていることだろうし、あんまりおもしろくないから、<br />その辺は省略して、わたしがコメントを書くときの考え方を紹介していくよ。</p>
<p>別に、書くのがめんどくさくなってきたとかそういうわけじゃない……から、ね？</p>
<p>あと、何度も書くけど、わたしはそういう系の教育受けてないから、これが本当によい方法かどうかはわからない～。<br />いろいろ比較とか研究とかできるわけでもないし。<br />ただ、個人開発で困っている人に、少しでも役に立てば良いな、とは思っているよ。</p>
<h2>☆☆☆すふぃあ流コメント術☆☆☆</h2>
<p>最大のポリシーは、<br />『あとで変更するときの判断基準を書く。』<br />ということ。</p>
<p>だって、システムとかアプリケーションを、作って～リリースして～書き換えて～ってやっていると、<br />コメントが必要な時って、『そういうとき』だよね？</p>
<p>このポリシーから、コメントを書く、より具体的な方針は、、<br />『普通ではない書き方をしたところには、その根拠を示す。』<br />ということになると思っているよ。</p>
<p>コメントを書いていないところは、コードそのままの意図で、おかしな動きをしていたら、それはバグとして直されたり、最適化されたりする。<br />コメントがあるところは、その妥当性をコメントをみて判断してから、変更や修正ができるってわけ。</p>
<p>たとえば、</p>
<pre class="prettyprint">// このメソッドの定義より、要素の最後には、○○を含めて返す必要がある。<br />// ○○は、××の特性を含むため、△△の処理は行ってはならないので除外されるように条件を調整している。<br />for(int = 0; i &lt; elements.length - 1; ++i) {<br />	// 略<br />}<br />return elements;</pre>
<p>まぁ、サンプルだから、もっと別のコード書きなよっていうのはあると思うんだけど、そこはそれ。<br />でも、こうすれば、なんで-1しているの？？っていう理由と、それが妥当かどうかの判断ができるはず。<br />実は、この見解が間違っているとか、見解が変わったとかなら、よりどころとなっているコメントごと直すって感じ。</p>
<p>単に、『理由』ではなくて、変更するときの『判断基準』となることを書いていくのが重要ってこと。<br />ほんと、ただ、それだけだよ？みんなが知りたいことってそういうことじゃないのかな？</p>
<p>コメントが長くなるじゃん！って言うのもあるかもしれないけど、<br />必要なことは書くべきだと思うの。<br />くだらないことじゃないわけだからね。</p>
<p>かなり自分でも困ったソースコードの付近には、15行超えるようなコメント書いたこともあったかな。<br />まぁ、稀だけど。<br />読んで判断基準にならないコメントなんて、だからなんなの！って叫びたくなるだけじゃない？</p>
<h2>☆☆☆弱点とか☆☆☆</h2>
<p>この書き方の最大の弱点は、『普通』っていうのを共有しないといけないこと。<br />基本的には、必要十分なコードで、設計ポリシーにあっていれば問題は起きないはず。</p>
<p>裏を返すと、実力に大きな差があるチームだと、失敗すると思う。<br />まぁ、足手まといな人がいると、きっと別の意味で失敗するだろうから、大丈夫な気もするんだけどね。<br />その辺は想像だから、よくわかんない。</p>
<p>もう一つは、設計が微妙だとコメントがすっごく増えること。<br />だから、作っているときに気づいた、設計が微妙なところはすぐに直していかないとダメかも。<br />その設計がおかしいと言うコメントを大量に残すことになるからね。</p>
<p>って、この書き方じゃなくてもコメントが増えること自体は同じかもしれないけど。</p>
<h2>☆☆☆よくありそうな質問と回答☆☆☆</h2>
<p>Q. 難しいコードに説明コメントつけるのはどうなの？<br />A.<br />難しいって、普通じゃないってことでしょ？それは書き換える基準も含めてコメント書かないとだめじゃない？<br />まさか、難しいのが普通になって……ないよね？<br />もし、どこもかしこも難しくてコメントだらけになりそうって言うことなら、<br />それは設計がおかしいんだと思うよ。</p>
<p>Q. 必要なコメントが書かれていなかったら、困ったことになるんじゃないの？<br />A.<br />それは、つまり、書き換えるときに、必要な変更の判断基準が書かれてなくて、<br />さくさく書き換えた結果、間違った書き換えになっちゃうんじゃないか、って言うことだよね。</p>
<p>良いんじゃない？だって、前書いた人（過去の自分含む）さえ、それに気づいてなかったんでしょ？<br />気づけて良かったじゃない。<br />と言うだけだと思う。</p>
<p>てけとーに書かれていたのが、たまたま、動いていただけで、それが壊れるだけのこと。<br />壊れるのは、困るけど、それは、元を書いた人の問題だと思う。</p>
<p>このコメントの書き方をしていると、書き換える元にコメントを残さなかったことが悪いってことになんじゃないかな。<br />いや、まぁ、どっちが悪いとか言っても慰めにもならないんだけどさ。</p>
<p>なんか、どんなひどいコードでも、動いていれば正義みたいな考え方もあるみたいだけど、<br />それが『絶対』だとはわたしは思わない。もちろん、動いていることも大事だけどね。<br />動いていても、手抜きを許すわけじゃないし、ミスはありえることだと思う。</p>
<p>『過去のわたし！しっかりしろ！』って思うこと、よくある。<br />そこには、きっと、コメント以外の対策みたいなのが必要なんじゃないかと思ってみたりするよ。</p>
<p>Q. 書き直す予定がないときもコメント書くべきなの？<br />A.<br />書き直す予定がないって言うのは、バグが絶対にないってことかな。<br />それとも、バグがあったら、そのソースコードを全く参照しないで作り直すって言うことかな。<br />というわけで、そんなことは、まず、あり得ないと思うから、書いた方が良いと思う。</p>
<p>ただ、ちょっとしたスクリプトとかまで毎回書くかとかは知らないっ。<br />でも、やっぱり、あとでスクリプトを発掘すると、<br />あれ？これなんでこうなっているんだっけ？みたいなことは、わたしもよくあるけどね？（゜▽、゜</p>
<h2>☆☆☆まとめ☆☆☆</h2>
<p>さぁ、『理由』じゃなくて、『判断基準』をコメントに書いていってみようよ！<br />コメントを書くときに後の人のことを想像して考えることがやりやすくなるんじゃないかな。<br />わたしは、何にも責任とれないけどね！('-^*/</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/178003039.html">
<link>http://memory.empressia.jp/article/178003039.html</link>
<title>RoslynのAanlyzerでC#のコードを分析するライブラリ作ってみたよ</title>
<description>これは、C# Advent Calendar 2016の12月11日（11日目）の記事なのだ。昨日は、@tanaka_733さんのC# 7 on Linux！今日は、RoslynのAnalyzerを使ってみた話を書くよ。☆☆☆コーディングスタイルを検証する仕組みへの不満☆☆☆突然だけど、わたしのコーディングスタイルって、C#とJavaの中間みたいな書き方なんだよね。例えばこんな感じ。public class Project {	// フィールドだいたいは大文字ではじめるからC..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2016-12-11T12:50:19+09:00</dc:date>
<content:encoded><![CDATA[
<p>これは、C# Advent Calendar 2016の12月11日（11日目）の記事なのだ。</p>
<p>昨日は、@tanaka_733さんの<a href="http://tech.tanaka733.net/entry/csharp-7-on-linux-preview" target="_blank">C# 7 on Linux</a>！<br />今日は、RoslynのAnalyzerを使ってみた話を書くよ。</p>
<h2>☆☆☆コーディングスタイルを検証する仕組みへの不満☆☆☆</h2>
<p>突然だけど、わたしのコーディングスタイルって、C#とJavaの中間みたいな書き方なんだよね。<br />例えばこんな感じ。</p>
<pre class="prettyprint">public class Project {<br />	// フィールドだいたいは大文字ではじめるからC#風？<br />	private string Field;<br />	// メソッドはだいたい小文字ではじめるからJava風？<br />	// 『{』は行末に書くからJava風？<br />	public void doSomething() {<br />		string f = this.Field;<br />	}<br />}</pre>
<p>で、VisualStudioってIntelliSenceが優秀だからか、<br />あまりコーディングスタイルを細かくチェックする仕組みがない気がする。<br />コーディングスタイルっていうか、静的解析？みたいな分類になるのかな？</p>
<p>いや、ほら、JavaのCheckstyleみたいに細かいチェックをするものって、<br />StyleCopとかFxCopとかだと思うんだけど、<br />これって、あんまり自由にカスタマイズする感じじゃない気がするの。</p>
<p>で、いろいろサイトめぐっていたら、<br />StyleCopがRoslyn使ってる～とか言う話をとか見て、興味がわいてきた感じ。<br />ないなら、自分で作っちゃえば良いんだよね。</p>
<h2>☆☆☆RoslynなAnalyzerの特徴☆☆☆</h2>
<p>細かいこと書き出すと、ホントに書きたいことから遠ざかりそうだから、<br />基本的な作り方は、<a href="http://neue.cc/2014/11/20_485.html" target="_blank">neueccさんの記事</a>を参考にしてもらう感じで。<br />ここでは概要だけにとどめてさくさく具体的な話へいこ～。</p>
<p>えっと、RoslynのDiagnosticAnalyzerってクラスを使うと、C#のコードの解析を簡単に作れる。<br />特に、次の点がとっても便利っ。もちろん、もっといっぱい便利な特徴はあるんだけど。<br />１．字句解析とか構文解析とか諸々省略して、SyntaxTreeがそろった状態から作業が始められる。<br />２．テンプレートプロジェクト？があるから、すぐ作るのをはじめられる。<br />３．作ったのは、NuGetで簡単に適用できる。<br />４．レポートした結果がVisualStudioと連動して見られる。</p>
<p>さぁ、じゃぁ、もう、プロジェクト作った前提で、<br />どのくらい簡単にかけるのか見てみよ～。</p>
<h2>☆☆☆具体的な例☆☆☆</h2>
<p>Initializeメソッドで具体的な検証アクションを登録しておくんだけど、<br />解析するフェーズはいくつかあって、以下の4つのアクションから選んで解析コードを書く感じ。</p>
<pre class="prettyprint">public override void Initialize(AnalysisContext context) {<br />	context.RegisterSyntaxTreeAction((c) =&gt; { });<br />	context.RegisterSymbolAction((c) =&gt; { }, SymbolKind.Assembly);<br />	context.RegisterSyntaxNodeAction((c) =&gt; { }, SyntaxKind.None);<br />	context.RegisterSemanticModelAction((c) =&gt; { });<br />}</pre>
<p>わたしの場合、使うのは、RegisterSyntaxNodeActionとRegisterSyntaxTreeActionかな。</p>
<p>RegisterSyntaxNodeActionは、名前の通り、読みたいNodeを指定して、そこから解析をしていく時に使うもの。<br />例えば、if文の書き方を確認したい！とかはこっちを使うといい感じ。</p>
<p>RegisterSyntaxTreeActionは、SyntaxTreeを自分で頭から読んでいくもの。<br />特に空白系を確認したい場合はこっちを使うといい感じ。</p>
<p>RegisterSyntaxNodeActionは、どうしてもNodeを基準にしないといけないから、Node無関係に空白を解析するのには向かないんだよね。</p>
<p>ちょっとサンプルを書くと、ブロックの手前にスペースが1個あるかどうかの確認はこんな感じ。</p>
<pre class="prettyprint">public override void Initialize(AnalysisContext context) {<br />	context.RegisterSyntaxNodeAction((c) =&gt; { analyze(c); }, SyntaxKind.Block);<br />}<br />private static void analyze(SyntaxNodeAnalysisContext context) {<br />	SyntaxNode node = context.Node;<br />	SyntaxToken firstToken = node.GetFirstToken();<br />	SyntaxToken previousToken = firstToken.GetPreviousToken();<br />	SyntaxTriviaList trivias = previousToken.TrailingTrivia;<br />	// 存在しない場合も0になる。<br />	int triviasLength = trivias.FullSpan.End - trivias.FullSpan.Start;<br />	if((triviasLength == 1) &amp;&amp; trivias.First().IsKind(SyntaxKind.WhitespaceTrivia) &amp;&amp;<br />		(previousToken.Parent.SyntaxTree.GetText().GetSubText(trivias.FullSpan).ToString() == " ")) {<br />		// ブロックの直前にスペースが1個ある。<br />	} else {<br />		// Ruleはテンプレ生成したときにあるやつなので説明省略で。<br />		Diagnostic diagnostic = Diagnostic.Create(Rule, node.GetLocation(), "メッセージ");<br />		context.ReportDiagnostic(diagnostic);<br />	}<br />}</pre>
<p>う～ん、簡単だね！<br />いや、まぁ、解析のロジック自体は、もちろん、みんなガンバレ！って話になるわけだけど、<br />構成は……簡単でしょ？</p>
<p>もう一種類も。<br />これは、行末に空白があったらダメだよっていうやつ。</p>
<pre class="prettyprint">public override void Initialize(AnalysisContext context) {<br />	context.RegisterSyntaxTreeAction((c) =&gt; { analyze(c); });<br />}<br />private static void analyze(SyntaxTreeAnalysisContext context) {<br />	SyntaxNode root = context.Tree.GetCompilationUnitRoot(context.CancellationToken);<br />	IEnumerable trivias = root.DescendantTrivia();<br />	{<br />		SyntaxTrivia previousTrivia = default(SyntaxTrivia);<br />		foreach(SyntaxTrivia trivia in trivias) {<br />			if(trivia.IsKind(SyntaxKind.EndOfLineTrivia) &amp;&amp; previousTrivia.IsKind(SyntaxKind.WhitespaceTrivia)) {<br />				if(trivia.FullSpan.Start == previousTrivia.FullSpan.End) {<br />					Diagnostic diagnostic = Diagnostic.Create(Rule, trivia.GetLocation(), "メッセージ");<br />					context.ReportDiagnostic(diagnostic);<br />				}<br />			}<br />			previousTrivia = trivia;<br />		}<br />		{<br />			SyntaxTrivia trivia = trivias.LastOrDefault();<br />			if(trivia.IsKind(SyntaxKind.WhitespaceTrivia) &amp;&amp; (trivia.FullSpan.End == root.FullSpan.End)) {<br />				Diagnostic diagnostic = Diagnostic.Create(Rule, trivia.GetLocation(), "メッセージ");<br />				context.ReportDiagnostic(diagnostic);<br />			}<br />		}<br />	}<br />}</pre>
<p>いける！いける！<br />StyleCopとか、何を検証しているのかわからないけど、結構カオスなコード書いてあるように見えるんだけど、<br />ちょっとしたの書くなら、検証する内容がはっきりイメージできれば、結構、簡単にかけるよ！<br />……はっきり……イメージできれば……ね……。</p>
<h2>☆☆☆作ってみたライブラリ☆☆☆</h2>
<p>ということで、作ったライブラリは、NuGetにおいてあるから、良ければ試してみてね。<br />『ECheckStyle』で登録してあるから検索すれば出てくると思うよ。<br /><a href="https://www.nuget.org/packages/ECheckStyle/" target="_blank">NuGetパッケージのサイト</a><br /><a href="http://www.empressia.jp/proj/ecs/ecs.html" target="_blank">プロジェクトサイト</a><br />折角だから、今日アップデートしておいたっ。</p>
<h2>☆☆☆注意点☆☆☆</h2>
<p>作る上での注意点とかを簡単にあげておくと、<br />１．解析でできるSyntaxTreeが一意ではない。<br />　　解析ロジック考える時に、いろいろなサイトでSyntax Visualizer見ると良いよって書いてあるんだけど、<br />　　これ、実は、エディタ開いた状態で行われる解析と、開いてない状態で行われる解析で、作られるSyntaxTreeが違うの。<br />　　あ、エディタ上の色分けもSyntax Visualizerのとは違うッぽいからこれも参考にするとダメみたいだよ。<br />　　理由は知らないけど、そのせいでだいぶ苦労したっ。特にコメント回りっ！<br />２．CodeFixを作る難易度が高い。<br />　　Roslynの良いところは、実は、解析の実装が簡単なだけじゃなくて、CodeFixも準備できることにもあると思うの。<br />　　おかしいところをまとめてぽちぽち直せるのは凄いと思う。でも、これ、作るの結構辛いっ。<br />　　解析のロジックを考えるのは、まぁ、変な構文が入ってきても、スルーするだけだと思うんだけど、<br />　　CodeFixはそれもまとめて直してあげないといけないからね。CodeFix作り込むのはかなり難易度高いね。<br />３．空白の作成で改行あたりが消える。<br />　　CodeFixと言えば、空白の調整とか、自前でやろうとすると、まぁ、空白を作成することになるんだけど、<br />　　SyntaxFactory.ParseLeadingTriviaとSyntaxFactory.ParseTrailingTriviaってあって、<br />　　後者は改行を作れないっぽいって言うか、作ろうとすると静かに消えるから注意なのだ。<br />　　どこにもそのあたりの説明書いてない感じで微妙なんだけど。<br />４．NuGetで入れたAnalyzerの削除で依存ライブラリが消える。<br />　　StyleCopもそうなんだけど、設定とかの読み込みとかに、Json.NETかな、使っているケースが多いみたい。<br />　　EcheckStyleもそうなんだけどね。<br />　　で、これ、NuGetで入れるときは自動で依存しているライブラリを入れるScriptが動くみたいなんだけど、<br />　　削除する時には、削除するScript勝手に動くみたいで、どうも依存関係が確認されないで消されるみたい。<br />　　うーんと、つまり、StyleCop入れて、ECheckStyle入れて、StyleCop消すと、<br />　　Json.NETのライブラリが消えて、ECheckStyleが動かなくなる。っていうこと。<br />　　この辺は、どうするのがいいのかな……。<br />５．ファイルの除外について<br />　　これは、ECheckStyleだと設定ファイルでサポートしているから、別に注意って言うわけじゃないんだけど、<br />　　この辺で解析対象の除外について<a href="https://github.com/dotnet/roslyn/issues/3705" target="_blank">議論</a>されているみたいなんだよね。<br />　　どうなるのかわからないけど、<br />　　解析するライブラリは複数あるわけで、それぞれ、かけたい範囲かけたくない範囲っていうのはあるはずで、<br />　　Roslyn側で全体まとめてかけるかけないって言うのは、ちょっと違うんじゃないかなぁ？とか思ったりしている。<br />　　まぁ、ここでは、ライブラリ作る側でその辺も考えようねって言うだけなんだけどね。</p>
<h2>☆☆☆まとめ☆☆☆</h2>
<p>いろいろ微妙な点も書いたけど、やっぱり便利だよ。ホントに。<br />ただ、対象がC#とVBだけなのが淋しいかな。</p>
<p>アプリつくってたらさ、<br />Webアプリケーションだったら、HTMLとかTypeScriptも書くし、<br />クライアントだったら、XAMLとかも書くわけじゃない？</p>
<p>この辺もサポートしてくれたら、とっても良いと思うんだけどなぁ（゜▽、゜</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/168997728.html">
<link>http://memory.empressia.jp/article/168997728.html</link>
<title>GlassFish上で動くWebアプリをGradleとNetBeansで開発するための準備</title>
<description>これは、Java EE Advent Calendar 2015 - Qiitaの2日目（12月2日）の記事だよ。昨日は、キクタロー（@kikutaro_）さんのJava EEでMicroserviceを実現するKumuluzEE #javaee #Qiita ね。明日は、Kohei Nozaki（@lbtc_xxx）さんが書くみたい。今日は、Gradleを使って、GlassFish上で動くWebアプリをNetBeansで開発するときのプロジェクトを作ってみようと思いま～す。..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2015-12-02T09:07:39+09:00</dc:date>
<content:encoded><![CDATA[
<p>これは、<a href="http://qiita.com/advent-calendar/2015/javaee" target="_blank">Java EE Advent Calendar 2015 - Qiita</a>の2日目（12月2日）の記事だよ。</p>
<p>昨日は、<a href="https://twitter.com/kikutaro_" target="_blank">キクタロー（@kikutaro_）さん</a>の<a href="http://kikutaro777.hatenablog.com/entry/2015/12/01/192036" target="_blank">Java EEでMicroserviceを実現するKumuluzEE #javaee #Qiita </a>ね。<br />明日は、<a href="https://twitter.com/lbtc_xxx" target="_blank">Kohei Nozaki（@lbtc_xxx）さん</a>が書くみたい。</p>
<p>今日は、Gradleを使って、<br />GlassFish上で動くWebアプリをNetBeansで開発するときのプロジェクトを作ってみようと思いま～す。<br />Webアプリは、CDIとJPA使う想定で、ユニットテストも出来るようにしたいかも。</p>
<p>でも、中身はなしでっ。<br />開発する環境を用意するのを中心にやることにするのだ。<br />あと、ユニットテストでCDIとかJPAを使う具体的な方法は別の記事を参照ってことで、</p>
<p>環境の前提はこんな感じでいくね。<br />１．Windows 8.1（まだ10にしてない）<br />２．JDK 1.8（手元で使ってるのはupdate66）<br />３．NetBeans 8.1（JavaEE開発用）<br />４．GlassFish 4.1（ドメインとデータソースの設定済み）（GlassFish 4.1.1でもほとんどおなじ）</p>
<p>流れは、こんな感じで。<br />１．GlassFishのサーバーとドメインをNetBeansに設定<br />２．NetBeansのGradle Supportモジュールをインストール<br />３．Gradleのプロジェクトを作ってビルドファイルをかきかきφ（。。<br />４．簡単な拡張とか</p>
<p>と言うことで。</p>
<h2>☆☆☆１．GlassFishのサーバーとドメインをNetBeansに設定☆☆☆</h2>
<p>ドメインの作成、DBのライブラリの設定、データソースの設定は終わってる前提だから、<br />NetBeansに登録して使えるようにするところから。</p>
<p>サーバーの追加でGlassFishを選んで……、<br />ここではGlassFish 4.1って名前にするね。この名前は後で使うよ。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_01.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_01-thumbnail2.png" alt="javaee_2015_01.png" width="320" height="186" border="0" /></a><br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_02.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_02-thumbnail2.png" alt="javaee_2015_02.png" width="320" height="215" border="0" /></a></p>
<p>GlassFishのインストール先と、ドメインを指定して完了～。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_03.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_03-thumbnail2.png" alt="javaee_2015_03.png" width="320" height="215" border="0" /></a><br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_04.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_04-thumbnail2.png" alt="javaee_2015_04.png" width="320" height="248" border="0" /></a></p>
<p>こんな感じになってるはず。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_05.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_05-thumbnail2.png" alt="javaee_2015_05.png" width="320" height="186" border="0" /></a></p>
<h2>☆☆☆２．NetBeansのGradle Supportモジュールをインストール☆☆☆</h2>
<p>いれるのは、これね、これ。<br /><a href="https://github.com/kelemen/netbeans-gradle-project">https://github.com/kelemen/netbeans-gradle-project</a></p>
<p>NetBeansのメニューバーから、ツール→プラグインと選んで、<br />プラグインの検索とインストール、更新が出来るダイアログを開くよ。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_11.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_11-thumbnail2.png" alt="javaee_2015_11.png" width="320" height="196" border="0" /></a></p>
<p>Gradleって検索すると『Gradle Support』って言うが見つかるからそれ。<br />もう1個のはよく知らない。とりあえず今回はいらないのでどっちでも。<br />何が出来るようになるんだろうね。</p>
<p>インストールしようとすると<br />『GroovyおよびGrails』も一緒に入れないとなのでまとめて入れちゃう。<br />GradleがGroovyで動くからだね。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_12.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_12-thumbnail2.png" alt="javaee_2015_12.png" width="320" height="290" border="0" /></a></p>
<p>NetBeansの再起動を要求されるから再起動すればGradleを使う準備は完了！</p>
<h2>☆☆☆３．Gradleのプロジェクトを作ってビルドファイルをかきかきφ（。。☆☆☆</h2>
<p>さぁ、新規プロジェクトを作るよ。<br />今回は、Single Gradle Projectで。<br />複雑なプロジェクト構成を組む場合は、Gradle Root Projectね。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_21.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_21-thumbnail2.png" alt="javaee_2015_21.png" width="320" height="225" border="0" /></a></p>
<p>適当な名前でプロジェクトを作成～。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_22.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_22-thumbnail2.png" alt="javaee_2015_22.png" width="320" height="220" border="0" /></a></p>
<p>Java用のbuild.gradleファイルが出来ているはず。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_23.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_23-thumbnail2.png" alt="javaee_2015_23.png" width="320" height="186" border="0" /></a></p>
<p>でもこのファイルは、Webアプリ用じゃないから、<br />ばっさりと書き換えちゃう。こんな感じに。</p>
<pre class="prettyprint">// GradleでGlassFishにアクセスするためのプラグイン用意してみたからそれ使うのだ。<br />// <a href="http://memory.empressia.jp/article/167469186.html" target="_blank">http://memory.empressia.jp/article/167469186.html</a><br />buildscript {<br />	repositories {<br />		maven { url "<a href="http://www.empressia.jp/maven/" target="_blank">http://www.empressia.jp/maven/</a>"; }<br />	}<br />	dependencies {<br />		classpath group: "jp.empressia", name: "jp.empressia.gradle.plugin.glassfish", version: "0.2.2";<br />	}<br />}<br /><br />// warをパッケージする。<br />apply plugin: "war";<br />// GlassFish用のタスクを追加する。<br />apply plugin: "glassfish";<br />// NetBeansのGlassFish設定を読み込める様にする。<br />apply plugin: "glassfish.netbeans";<br /><br />// Java8で。<br />sourceCompatibility = 1.8;<br />targetCompatibility = 1.8;<br /><br />// Mavenな感じと同じようにclassesの中にリソースが配置されるようにする。<br />sourceSets {<br />	main.output.resourcesDir = main.output.classesDir;<br />	test.output.resourcesDir = test.output.classesDir;<br />}<br /><br />// アプリの依存性解決用のリポジトリにMavenセントラル使うよ。<br />repositories {<br />	mavenCentral();<br />}<br /><br />// 依存関係。必要なの追加で。<br />dependencies {<br />	providedCompile(group:"javax", name:"javaee-web-api", version:"7.0");<br />}<br /><br />// NetBeans GlassFishプラグインの設定。NetBeansに登録したGlassFishサーバーを使う設定をしているよ。<br />// 記事の前半で設定したサーバーの名前を設定するのだ。<br />// glassfish.netbeans.serverName = "GlassFish Server 4.1";って書いてもokだからね。<br />glassfish {<br />	netbeans {<br />		serverName = "GlassFish Server 4.1";<br />	}<br />}<br /><br />// JavaはUTF-8でいいよね。<br />tasks.withType(JavaCompile) {<br />	options.encoding = "UTF-8";<br />}<br /><br />// リソースは、classes側に移動するようにしたからデプロイでリソースを個別に動かす必要はないからスルーする設定。<br />tasks.deployResources {<br />	exclude "**/*"<br />}</pre>
<p>これで基本的な設定は終わり～。</p>
<p>開始ボタン、ビルドボタン、とか普通に動くはず。<br />ブラウザは自動では開かないから、自分で開くっ。一度開けばいいし、良いよね。</p>
<p>あと、ログ見たいときは、サービスのサーバーからドメイン・サーバー・ログの表示を選べばでてくるよ。<br />これも一度だけ選べばずっと出てるかな。</p>
<p>あ、メインメニューのツール→オプションの設定に、Compile on saveがあるから、使ってみるといいかも。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_91.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_91-thumbnail2.png" alt="javaee_2015_91.png" width="320" height="204" border="0" /></a></p>
<p>プロジェクトのプロパティ設定でApply Code Changesを『${project}:deploy』がおすすめかな。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_92.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/javaee_2015_92-thumbnail2.png" alt="javaee_2015_92.png" width="320" height="199" border="0" /></a></p>
<h2>☆☆☆４．簡単な拡張とか☆☆☆</h2>
<p>☆ユニットテストでCDIとJPAを使う設定</p>
<p>CDIを実際に動かすには、コンテナ作るか用意しないとだと思うけど、<br />基本的な設定はこんな感じ？</p>
<pre class="prettyprint">dependenies {<br />	// testRuntimeでも良いのもあるかも。<br />	testCompile(group:"junit", name:"junit", version:"4.12");<br />	testCompile(group:"org.hamcrest", name:"hamcrest-library", version:"1.3");<br />	testCompile(group:"org.jboss.weld.se", name:"weld-se", version:"2.2.2.Final");<br />	testCompile(group:"org.eclipse.persistence", name:"eclipselink", version:"2.5.2-RC1");<br />	testCompile(group:"javax.transaction", name:"javax.transaction-api", version:"1.2");<br />}<br /><br />tasks.test {<br />	doFirst {<br />		// test側のbeans.xmlに書かれた内容をユニットテストの時に読み込まれるようにbeans.xmlをmainのclassesにコピーする。<br />		// 要らなければスルーで。<br />		copy { from "${sourceSets.test.resources.srcDirs.find({true})}/META-INF/beans.xml" into "${sourceSets.main.output.resourcesDir}/META-INF/" }<br />		// コネクションはテスト側にあっても取れるけど、Entityはmain側にないと読み込めないから、persistence.xmlをmainのclassesにコピーする。<br />		// あと、persistence.xmlの競合を避けるために、テスト側からpersistence.xmlを削除する。<br />		copy { from "${sourceSets.test.resources.srcDirs.find({true})}/META-INF/persistence.xml" into "${sourceSets.main.output.resourcesDir}/META-INF/" }<br />		delete "${sourceSets.test.output.resourcesDir}/META-INF/persistence.xml"<br />	}<br />	// JPAをJavaAgentで使う～。<br />	jvmArgs "-javaagent:${configurations.testCompile.files.find({ file -&gt; file.name.equals('eclipselink-2.5.2-RC1.jar') })}"<br />}<br /><br />// ビルドする場合は、testをはじめる前にパッケージ（とデプロイ）をしないと、テスト直前でリソースを書き換えているのが問題になっちゃうから、依存関係を追加しておく。<br />if(this.gradle.startParameter.taskNames.contains("build")) {<br />	// tasks.test.dependsOn(tasks.assemble);っていう方法も。<br />	tasks.test.dependsOn(tasks.war);<br />	tasks.test.dependsOn(tasks.deploy);<br />}</pre>
<p>余談だけど、CDIってなんかパッケージの外から設定上書きできないのかなぁ？<br />Alternativeとか、外から切り替えたいんだけど、できないよね。<br />そのうちできるようになるのかなぁ……？それとも、そういうものじゃないのかな……？</p>
<p>☆LESSを使う設定</p>
<p>LESS便利だよね。と言うか、CSSが不便すぎて……。<br />パスを直接ビルドファイルに書くのいやだし、NetBeansからパスを読み込むのを用意してみたよ。<br />ビルドスクリプトに追加で。</p>
<pre class="prettyprint">buildscript {<br />	repositories {<br />		maven { url "<a href="http://www.empressia.jp/maven/" target="_blank">http://www.empressia.jp/maven/</a>"; }<br />	}<br />	dependencies {<br />		classpath group: "jp.empressia", name: "jp.empressia.gradle.plugin.netbeans.configuration", version: "0.0.6";<br />	}<br />}<br />apply plugin: "netbeans.configuration";<br /><br />// タスクを追加するよ。<br />// タスクの定義で『&lt;&lt;』で作ってる例あるけど、タスクの定義に『&lt;&lt;』は追加したらだめだよね。<br />// タスクの定義ちゃんとしてインクリメンタルビルドしないとね。<br />task processLess(type: Copy) {<br />	from webAppDir<br />	into webAppDir<br />	include "**/*.less"<br />	eachFile { file -&gt;<br />		exec {<br />			commandLine "${netbeans.configuration.lessPath}", "--compress", "${webAppDir}/${file.path}", "${webAppDir}/${file.path.substring(0, file.path.length() - '.less'.length())}.min.css"<br />		}<br />		file.exclude();<br />	}<br />}<br /><br />// タスクの依存関係はこんな感じ？<br />// コンパイル系の作業の中にLESSコンパイルもいれるよ。<br />tasks.classes.dependsOn(tasks.processLess);</pre>
<p>他にも、netbeans.configuration.***Pathっていう形でいろいろ取れるようにしてあるよ。</p>
<ul>
<li>sassPath</li>
<li>gruntPath</li>
<li>gulpPath</li>
<li>karmaPath</li>
<li>expressPath</li>
<li>nodePath</li>
<li>npmPath</li>
</ul>
<p>バージョンとか指定したい場合は</p>
<pre class="prettyprint">netbeans {<br />	configuration {<br />		version = "8.1";<br />	}<br />}</pre>
<p>とか</p>
<pre class="prettyprint">netbeans {<br />	configuration {<br />		appDataDirectoryPath = "C:/Users/[UserName]/AppData/Roaming/NetBeans";<br />		version = "8.1";<br />	}<br />}</pre>
<p>とか</p>
<pre class="prettyprint">netbeans {<br />	configuration {<br />		directoryPath = "C:/Users/[UserName]/AppData/Roaming/NetBeans/8.1/config";<br />	}<br />}</pre>
<p>とかで指定できるよ。</p>
<p>☆JavadocとMavenでのアップロードは……。</p>
<p>まぁ、いいよね。<br />今回は、スルーで。</p>
<p>☆最終的なbuild.gradle</p>
<p>こんな感じになるかな？</p>
<pre class="prettyprint">buildscript {<br />	repositories {<br />		maven { url "<a href="http://www.empressia.jp/maven/" target="_blank">http://www.empressia.jp/maven/</a>"; }<br />	}<br />	dependencies {<br />		classpath group: "jp.empressia", name: "jp.empressia.gradle.plugin.glassfish", version: "0.2.2";<br />		classpath group: "jp.empressia", name: "jp.empressia.gradle.plugin.netbeans.configuration", version: "0.0.6";<br />	}<br />}<br /><br />apply plugin: "war";<br />apply plugin: "glassfish";<br />apply plugin: "glassfish.netbeans";<br />apply plugin: "netbeans.configuration";<br /><br />sourceCompatibility = 1.8;<br />targetCompatibility = 1.8;<br /><br />sourceSets {<br />	main.output.resourcesDir = main.output.classesDir;<br />	test.output.resourcesDir = test.output.classesDir;<br />}<br /><br />repositories {<br />	mavenCentral();<br />}<br /><br />dependencies {<br />	providedCompile(group:"javax", name:"javaee-web-api", version:"7.0");<br />	testCompile(group:"junit", name:"junit", version:"4.12");<br />	testCompile(group:"org.hamcrest", name:"hamcrest-library", version:"1.3");<br />	testCompile(group:"org.jboss.weld.se", name:"weld-se", version:"2.2.2.Final");<br />	testCompile(group:"org.eclipse.persistence", name:"eclipselink", version:"2.5.2-RC1");<br />	testCompile(group:"javax.transaction", name:"javax.transaction-api", version:"1.2");<br />}<br /><br />glassfish {<br />	netbeans {<br />		serverName = "GlassFish Server 4.1";<br />	}<br />}<br /><br />tasks.withType(JavaCompile) {<br />	options.encoding = "UTF-8";<br />}<br /><br />task processLess(type: Copy) {<br />	from webAppDir<br />	into webAppDir<br />	include "**/*.less"<br />	eachFile { file -&gt;<br />		exec {<br />			commandLine "${netbeans.configuration.lessPath}", "--compress", "${webAppDir}/${file.path}", "${webAppDir}/${file.path.substring(0, file.path.length() - '.less'.length())}.min.css"<br />		}<br />		file.exclude();<br />	}<br />}<br />tasks.classes.dependsOn(tasks.processLess);<br /><br />tasks.deployResources {<br />	exclude "**/*"<br />}<br /><br />tasks.test {<br />	doFirst {<br />		copy { from "${sourceSets.test.resources.srcDirs.find({true})}/META-INF/beans.xml" into "${sourceSets.main.output.resourcesDir}/META-INF/" }<br />		copy { from "${sourceSets.test.resources.srcDirs.find({true})}/META-INF/persistence.xml" into "${sourceSets.main.output.resourcesDir}/META-INF/" }<br />		delete "${sourceSets.test.output.resourcesDir}/META-INF/persistence.xml"<br />	}<br />	jvmArgs "-javaagent:${configurations.testCompile.files.find({ file -&gt; file.name.equals('eclipselink-2.5.2-RC1.jar') })}"<br />}<br /><br />if(this.gradle.startParameter.taskNames.contains("build")) {<br />	tasks.test.dependsOn(tasks.war);<br />	tasks.test.dependsOn(tasks.deploy);<br />}</pre>
<p>これで、割と自然にGradle＋NetBeans＋GlassFishでWebアプリ作れるんじゃないかなぁって。</p>
<p>そうそう、NetBeansのGradleプラグイン作ってる人に聞いたんだけど、<br />非同期構成意識して作っているからプロジェクトの構成が古いのと違っていて、<br />古いNetBeansのNode追加するプラグインがGradleのプロジェクトには追加されないんだよね（Ver.1.3.7.2）。</p>
<p>対応してもらったバージョン作ってもらったから、良ければ直接ダウンロードして使ってみてね。<br /><a href="https://github.com/kelemen/netbeans-gradle-project" target="_blank">https://github.com/kelemen/netbeans-gradle-project</a><br />NetBeans81ブランチで試してたんだけど、今は、masterにも取り込まれたみたいだよ。</p>
<p>かなり、メールの応答が速かったから、とっても助かったのだ（゜▽、゜感謝だね</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/167469186.html">
<link>http://memory.empressia.jp/article/167469186.html</link>
<title>GradleからGlassFishにデプロイするためのプラグインをつくってみたよ</title>
<description>NetBeansつかってWebサイトつくってたんだけど、Mavenでビルドしてると遅いよね！なんか、MavenじゃなくてGradleっていうのを使うと、インクリメンタルビルドっていうのができて速くなるみたい。と言うわけで、Webサイト作るのに使ってみたんだけど、GlassFishへのデプロイはデフォルトではサポートしていないみたい。で、ないならプラグインで作れば良いよね！ってことでつくってみたよ。☆☆☆準備編☆☆☆build.gradleに下のように書けばok。buildsc..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2015-11-09T22:16:00+09:00</dc:date>
<content:encoded><![CDATA[
<p>NetBeansつかってWebサイトつくってたんだけど、<br />Mavenでビルドしてると遅いよね！</p>
<p>なんか、MavenじゃなくてGradleっていうのを使うと、<br />インクリメンタルビルドっていうのができて速くなるみたい。</p>
<p>と言うわけで、Webサイト作るのに使ってみたんだけど、<br />GlassFishへのデプロイはデフォルトではサポートしていないみたい。<br />で、ないならプラグインで作れば良いよね！ってことでつくってみたよ。</p>
<h2>☆☆☆準備編☆☆☆</h2>
<p>build.gradleに下のように書けばok。</p>
<pre class="prettyprint">buildscript {<br /> 	repositories {<br />		maven { url "<a href="http://www.empressia.jp/maven/" target="_blank">http://www.empressia.jp/maven/</a>" }<br /> 	}<br /> 	dependencies {<br /> 		classpath "jp.empressia:jp.empressia.gradle.plugin.glassfish:+";<br /> 	}<br />}</pre>
<pre class="prettyprint">apply plugin: "glassfish";</pre>
<p>NetBeans使っている場合は、さらに下の1行を追加すると便利になるよ。</p>
<pre class="prettyprint">apply plugin: "glassfish.netbeans";</pre>
<h2>☆☆☆定義編☆☆☆</h2>
<p>プラグインを適用すると、glassfishの設定をできるようになるよ。<br />netbeansの部分はglassfish.netbeansを適用したときだけね。<br />長いけど、別に、全部設定しなくても大丈夫。</p>
<pre class="prettyprint">glassfish {<br />	// GlassFishの場所<br />	homeDirectoryPath;<br />	// GlassFishの起動に使用するJDKHomeの場所<br />	JDKHomeDirectoryPath;<br />	// ドメインのディレクトリ<br />	domainsDirectoryPath;<br />	// ドメイン名<br />	domainName;<br />	// 管理ポート<br />	adminPort;<br />	// deployでサーバーの開始を試みる時にデバッグにするかどうか<br />	deployWithDebug;<br />	// ビルドの時にデプロイするかどうか（デフォルトtrue）<br />	deployOnBuild;<br />	netbeans {<br />		// NetBeansのサーバー設定の名前<br />		serverName;<br />		// NetBeansのJavaプラットフォーム設定の名前<br />		JDKName;<br />		// NetBeansのサーバー設定ファイル（/config/GlassFishEE6/Instances/.nbattrs）<br />		configFilePath;<br />		// NetBeansのVersion<br />		version;<br />	}<br />	deploy {<br />		// デプロイ用のクラスファイルの場所<br />		String classesDirectoryPath;<br />		// デプロイ用のリソースファイルの場所<br />		String resourcesDirectoryPath;<br />		// デプロイ用のWebPage系のファイルの場所<br />		String webappDirectoryPath;<br />		// デプロイ用のディレクトリ名<br />		String directoryName;<br />		// デプロイ用のコンテキストルート<br />		String contextRoot;<br />	}<br />}</pre>
<h2>☆☆☆設定サンプル編☆☆☆</h2>
<p>netbeansを適用していないときは、GlassFishのホームディレクトリを設定してね。<br />gradle,propertiesあたりから設定しても良いかも。</p>
<pre class="prettyprint">glassfish {<br />	homeDirectoryPath = "C:\\Program Files\\glassfish-4.1\\glassfish";<br />}</pre>
<p>JDKは放っておくとGradleのを使うから、指定したい場合は、↓の感じ。</p>
<pre class="prettyprint">glassfish {<br />	homeDirectoryPath = "C:\\Program Files\\glassfish-4.1\\glassfish";<br />	JDKHomeDirectoryPath = "C:\\Program Files\\Java\\jdk1.8.*_**";<br />}</pre>
<p>でも、やっぱり設定めんどくさいよね！<br />と言うわけで、netbeansを適用しておけば、NetBeansの設定から勝手にGlassFish探すようにしてあるよ。<br />このときは何もしなくても全自動～。</p>
<p>サーバーの設定を複数しているなら、設定したサーバーの名前を付ければ大丈夫！</p>
<pre class="prettyprint">glassfish {<br />	netbeans {<br />		serverName = "GlassFish Server 4.1";<br />	}<br />}</pre>
<p>簡単だね！<br />あ、古いNetBeansに設定がある場合は、versionを指定してね。</p>
<pre class="prettyprint">glassfish {<br />	netbeans {<br />		serverName = "GlassFish Server 4.1";<br />		version = "8.0.1";<br />	}<br />}</pre>
<p>サーバーのJDK設定がデフォルトだと、Graldeのを使おうとするから注意してね。<br />こんな風にも設定できるけど、サーバーの設定をちゃんとした方が良いかな。</p>
<pre class="prettyprint">glassfish {<br />	netbeans {<br />		serverName = "GlassFish Server 4.1";<br />		JDKName = "JDK 1.8";<br />	}<br />}</pre>
<h2>☆☆☆実行編☆☆☆</h2>
<p>glassfishプラグインを適用すると、5つのタスクが追加されるよ。</p>
<ul>
<li>deployClasses</li>
<li>deployResources</li>
<li>deployWebapp</li>
<li>deployLibraries</li>
<li>deploy</li>
</ul>
<p>他にも、いくつかのタスクが書き換わるよ。<br />clean、run、debugとか。</p>
<p>これで、↓の感じで行けるはず。</p>
<pre class="prettyprint">// サーバー停止（デプロイディレクトリ削除）<br />gradle clean<br />// サーバー起動<br />gradle run<br />// サーバーdebug起動<br />gradle debug<br />// デプロイ<br />gradle deploy</pre>
<p>タスクの関係はだいたいこんな感じ。<br /><a href="http://www.empressia.jp/sblo_files/sphere/image/EmpressiaGradleGlassFishPlugin_Tasks.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/EmpressiaGradleGlassFishPlugin_Tasks-thumbnail2.png" alt="EmpressiaGradleGlassFishPlugin_Tasks.png" width="320" height="164" border="0" /></a></p>
<p>NetBeans使ってるなら、ビルドボタン、クリーンビルドボタン、実行、デバッグボタンがあるから簡単に連動すると思うよ。<br />ただ、サーバーのログが見たかったら、サービスのサーバーを選んで、ドメイン・サーバー・ログの表示とかやってね。<br />あ、NetBeans閉じてもサーバー止まらないかも……まぁ、いいよね（゜▽、゜</p>
<h2>☆☆☆注意編☆☆☆</h2>
<p>なるべく、無駄にdeployコマンドが発行されないようにしているんだけど、<br />そのせいで、deployコマンド自体で失敗すると、次からデプロイされなくなる……かも？<br />そんなときは、コンパイルが動くようにプログラムを一回書き換えるとか、cleanするとかがいいかなぁ。</p>
<p>あと、MacとLinuxでの確認はしてないー。</p>
<p>Version 0.2.0は、Javaのソースがないと、<br />webappにWEB-INFディレクトリが作られなくてデプロイ失敗することがあるんだけど、<br />そういうことあんまり無いだろうし、すぐ直す予定～。</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/107610366.html">
<link>http://memory.empressia.jp/article/107610366.html</link>
<title>Maven経由のユニットテストでCDIを簡単に使う</title>
<description>前の二つの記事でCDIとJPAをユニットテストで使う方法について書いてきたから詳細なプログラムの内容はそっちに任せるとして、まとめたパッケージを簡単に使う方法をまとめると、pom.xmlをこんな感じに。	&amp;lt;repositories&amp;gt;		……		&amp;lt;repository&amp;gt;			&amp;lt;id&amp;gt;Empressia&amp;lt;/id&amp;gt;			&amp;lt;url&amp;gt;http://www.empressia.jp/maven/&amp;lt;/url&amp;gt;		&amp;lt..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2014-12-21T21:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<p><a href="http://memory.empressia.jp/article/107596261.html">前の二つの記事</a>でCDIとJPAをユニットテストで使う方法について書いてきたから詳細なプログラムの内容はそっちに任せるとして、<br />まとめたパッケージを簡単に使う方法をまとめると、pom.xmlをこんな感じに。</p>
<pre class="prettyprint">	&lt;repositories&gt;<br />		……<br />		&lt;repository&gt;<br />			&lt;id&gt;Empressia&lt;/id&gt;<br />			&lt;url&gt;<a href="http://www.empressia.jp/maven/">http://www.empressia.jp/maven/</a>&lt;/url&gt;<br />		&lt;/repository&gt;<br />	&lt;/repositories&gt;<br /><br />	&lt;dependencies&gt;<br />		……<br />		&lt;dependency&gt;<br />			&lt;groupId&gt;jp.empressia&lt;/groupId&gt;<br />			&lt;artifactId&gt;jp.empressia.test&lt;/artifactId&gt;<br />			&lt;version&gt;1.0.1-1&lt;/version&gt;<br />			&lt;scope&gt;test&lt;/scope&gt;<br />		&lt;/dependency&gt;<br />	&lt;/dependencies&gt;</pre>
<p>これでRunner指定すればCDIが使えるよ。さらにbeans.xmlをこんな感じに。</p>
<pre class="prettyprint">	&lt;beans&gt;<br />		&lt;alternatives&gt;<br />			&lt;stereotype&gt;jp.empressia.test.UnitTest&lt;/stereotype&gt;<br />		&lt;/alternatives&gt;<br />		&lt;intercepters&gt;<br />			&lt;class&gt;jp.empressia.test.TransactionInterceptor&lt;/class&gt;<br />		&lt;/intercepters&gt;<br />	&lt;/beans&gt;</pre>
<p>これでJPAとJTAが使える。一部だけど。</p>
<p>で、Maven使ってるときに気をつけないといけないことがあるんだよね。</p>
<p>beans.xmlにalternatives書くのは、main側じゃないとダメって言うか、main側のパスに入ってないとだめなのね。<br />でも、main側にテストの要素書いちゃだめなわけで……えーっと、<br />mainのclassesとtest-classesで別のbeans.xm管理になるって言う感じ？<br />実際にalternativeしたいのは、main側。設定書いておきたいのはtest側。<br />有効にするためには、test側にもbeans.xml必要。<br />うーん……ここ、ポイントだからしっかり整理～。</p>
<ul>
<li>JavaEEの環境だと、beans.xml無くてもCDIは有効になる。</li>
<li>JavaSEの環境だとbeans.xmlが必要。</li>
<li>Mavenだとクラスファイルの格納場所が、classesとtest-classesに分かれてる。</li>
<li>ユニットテストでalternativeの書いたbeans.xmlを置かないといけないのはclasses側（試して見た感じ）。</li>
<li>ユニットテストでCDIを有効にするためにbeans.xml置くのはtest-classes側（中身は何でもよさそう）（試して見た感じ）。</li>
<li>テスト固有のbeans.xmlの内容はmain側じゃなくてtest側に書いておきたい。</li>
</ul>
<p>これを解決しようとすると、<br />testするときには、テスト側のbeans.xmlをmain側において実行して、<br />パッケージするときは、メイン側のbeans.xmlで上書きするって手順にしないとダメそう。</p>
<p>あと、persistence.xmlもmain側だけじゃなくてtest側にもおいちゃうと、ユニットテストの時に、EclipseLinkを使った時点で両方読まれちゃう。<br />test用にRESOURCE_LOCALで細かい接続設定書いたpersistence.xmlを置くだろうから、<br />ユニットテストの時はmainの方を消すのを忘れずに。packagingの時にこれも気をつけないとね。</p>
<p>具体的にはMavenのビルドをこんな感じに。<br />良い悪いいろんな意見はあるだろうけど、まぁ、手軽にやるなら良いかなって。</p>
<pre class="prettyprint">	&lt;build&gt;<br />		&lt;plugins&gt;<br />			……<br />			&lt;!-- process-test-resources：beans.xmlをmainのclassesにコピーする。test側のbeans.xmlに書かれた内容をユニットテストの時に読み込まれるようにする。 --&gt;<br />			&lt;plugin&gt;<br />				&lt;groupId&gt;com.github.goldin&lt;/groupId&gt;<br />				&lt;artifactId&gt;copy-maven-plugin&lt;/artifactId&gt;<br />				&lt;version&gt;0.2.5&lt;/version&gt;<br />				&lt;executions&gt;<br />					&lt;execution&gt;<br />						&lt;id&gt;copy test beans.xml to main for unit test.&lt;/id&gt;<br />						&lt;phase&gt;process-test-resources&lt;/phase&gt;<br />						&lt;goals&gt;<br />							&lt;goal&gt;copy&lt;/goal&gt;<br />						&lt;/goals&gt;<br />						&lt;configuration&gt;<br />							&lt;resources&gt;<br />								&lt;resource&gt;<br />									&lt;targetPath&gt;${project.build.directory}/classes/META-INF&lt;/targetPath&gt;<br />									&lt;file&gt;src/test/resources/META-INF/beans.xml&lt;/file&gt;<br />								&lt;/resource&gt;<br />							&lt;/resources&gt;<br />						&lt;/configuration&gt;<br />					&lt;/execution&gt;<br />				&lt;/executions&gt;<br />			&lt;/plugin&gt;<br />			&lt;!-- process-test-resources：persistence.xmlをmainのclassesから削除する。mainのpersistence.xmlがユニットテストの時に読み込まれるのを防止する。 --&gt;<br />			&lt;plugin&gt;<br />				&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br />				&lt;artifactId&gt;maven-clean-plugin&lt;/artifactId&gt;<br />				&lt;version&gt;2.6.1&lt;/version&gt;<br />				&lt;executions&gt;<br />					&lt;execution&gt;<br />						&lt;id&gt;remove persistence.xml from classes for avoiding duplicated persistence.xml at unit test.&lt;/id&gt;<br />						&lt;phase&gt;process-test-resources&lt;/phase&gt;<br />						&lt;goals&gt;<br />							&lt;goal&gt;clean&lt;/goal&gt;<br />						&lt;/goals&gt;<br />						&lt;configuration&gt;<br />							&lt;excludeDefaultDirectories&gt;true&lt;/excludeDefaultDirectories&gt;<br />							&lt;filesets&gt;<br />								&lt;fileset&gt;<br />									&lt;directory&gt;${project.build.directory}/classes/META-INF&lt;/directory&gt;<br />									&lt;includes&gt;<br />										&lt;include&gt;persistence.xml&lt;/include&gt;<br />									&lt;/includes&gt;<br />								&lt;/fileset&gt;<br />							&lt;/filesets&gt;<br />						&lt;/configuration&gt;<br />					&lt;/execution&gt;<br />				&lt;/executions&gt;<br />			&lt;/plugin&gt;<br />			&lt;plugin&gt;<br />				&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br />				&lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;<br />				&lt;version&gt;2.3&lt;/version&gt;<br />				&lt;configuration&gt;<br />					&lt;failOnMissingWebXml&gt;false&lt;/failOnMissingWebXml&gt;<br />					&lt;!-- テスト用にtargetが書き換えているから、src側からパッケージする。 --&gt;<br />					&lt;webResources&gt;<br />						&lt;resource&gt;<br />							&lt;directory&gt;src/main/resources/META-INF&lt;/directory&gt;<br />							&lt;targetPath&gt;WEB-INF/classes/META-INF&lt;/targetPath&gt;<br />							&lt;includes&gt;<br />								&lt;include&gt;beans.xml&lt;/include&gt;<br />								&lt;include&gt;persistence.xml&lt;/include&gt;<br />							&lt;/includes&gt;<br />						&lt;/resource&gt;<br />						&lt;resource&gt;<br />							&lt;directory&gt;target/classes/META-INF&lt;/directory&gt;<br />							&lt;excludes&gt;<br />								&lt;!-- beans.xmlはユニットテストの時にmain側にコピーしてるから取り込まないようにする。 --&gt;<br />								&lt;exclude&gt;beans.xml&lt;/exclude&gt;<br />								&lt;!-- persistence.xmlはユニットテストの時に消えるはずだけどincludeと形をあわせておく。 --&gt;<br />								&lt;exclude&gt;persistence.xml&lt;/exclude&gt;<br />							&lt;/excludes&gt;<br />						&lt;/resource&gt;<br />					&lt;/webResources&gt;<br />				&lt;/configuration&gt;<br />			&lt;/plugin&gt;<br />		&lt;/plugins&gt;<br />	&lt;/build&gt;</pre>
<p>ここまでで、@Injectが使いたい放題でJPAのPersistenceUnit1個に対応して、Transactionalにも簡単にだけど対応した状態になっているはず！（゜▽、゜<br />気に入らなければCDIの力で差し替えも簡単だから、是非、お試しあれ？（゜▽、゜</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/107606370.html">
<link>http://memory.empressia.jp/article/107606370.html</link>
<title>JPAとJTAをJavaSE環境で使えるようにする</title>
<description>こっちの記事に書いたけど、CDIが出来ちゃえば、後はもう簡単だよね。CDI使ってるってことは、リソース系は、全部@Produces作ってるはずだから、それを上書きしちゃえばok！JPAは普通……かどうか分からないけど、CDI使っているなら、persistence.xmlに定義しているUnitごとに、Producer作るよね。たとえば、persistence.xmlにSamplePUが定義されてるとするとこんな感じ。	@Qualifier	@Retention(Retentio..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2014-12-21T21:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<p><a href="http://memory.empressia.jp/article/107602460.html">こっちの記事</a>に書いたけど、CDIが出来ちゃえば、後はもう簡単だよね。<br />CDI使ってるってことは、リソース系は、全部@Produces作ってるはずだから、<br />それを上書きしちゃえばok！</p>
<p>JPAは普通……かどうか分からないけど、CDI使っているなら、<br />persistence.xmlに定義しているUnitごとに、Producer作るよね。<br />たとえば、persistence.xmlにSamplePUが定義されてるとするとこんな感じ。</p>
<pre class="prettyprint">	@Qualifier<br />	@Retention(RetentionPolicy.RUNTIME)<br />	@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})<br />	public @interface Sample {<br />	}</pre>
<pre class="prettyprint">	@RequestScoped<br />	public class SampleProducer {<br /><br />		@PersistenceContext(unitName="SamplePU")<br />		private EntityManager em;<br /><br />		@Produces @Sample<br />		public EntityManager getEntityManager() {<br />			return this.em;<br />		}<br /><br />	}</pre>
<p>そしたら、テスト側はこんな感じのを用意すれば完成！テスト用のbeans.xmlに追加するの忘れないでね。</p>
<pre class="prettyprint">	package jp.empressia.test;<br />	// （略）<br />	@Stereotype<br />	@Alternative<br />	@Retention(RetentionPolicy.RUNTIME)<br />	@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})<br />	@Documented<br />	public @interface UnitTest {<br />	}</pre>
<pre class="prettyprint">	@RequestScoped<br />	public class LocalSampleProducer {<br /><br />		private static final EntityManagerFactory emf;<br /><br />		static {<br />			emf = Persistence.createEntityManagerFactory("SamplePU");<br />		}<br /><br />		private EntityManager em;<br /><br />		public LocalSampleProducer() {<br />			this.em = emf.createEntityManager();<br />		}<br /><br />		@Produces @Sample @UnitTest<br />		public EntityManager getEntityManager() {<br />			return this.em;<br />		}<br /><br />	}</pre>
<pre class="prettyprint">	&lt;beans&gt;<br />		&lt;alternatives&gt;<br />			&lt;stereotype&gt;jp.empressia.test.UnitTest&lt;/stereotype&gt;<br />		&lt;/alternatives&gt;<br />	&lt;/beans&gt;</pre>
<p>EntityManagerFactoryとEntityManagerの扱い方はこれでいいよね、たぶん。</p>
<p>それから、JTAかな。<br />CDIとJTAなら、javax.transaction.Transactional使っているという前提で、Interceptorを作っちゃう方向でいいよね。<br />あと、ユニットテストだし、接続先は共通で1個だけってことにするよ。<br />まず、ProducerにTransactionを提供する部分を追加するのだ。</p>
<pre class="prettyprint">	@RequestScoped<br />	public class LocalSampleProducer {<br />		// （略）<br />		@Produces<br />		public EntityTransaction getTransaction() {<br />			return this.em.getTransaction();<br />		}<br />	}</pre>
<p>そうしたら、こんな感じのInterceptorでも十分かなっと。<br />別にもっと作り込んでも良いんだけど、わたしはそんな高度な使い方しないしね。</p>
<pre class="prettyprint">	package jp.empressia.test;<br />	// （略）<br />	@Transactional<br />	@Interceptor<br />	public class TransactionInterceptor {<br /><br />		@Inject<br />		private EntityTransaction trans;<br /><br />		@AroundInvoke<br />		public Object doTransaction(InvocationContext ic) throws Exception {<br />			Object result = null;<br />			try {<br />				this.trans.begin();<br />				result = ic.proceed();<br />				if(this.trans.isActive()) {<br />					if(this.trans.getRollbackOnly() == false) {<br />						this.trans.commit();<br />					} else {<br />						this.trans.rollback();<br />					}<br />				} else {<br />					String message = "なにかメッセージ";<br />					logger(this).warning(message);<br />				}<br />			} catch(Exception proceededEx) {<br />				try {<br />					if(trans.isActive()) {<br />						trans.rollback();<br />					}<br />				} catch(Exception rollbackEx) {<br />					String message = "なにかメッセージ";<br />					logger(this).severe(message);<br />				}<br />				throw proceededEx;<br />			}<br />			return result;<br />		}<br />	}</pre>
<p>で、これを、やっぱりbeans.xmlにっと。</p>
<pre class="prettyprint">	&lt;beans&gt;<br />		&lt;intercepters&gt;<br />			&lt;class&gt;jp.empressia.test.TransactionInterceptor&lt;/class&gt;<br />		&lt;/intercepters&gt;<br />	&lt;/beans&gt;</pre>
<p>これで、@TransactionalもEntityManagerのInjectも動くね。</p>
<p>ちなみに、<a href="http://memory.empressia.jp/article/107602460.html">前の記事</a>のパッケージには、接続先のUnitが1個前提の実装が入ってるよ。<br />beans.xmlをこんな感じにすれば有効になるようになってる。</p>
<pre class="prettyprint">	&lt;beans&gt;<br />		&lt;alternatives&gt;<br />			&lt;stereotype&gt;jp.empressia.test.UnitTest&lt;/stereotype&gt;<br />		&lt;/alternatives&gt;<br />		&lt;intercepters&gt;<br />			&lt;class&gt;jp.empressia.test.TransactionInterceptor&lt;/class&gt;<br />		&lt;/intercepters&gt;<br />	&lt;/beans&gt;</pre>
<p>あとはLocalなEntityManagerなProducerをこんな感じに書けばok。<br />『@Sample』みたいな指定が元々なければ、子のクラスは用意する必要も無い……はず、たぶん。<br />@Inject EntityManagerが動作してるからね。</p>
<pre class="prettyprint">	@RequestScoped<br />	public class LocalSampleProducer {<br /><br />		@Inject<br />		private EntityManager em;<br /><br />		@Produces @Sample @UnitTest<br />		public EntityManager getEntityManager() {<br />			return this.em;<br />		}<br /><br />	}</pre>
<p>あ、もしかしたら、EclipseLinkをjavaagentに指定して起動すれば、もっと簡単かもしれないけど、その辺は試してないよ。</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/107602460.html">
<link>http://memory.empressia.jp/article/107602460.html</link>
<title>JavaEE用のスコープをJavaSE環境で使えるようにする</title>
<description>CDIをJavaSEで使えるようにするのはそんな難しいことじゃないよね。問題なのは、JavaEEのスコープをJavaSEで使うこと。RequestScopedとか、ConversationScopedとか、SessionScopedとか。他のカスタムされたスコープは、まぁ、必要な人が対応すれば良いよねってことで。基本は、同じだし。まずは、コンテナとしての機能を追加しないとダメだから、JUnitのRunnerを拡張してWeldの機能を有効にするところから。ホントは、別の方法とか..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2014-12-21T21:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<p>CDIをJavaSEで使えるようにするのはそんな難しいことじゃないよね。<br />問題なのは、JavaEEのスコープをJavaSEで使うこと。<br />RequestScopedとか、ConversationScopedとか、SessionScopedとか。</p>
<p>他のカスタムされたスコープは、まぁ、必要な人が対応すれば良いよねってことで。<br />基本は、同じだし。</p>
<p>まずは、コンテナとしての機能を追加しないとダメだから、JUnitのRunnerを拡張してWeldの機能を有効にするところから。<br />ホントは、別の方法とかあるのかもしれないけど、JUnitに詳しくなくて……動けば良いよね！</p>
<p>こんな感じ。</p>
<pre class="prettyprint">	public class JUnit4RunnerWithWeld extends BlockJUnit4ClassRunner {<br /><br />		/** Weld */<br />		private static final Weld Weld;<br />		/** Weld Container. */<br />		private static final WeldContainer Container;<br /><br />		static {<br />			Weld = new Weld();<br />			Container = Weld.initialize();<br />		}<br /><br />		/** 対象となるテストクラス。 */<br />		private final Class&lt;?&gt; TargetTestClass;<br /><br />		public JUnit4RunnerWithWeld(final Class&lt;Object&gt; TargetTestClass) throws InitializationError {<br />			super(TargetTestClass);<br />			this.TargetTestClass = TargetTestClass;<br />		}<br /><br />		@Override<br />		protected Object createTest() throws Exception {<br />			Object test = JUnit4RunnerWithWeld.Container.instance().select(this.TargetTestClass).get();<br />			return test;<br />		}<br /><br />	}</pre>
<p>ポイントは２つ。</p>
<ol>
<li>Weldを作って初期化する。</li>
<li>BlockJUnit4ClassRunnerを継承してcreateTestで対象のクラスをコンテナとして作る。</li>
</ol>
<p>うん、簡単だよね！Weld大好き！……かも。いや、別にそんな好きなわけじゃないけど。<br />ちなみに、Runner自体は、テストクラスごとに出来るから、Weldはstaticに作ってるよ。<br />これで、ユニットテストクラスに@RunWithでCDIを有効にできるようになったのだ。</p>
<pre class="prettyprint">	@RunWith(JUnit4RunnerWithWeld.class)<br />	public class SomethingTest {</pre>
<p>あ、実際に有効にするには、beans.xml忘れないでね。<br />beans.xmlをどういう風にしたら良いかは<a href="http://memory.empressia.jp/article/107610366.html">別の記事</a>でまとめるよ。</p>
<p>次に、JavaEEのスコープにコンテキストとストレージを設定しないとね。<br />まぁ、これは、GlassFish3.xのころにViewScopedとか作ってたし楽勝ってことで。<br />こんなユーティリティを用意する。長いよ。</p>
<pre class="prettyprint">	public class WeldUtilities {<br /><br />		public static String ExtensionProductName = "Empressia Test Library";<br />		public static String SupportCDIProductName = "Weld";<br /><br />		/** JavaEE向けのスコープをJavaSEでサポートする。 */<br />		public static void supportEEScopes(BeanManager bm)<br />				throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {<br />			Map&lt;String, Object&gt; requestMap = new HashMap&lt;&gt;();<br />			WeldUtilities.activateContextWithStorage(bm, RequestScoped.class, requestMap);<br />			Map&lt;String, Object&gt; sessionMap = new HashMap&lt;&gt;();<br />			WeldUtilities.activateContextWithStorage(bm, SessionScoped.class, sessionMap);<br />			WeldUtilities.activateContextWithStorage(bm, ConversationScoped.class, new MutableBoundRequest(requestMap, sessionMap));<br />		}<br /><br />		/** JavaEE向けのスコープをJavaSEでサポートをリセットする。 */<br />		public static void resetEEScopeContexts(BeanManager bm)<br />				throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {<br />			AbstractBoundContext&lt;?&gt;[] contexts = {<br />				WeldUtilities.findFirstContext(bm, RequestScoped.class),<br />				WeldUtilities.findFirstContext(bm, SessionScoped.class),<br />				WeldUtilities.findFirstContext(bm, ConversationScoped.class)<br />			};<br />			for(AbstractBoundContext&lt;?&gt; context : contexts) {<br />				context.deactivate();<br />				context.cleanup();<br />			}<br />			WeldUtilities.supportEEScopes(bm);<br />		}<br /><br />		public static &lt;S&gt; void activateContextWithStorage(BeanManager bm, Class&lt;? extends Annotation&gt; scopeType, S storage)<br />				throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {<br />			AbstractBoundContext&lt;S&gt; boundContext = WeldUtilities.findFirstContext(bm, scopeType);<br />			boundContext.associate(storage);<br />			boundContext.activate();<br />		}<br /><br />		/** BeanManagerから指定スコープの最初のContextを抽出する。アクティブかどうかは考慮されない。 */<br />		public static &lt;S&gt; AbstractBoundContext&lt;S&gt; findFirstContext(BeanManager bm, Class&lt;? extends Annotation&gt; scopeType)<br />				throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {<br />			Map&lt;Class&lt;? extends Annotation&gt;, List&lt;Context&gt;&gt; contextsMap = WeldUtilities.extractContextsMap(bm);<br />			List&lt;Context&gt; contexts = contextsMap.get(scopeType);<br />			// ContextはScopeの種類に対して必ず1個用意してある前提で。<br />			if(contexts.isEmpty()) {<br />				String message = "なにかメッセージ";<br />				throw new IllegalStateException(message);<br />			}<br />			Context context = contexts.get(0);<br />			if(context instanceof AbstractBoundContext) {<br />			} else {<br />				// java.lang.ClassCastException: org.jboss.weld.context.PassivatingContextWrapper$AlterableContextWrapper cannot be cast to org.jboss.weld.context.AbstractBoundContext<br />				context = PassivatingContextWrapper.unwrap(context);<br />			}<br />			@SuppressWarnings("unchecked")<br />			AbstractBoundContext&lt;S&gt; boundContext = (AbstractBoundContext&lt;S&gt;)context;<br />			return boundContext;<br />		}<br /><br />		/** BeanManagerからScopeTypeごとの？Contextを抽出する。 */<br />		public static Map&lt;Class&lt;? extends Annotation&gt;, List&lt;Context&gt;&gt; extractContextsMap(BeanManager bm)<br />				throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {<br />			BeanManagerImpl beanManagerImpl;<br />			if(bm instanceof BeanManagerProxy) {<br />				BeanManagerProxy beanManagerProxy = (BeanManagerProxy)bm;<br />				beanManagerImpl = beanManagerProxy.delegate();<br />			} else if(bm instanceof BeanManagerImpl) {<br />				beanManagerImpl = (BeanManagerImpl)bm;<br />			} else {<br />				String message = "なにかメッセージ";<br />				throw new IllegalStateException(message);<br />			}<br />			// getContextだとactiveなものしか取れないから、強引に設定したい今回の用途では使えない。<br />			Method m = BeanManagerImpl.class.getDeclaredMethod("getContexts");<br />			m.setAccessible(true);<br />			@SuppressWarnings("unchecked")<br />			Map&lt;Class&lt;? extends Annotation&gt;, List&lt;Context&gt;&gt; contextsMap = (Map&lt;Class&lt;? extends Annotation&gt;, List&lt;Context&gt;&gt;)m.invoke(beanManagerImpl);<br />			return contextsMap;<br />		}<br /><br />	}</pre>
<p>この辺は、解説入れるとめんどくさいから、WeldのソースとかGlassFishのソースとか読むと良いかもってことで。<br />で、これを呼び出すExtensionを用意する。</p>
<pre class="prettyprint">	package jp.empressia.test;<br />	// （略）<br />	public class WeldTestExtension implements Extension {<br /><br />		public void afterDeployment(@Observes AfterDeploymentValidation e, BeanManager bm)<br />				throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {<br />			logger(this).info("Empressia Test Library Weld Test Extension startup.");<br />			WeldUtilities.supportEEScopes(bm);<br />		}<br /><br />	}</pre>
<p>このExtensionは、META-INF/servicesに『javax.enterprise.inject.spi.Extension』ってファイルを作って、その中に、</p>
<pre class="prettyprint">	jp.empressia.test.WeldTestExtension</pre>
<p>とか書いておくよ。こうしておくことで、Weldの初期化がされたときに、@Observesが呼ばれてJavaEEのスコープがすぐに有効になるのだ。<br />あと、テストごとに、スコープはリセットした方が良いだろうから、Runnerのメソッドをオーバーライドしておこうね。</p>
<pre class="prettyprint">	public class JUnit4RunnerWithWeld extends BlockJUnit4ClassRunner {<br /><br />		// （略）<br /><br />		/** テストを1個ずつ実行します。順序は保証されないから、コンテキストは毎回リセットする。 */<br />		@Override<br />		protected void runChild(FrameworkMethod method, RunNotifier notifier) {<br />			BeanManager bm = JUnit4RunnerWithWeld.Container.getBeanManager();<br />			try {<br />				WeldUtilities.resetEEScopeContexts(bm);<br />			} catch(NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {<br />				String message = "なにかメッセージ";<br />				throw new IllegalStateException(message, ex);<br />			}<br />			super.runChild(method, notifier);<br />		}<br /><br />	}</pre>
<p>これで完成(ﾟｰﾟ)(｡_｡)(ﾟ-ﾟ)(｡_｡)ｳﾝｳﾝ</p>
<p>この辺のクラスをパッケージにしたのを置いておいたよ。</p>
<p>Mavenのリポジトリを追加して依存関係を書けば使えるよ。こんな感じ？</p>
<pre class="prettyprint">&lt;repositories&gt;<br />	……<br />	&lt;repository&gt;<br />		&lt;id&gt;Empressia&lt;/id&gt;<br />		&lt;url&gt;<a href="http://www.empressia.jp/maven/" target="_blank">http://www.empressia.jp/maven/</a>&lt;/url&gt;<br />	&lt;/repository&gt;<br />&lt;/repositories&gt;<br /><br />&lt;dependencies&gt;<br />	……<br />	&lt;dependency&gt;<br />		&lt;groupId&gt;jp.empressia&lt;/groupId&gt;<br />		&lt;artifactId&gt;jp.empressia.test&lt;/artifactId&gt;<br />		&lt;version&gt;1.0.1-1&lt;/version&gt;<br />		&lt;scope&gt;test&lt;/scope&gt;<br />	&lt;/dependency&gt;<br />&lt;/dependencies&gt;</pre>
<p>古い実装とかライブラリが混じっていると、JNDIにBeanManagerを登録しておかないとちゃんと動かないかもしれないけど。<br />このパッケージは、その辺も簡単にだけど対応しておいたよ。</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/107596261.html">
<link>http://memory.empressia.jp/article/107596261.html</link>
<title>JavaEEのユニットテスト環境を用意する</title>
<description>☆はじめに☆JavaEEの機能は、去年作ったファイル転送Webアプリだと、WebSocketの機能しか使ってなかったし、そんなユニットテスト書くくらい難しいの自体作ってないんだよね。で、今回は、JavaEEをもうちょっとしっかり使おうかなと。とりあえず、JavaEEが提供しているJAX-RS、CDI、JPAあたりを使ってWebアプリケーションを作ろうと思ったんだけど、これ、どれもユニットテストがめんどくさい。まぁ、JAX-RSは、問題領域とは直接関係ないから、ユニットテストの..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2014-12-21T21:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<h2>☆はじめに☆</h2>
<p>JavaEEの機能は、去年作ったファイル転送Webアプリだと、WebSocketの機能しか使ってなかったし、<br />そんなユニットテスト書くくらい難しいの自体作ってないんだよね。<br />で、今回は、JavaEEをもうちょっとしっかり使おうかなと。</p>
<p>とりあえず、JavaEEが提供しているJAX-RS、CDI、JPAあたりを使ってWebアプリケーションを作ろうと思ったんだけど、<br />これ、どれもユニットテストがめんどくさい。<br />まぁ、JAX-RSは、問題領域とは直接関係ないから、ユニットテストの対象からは外すとして、問題はCDIとJPAだよね。</p>
<p>JavaEE自体は、関数の集まりみたいなライブラリと言うよりは、コンテナだから、<br />ユニットテストするためには、そのコンテナを再現しないといけないんだよね。<br />特に、CDIはコンテナそのものって感じだしね。</p>
<p>あとは、環境に依存したリソースの表現だよね。<br />JPAは、永続化先を抽象化してくれるけど、ユニットテストの時は、それが逆にめんどくさいことに。<br />でも、抽象化されること自体は便利だからやっぱりJPAは使うよね。</p>
<p>なんか、Mockって言うの作って対応したりする例とかよくWebで見かけるんだけど、<br />DBとかに繋げば良いだけなのに、そんなの作るのめんどくさいよね。<br />きっとそれが必要な時もあるんだとは思うけど。</p>
<h2>☆準備☆</h2>
<p>さて、ユニットテスト環境を作るにしても、何か想定はしないとダメだよね。<br />今回は、こんな条件を付けてみるよ。</p>
<ul>
<li>ゴール：JavaEEのWebアプリ向けに問題領域のユニットテストとしてのコンテナを用意して簡単に使えるようにする。</li>
<li>ユニットテストには、JUnitを使う。</li>
<li>CDIとJPA（とJTA）を何となく使えるようにする。</li>
<li>ユニットテストでJPAは1個の接続先を使う。</li>
<li>JavaSE向けの実装は、WeldとEclipseLinkのGlassFish4.1のバージョンを使う。</li>
<li>Mock系のライブラリとかは使わない（使うといろいろ楽だけど依存しない）。</li>
<li>ビルドにはMavenを使う。</li>
</ul>
<p>で、ちょっと書き出してから気づいたんだけど、これ、結構長くなる……。<br />ってことで分割したよ。</p>
<ul>
<li><a href="http://memory.empressia.jp/article/107602460.html">☆JavaEE用のスコープをJavaSE環境で使えるようにする☆</a></li>
<li><a href="http://memory.empressia.jp/article/107606370.html">☆JPAとJTAをJavaSE環境で使えるようにする☆</a></li>
<li><a href="http://memory.empressia.jp/article/107610366.html">☆Maven経由のユニットテストでCDIを簡単に使う☆</a></li>
</ul>
<p>ホントいつも流れで書いてごめんなさい（゜▽、゜</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/107593138.html">
<link>http://memory.empressia.jp/article/107593138.html</link>
<title>Java8の暗号利用モードのGCMって遅すぎない？</title>
<description>去年作ったファイル転送WebアプリをGlassFish4.1とJava8に載せ替えたんだけど、それだけで、凄ーく遅くなったの。載せ替える前はGlassFish4だからJava7で動いてたんだよね。あ、ハードはfit-PC2iのWindows8（32bit）で動いてるよ。実際に測定するとこんな感じ。ファイル転送Webアプリの転送速度（インターネットを経由させて測定）サーバー速度Java7GlassFish4.026000～28000bpsJava8GlassFish4.1260..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2014-12-21T21:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<p>去年作ったファイル転送WebアプリをGlassFish4.1とJava8に載せ替えたんだけど、<br />それだけで、凄ーく遅くなったの。<br />載せ替える前はGlassFish4だからJava7で動いてたんだよね。<br />あ、ハードはfit-PC2iのWindows8（32bit）で動いてるよ。</p>
<p>実際に測定するとこんな感じ。</p>
<table style="border-collapse: collapse;" border="1"><caption>ファイル転送Webアプリの転送速度（インターネットを経由させて測定）</caption>
<tbody>
<tr>
<td colspan="2">サーバー</td>
<td>速度</td>
</tr>
<tr>
<td>Java7</td>
<td>GlassFish4.0</td>
<td>26000～28000bps</td>
</tr>
<tr>
<td>Java8</td>
<td>GlassFish4.1</td>
<td>2600～2800bps</td>
</tr>
</tbody>
</table>
<p>うん、超遅くなった。あり得ないよね、ほんと。</p>
<p>わたしが作った部分がおかしいのかなーとか思って、プログラムを見たんだけど、<br />ファイルの書き込みくらいしか重そうな処理がなったんだよね。<br />ファイルIOが遅いのかなーって思って、ファイルの書き込み処理を外すとこんな感じ。</p>
<table style="border-collapse: collapse;" border="1"><caption>ファイル転送Webアプリの転送速度（インターネットを経由させて測定：書き込み処理無し）</caption>
<tbody>
<tr>
<td colspan="2">サーバー</td>
<td>速度</td>
</tr>
<tr>
<td>Java7</td>
<td>GlassFish4.0</td>
<td>26000～28000bps</td>
</tr>
<tr>
<td>Java8</td>
<td>GlassFish4.1</td>
<td>2600～2800bps</td>
</tr>
</tbody>
</table>
<p>変わんないし！全く変わんないし！</p>
<p>ファイル転送のWebアプリだから、ファイルの書き込み取っちゃったら、何も残らないのに……。<br />と言うことで、JavaとGlassFishに問題がないか探すことに。<br />まぁ、普通に考えると、GlassFish側かな？Javaなコアなところはみんな触ってるだろうし。</p>
<p>とはいえまずは実測実測。パフォーマンス測定の基本は実測だよね！<br />GlasFish4.1はJava7でも動くはずだから計ってみた。</p>
<table style="border-collapse: collapse;" border="1"><caption>ファイル転送Webアプリの転送速度（インターネットを経由させて測定）</caption>
<tbody>
<tr>
<td colspan="2">サーバー</td>
<td>速度</td>
</tr>
<tr>
<td>Java7</td>
<td>GlassFish4.1</td>
<td>26000～28000bps</td>
</tr>
</tbody>
</table>
<p>あれ……。えーっと……。遅くならない……。<br />Javaのコアな部分に問題があるってこと……？？？（゜▽、゜<br />もうわたしにはわからない、わからないよ。</p>
<p>こうなったらもうプロファイル？を見るしかないよね！<br />と言うことで、NetBeansのプロファイル機能を見てみる。</p>
<p>Java8で時間がかかってるあたり<br /> <a href="http://www.empressia.jp/sblo_files/sphere/image/call_hierarchy.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/call_hierarchy-thumbnail2.png" alt="call_hierarchy.png" width="320" height="193" border="0" /></a></p>
<p>うん、完全にSSLの復号化のところですっごい時間かかってるね。<br />同じあたりをJava7とJava8で比較してみる。</p>
<p>Java7の呼び出しツリー<br /> <a href="http://www.empressia.jp/sblo_files/sphere/image/call_hierarchy_jdk7.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/call_hierarchy_jdk7-thumbnail2.png" alt="call_hierarchy_jdk7.png" width="320" height="193" border="0" /></a></p>
<p>Java8の呼び出しツリー<br /> <a href="http://www.empressia.jp/sblo_files/sphere/image/call_hierarchy_jdk8.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/call_hierarchy_jdk8-thumbnail2.png" alt="call_hierarchy_jdk7.png" width="320" height="193" border="0" /></a></p>
<p>Java8は結構呼び出しを展開してみたんだけど、同じ呼び出しが延々深く続いてるだけみたい。<br />NetBeansが重くなって最後まで開けなかったけど。</p>
<p>ここから分かるのは、Java7の方は、『AES』とか『CihperBlockChaining』とかが見えるんだけど、<br />Java8の方は、『AES』の他に、『GaloisCounterMode』とか『GHASH』とかが見える。<br />使われている暗号化の方式が変わってる……？</p>
<p>Oracleのサイトを見ると、Java7からJava8になったときにGCMって言うのが増えてるんだね。<br /> <a href="https://docs.oracle.com/javase/jp/8/technotes/guides/security/SunProviders.html" target="_blank">https://docs.oracle.com/javase/jp/8/technotes/guides/security/SunProviders.html</a></p>
<p>これか！これが悪さしてるのか！新しいのに遅いなんて！</p>
<p>と、決めつける前に、確認確認。<br />Java7とJava8とで、それぞれのGlassFish4.1のセキュリティ設定を確認してみる。</p>
<p>Java7のセキュリティ設定<br /> <a href="http://www.empressia.jp/sblo_files/sphere/image/ciphers_jdk7.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/ciphers_jdk7-thumbnail2.png" alt="ciphers_jdk7.png" width="320" height="193" border="0" /></a></p>
<p>Java8のセキュリティ設定<br /> <a href="http://www.empressia.jp/sblo_files/sphere/image/ciphers_jdk8.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/ciphers_jdk8-thumbnail2.png" alt="ciphers_jdk8.png" width="320" height="193" border="0" /></a></p>
<p>たしかに、Java8の方が多くなってる～。<br />これでJava8の時の暗号化方式をGCM以外から選ぶように設定ッと。<br /> <a href="http://www.empressia.jp/sblo_files/sphere/image/select_without_gcm.png" target="_blank"><img src="http://www.empressia.jp/sblo_files/sphere/image/select_without_gcm-thumbnail2.png" alt="select_without_gcm.png" width="320" height="87" border="0" /></a></p>
<p>測定しなおした結果はこんな感じ。</p>
<table style="border-collapse: collapse;" border="1"><caption>ファイル転送Webアプリの転送速度（インターネットを経由させて測定：GCM除外）</caption>
<tbody>
<tr>
<td colspan="2">サーバー</td>
<td>速度</td>
</tr>
<tr>
<td>Java8</td>
<td>GlassFish4.1</td>
<td>26000～28000bps</td>
</tr>
</tbody>
</table>
<p>速くなった！原因はGCM、これだね！</p>
<p>GlassFish4.1が関係ないことを確認するために、<br />Java8で実際に検証コードを書いてみる。</p>
<p>こんな感じ。大きく差が出るだろうから、適当で良いよね。</p>
<p>CBC</p>
<pre class="prettyprint">	int length = 200000000;<br />	KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");<br />	Key key = keyGenerator.generateKey();<br />	IvParameterSpec parameters = new IvParameterSpec(new byte[16]);<br />	Cipher CBCEncrypter = Cipher.getInstance("AES/CBC/PKCS5Padding");<br />	CBCEncrypter.init(Cipher.ENCRYPT_MODE, key, parameters);<br />	Cipher CBCDecrypter = Cipher.getInstance("AES/CBC/PKCS5Padding");<br />	CBCDecrypter.init(Cipher.DECRYPT_MODE, key, parameters);<br />	byte[] targetBytes = new byte[length];<br />	long time0 = System.currentTimeMillis();<br />	byte[] encryptedBytes = CBCEncrypter.doFinal(targetBytes);<br />	long time1 = System.currentTimeMillis();<br />	byte[] decryptedBytes = CBCDecrypter.doFinal(encryptedBytes);<br />	long time2 = System.currentTimeMillis();<br />	System.out.println(Arrays.equals(targetBytes, decryptedBytes));<br />	System.out.println("暗号化時間：" + (time1 - time0) + "ミリ秒");<br />	System.out.println("復号化時間：" + (time2 - time1) + "ミリ秒");<br />	</pre>
<p>GCM</p>
<pre class="prettyprint">	int length = 200000000;<br />	KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");<br />	Key key = keyGenerator.generateKey();<br />	GCMParameterSpec parameters = new GCMParameterSpec(128, new byte[16]);<br />	Cipher GCMEncrypter = Cipher.getInstance("AES/GCM/PKCS5Padding");<br />	GCMEncrypter.init(Cipher.ENCRYPT_MODE, key, parameters);<br />	Cipher GCMDecrypter = Cipher.getInstance("AES/GCM/PKCS5Padding");<br />	GCMDecrypter.init(Cipher.DECRYPT_MODE, key, parameters);<br />	byte[] targetBytes = new byte[length];<br />	long time0 = System.currentTimeMillis();<br />	byte[] encryptedBytes = GCMEncrypter.doFinal(targetBytes);<br />	long time1 = System.currentTimeMillis();<br />	byte[] decryptedBytes = GCMDecrypter.doFinal(encryptedBytes);<br />	long time2 = System.currentTimeMillis();<br />	System.out.println(Arrays.equals(targetBytes, decryptedBytes));<br />	System.out.println("暗号化時間：" + (time1 - time0) + "ミリ秒");<br />	System.out.println("復号化時間：" + (time2 - time1) + "ミリ秒");<br />	</pre>
<p>測定結果は、こんな感じ～。</p>
<table style="border-collapse: collapse; text-align: right;" border="1">
<tbody>
<tr style="text-align: center;">
<td rowspan="3">#</td>
<td rowspan="3">バイト数</td>
<td colspan="4">時間（ミリ秒）</td>
<td colspan="3">&nbsp;</td>
</tr>
<tr style="text-align: center;">
<td colspan="2">CBC</td>
<td colspan="2">GCM</td>
<td colspan="3">GCM/CBC</td>
</tr>
<tr style="text-align: center;">
<td>暗号化</td>
<td>復号化</td>
<td>暗号化</td>
<td>復号化</td>
<td>暗号化</td>
<td>復号化</td>
<td>AVG</td>
</tr>
<tr>
<td>1</td>
<td>100000</td>
<td>16</td>
<td>24</td>
<td>160</td>
<td>96</td>
<td>10.0</td>
<td>4.0</td>
<td>7.0</td>
</tr>
<tr>
<td>2</td>
<td>100000</td>
<td>16</td>
<td>24</td>
<td>160</td>
<td>96</td>
<td>10.0</td>
<td>4.0</td>
<td>7.0</td>
</tr>
<tr>
<td>3</td>
<td>1000000</td>
<td>64</td>
<td>88</td>
<td>1000</td>
<td>915</td>
<td>15.6</td>
<td>10.4</td>
<td>13.0</td>
</tr>
<tr>
<td>4</td>
<td>1000000</td>
<td>56</td>
<td>88</td>
<td>1016</td>
<td>912</td>
<td>18.1</td>
<td>10.4</td>
<td>14.3</td>
</tr>
<tr>
<td>5</td>
<td>10000000</td>
<td>392</td>
<td>400</td>
<td>8483</td>
<td>7852</td>
<td>21.6</td>
<td>19.6</td>
<td>20.6</td>
</tr>
<tr>
<td>6</td>
<td>10000000</td>
<td>376</td>
<td>368</td>
<td>7881</td>
<td>8004</td>
<td>21.0</td>
<td>21.8</td>
<td>21.4</td>
</tr>
<tr>
<td>7</td>
<td>100000000</td>
<td>3060</td>
<td>3351</td>
<td>53760</td>
<td>68640</td>
<td>17.6</td>
<td>20.5</td>
<td>19.0</td>
</tr>
<tr>
<td>8</td>
<td>100000000</td>
<td>3065</td>
<td>3325</td>
<td>74182</td>
<td>72976</td>
<td>24.2</td>
<td>21.9</td>
<td>23.1</td>
</tr>
<tr>
<td>9</td>
<td>100000000</td>
<td>2720</td>
<td>3142</td>
<td>61908</td>
<td>64244</td>
<td>22.8</td>
<td>20.4</td>
<td>21.6</td>
</tr>
<tr>
<td>10</td>
<td>200000000</td>
<td>4886</td>
<td>5103</td>
<td>110236</td>
<td>102925</td>
<td>22.6</td>
<td>20.2</td>
<td>21.4</td>
</tr>
<tr>
<td>11</td>
<td>200000000</td>
<td>6267</td>
<td>6223</td>
<td>125277</td>
<td>110626</td>
<td>20.0</td>
<td>17.8</td>
<td>18.9</td>
</tr>
<tr>
<td>12</td>
<td>200000000</td>
<td>5658</td>
<td>6211</td>
<td>124878</td>
<td>124675</td>
<td>22.1</td>
<td>20.1</td>
<td>21.1</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>うん、サイズが小さいときも遅いけど、サイズが大きくなってくるとだいたい20倍くらい遅いね！これはひどい（゜▽、゜<br />GCMのWikipedia見た感じだと、速くなりそうなのにね。<br />遅いにしても限度って言うものがあると思うんだけど……。<br />もしかして、去年Windowsのアプリが遅くなったのも何か関係あったりするのかな？<br />暗号化は、WindowsのNativeなAPI使ってるとか……？</p>
<p>まぁ、とりあえず直ったから良いよね(ﾟｰﾟ)(｡_｡)(ﾟ-ﾟ)(｡_｡)ｳﾝｳﾝ</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/107581178.html">
<link>http://memory.empressia.jp/article/107581178.html</link>
<title>JavaEEなWebアプリケーションを作ろうとしたときのお話</title>
<description>☆はじめに☆この記事はJava Advent Calendar 2014の12月21日分の記事だよ。前は、2012年の時にAndroidの話を書いたんだけど、今回は、Webアプリケーションを作ろうとしたときの話を書くのだ。☆導入☆去年の12月くらいに、いつも使ってたWindowsのファイル転送アプリが凄ーく遅くなって、もう、これは耐えられない！ってことで、代わりにGlassFish4のWebSocketを使ったファイル転送Webアプリを作ったの。で、先月、他にもなんかWebア..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2014-12-21T21:00:00+09:00</dc:date>
<content:encoded><![CDATA[
<h2>☆はじめに☆</h2>
<p>この記事はJava Advent Calendar 2014の12月21日分の記事だよ。</p>
<p>前は、2012年の時にAndroidの話を書いたんだけど、<br />今回は、Webアプリケーションを作ろうとしたときの話を書くのだ。</p>
<h2>☆導入☆</h2>
<p>去年の12月くらいに、いつも使ってたWindowsのファイル転送アプリが凄ーく遅くなって、<br />もう、これは耐えられない！ってことで、代わりにGlassFish4のWebSocketを使ったファイル転送Webアプリを作ったの。</p>
<p>で、先月、他にもなんかWebアプリケーションを作ってみようかなって思って、<br />ついでだからGlassFishを4.1にバージョンアップ！さらにJavaも7から8にバージョンアップ！したんだけど、<br />そのとたんに、ファイル転送Webアプリの速度が凄～く遅くなったんだよね。</p>
<p>と言うわけで前編はその時のお話だよ。</p>
<ul>
<li><a href="http://memory.empressia.jp/article/107593138.html">☆Java8の暗号利用モードのGCMって遅すぎない？☆</a></li>
</ul>
<p>で、遅いのは解決したんだけど、<br />新しいWebアプリは、ただ、転送するだけのアプリと違って、<br />いろいろ作りたかったから、<br />ちょっとがんばってユニットテストも作ろうかな！って思ったんだよね。</p>
<p>と言うわけで後編はその時のお話だったり。</p>
<ul>
<li><a href="http://memory.empressia.jp/article/107596261.html">☆JavaEEのユニットテスト環境を用意する☆</a></li>
<ul>
<li><a href="http://memory.empressia.jp/article/107602460.html">☆JavaEE用のスコープをJavaSE環境で使えるようにする☆</a></li>
<li><a href="http://memory.empressia.jp/article/107606370.html">☆JPAとJTAをJavaSE環境で使えるようにする☆</a></li>
<li><a href="http://memory.empressia.jp/article/107610366.html">☆Maven経由のユニットテストでCDIを簡単に使う☆</a></li>
</ul>
</ul>
<p>ちなみに、今回作ったWebアプリケーション自体については何も書いてなかったりする（゜▽、゜</p>
<p>それと、今回はホント、ネタのあたりから、検証あたりとか、いろいろ協力してもらえて助かったのだ(ﾟｰﾟ)(｡_｡)(ﾟ-ﾟ)(｡_｡)ｳﾝｳﾝ<br />間接的にも助けてもらったりしたし、みんなに感謝なのだ（゜▽、゜</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/60610228.html">
<link>http://memory.empressia.jp/article/60610228.html</link>
<title>Androidのjava.util.zipパッケージと戦った記憶</title>
<description>この記事はAdvent Calendar 2012 Javaの8日目だよ。前の日の記事はryu22e（@ryu22）さんで、『Javaのデータベースマイグレーションツール「Flyway」 #JJUG』だったね。次の日の記事はHideki Kishida（@quicy）さんがやってくれるみたいだよ。で、この記事では、わたしが最近作っている『コミみみ』って言うAndroidアプリの動きを確認していたときのことを淡々と書くのだ。読みやすさは全く考えられていないので覚悟して読んでね。..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2012-12-08T11:34:40+09:00</dc:date>
<content:encoded><![CDATA[
この記事は<a href="http://atnd.org/events/33871">Advent Calendar 2012 Java</a>の8日目だよ。<br /><p>前の日の記事はryu22e（<a href="http://twitter.com/ryu22e">@ryu22</a>）さんで、<br />『<a href="http://blog.livedoor.jp/ryu22e/archives/65722084.html">Javaのデータベースマイグレーションツール「Flyway」 #JJUG</a>』だったね。<br />次の日の記事はHideki Kishida（<a href="http://twitter.com/quicy">@quicy</a>）さんがやってくれるみたいだよ。</p><p>で、この記事では、わたしが最近作っている『<a href="https://play.google.com/store/apps/details?id=jp.empressia.app.comimimi">コミみみ</a>』って言うAndroidアプリの動きを確認していたときのことを淡々と書くのだ。<br />読みやすさは全く考えられていないので覚悟して読んでね。</p><p>『<a href="https://play.google.com/store/apps/details?id=jp.empressia.app.comimimi">コミみみ</a>』って言うのは、コミックマーケット用のアプリ……まぁ、えっと、カタログViewerなんだけど、<br />サークルカット（画像）をZIPで1つのファイルにまとめたものを読み込んでいるんだ。<br />その読み込みをするときに問題が起こったの。</p><p>ZIPファイルをJavaで扱う定番は、java.util.zipパッケージのZipFileクラスだよね。<br />で、ZipFileクラスで有名な問題といえば、<br />開こうとしているZIPファイルに含まれているファイル数が65535個を超えていると開けないってこと。<br />ZIP64っていう拡張仕様までサポートしてないよってことだから、不具合と言うより仕様かな。<br />これはAndroidに限らずある問題だね。Java7ではサポートしたみたいだけど、AndroidはJava6までだし。</p><p>65535個って言うのは、unsigned short型（2バイト）での表現の限界から来ているみたい。<br />だから、ZIP64をサポートしないと、それより多いファイルを含むZIPファイルは開けない。</p><p>Androidでわたしがぶつかった問題も、この『含まれているファイルの数』によるものなの。<br />この問題をぐーぐるさんで検索すると『多すぎるからだよ！』って言うのがたくさんヒットするよ。</p><p>でも、カタログでサークルカットが多いって言っても、35000個くらいしかないんだよね……。<br />65535個を超えてなくても開けないっ。<br />試して見ると、だいたい32000個を超えてきたくらいで開けなくなるんだよね。</p><p>はい、これでわかっちゃうと思うけど、32767個を超えると開けなくなるみたい。<br />Javaにはunsignedな型はないから、<br />unsigned shortで表現されている『含まれているファイルの数』を持つためにはint型が必要になるんだよね。<br />short型は-32768～32767までしか表現できないから。<br />たぶん、short型でファイルの数を持っちゃっておかしくなっているんだと思う。</p><p>あれ？でも、ちょっと待って。なんでこんな問題があるの？<br />java.util.zipパッケージなんて大昔からあるわけでしょ？<br />そこに、こんな問題があるなんておかしいでしょ！</p><p>で、調べてみるとAndorid 2.3.3系では発生しないんだよね。これ。<br />でも、Android 4.0.xでは発生するの。Android 3.xはどっちかな～？まぁ、どっちでもいいよね。<br />つまり、これ、最近……でもないかもだけど、java.util.zipパッケージが書き換えられたことで起こっているみたい。</p><p>……え？やっぱり、おかしくない？<br />拡張仕様をサポートしたわけでもないのに、ZipFileの実装が書き換わってる……？</p>Android 2.3.3系は……大丈夫っ。でも、その後書き換えられておかしくなっている……。<br /><p>もしかして、OracleとGoogleの訴訟の問題から来てたりするのかな？<br />やっぱりGoogle、やっちゃってたのかな？とか想像してみちゃった。<br />詳しいこと何も知らないけどね！</p><p>Android 4系のアプリサポートはこれからだろうから、<br />ZIPでファイルをいっぱい使うする人は気をつけようね。</p>そこ！ZIPファイルで数万もファイル扱うことなんて無いなんて言わないでっ！<br />あ、忘れるところだった。解決方法だけど、<br /><ol><li>ZIPフォーマットは簡単だから自分で作る。</li><li>Android 2.3.3系は動くからそっちから持ってくる。</li><li>org.apache.tools.zipパッケージを使う。</li></ol>って言う感じかな。最後のは、java.util.zipパッケージより20倍～40倍遅いからやめた方が良いと思うけど。<br /><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/58268386.html">
<link>http://memory.empressia.jp/article/58268386.html</link>
<title>Androidのライブラリを作ってみたよ</title>
<description>Empressia for AndroidってAndroid用のライブラリを作ってみたよ。いくつか便利なクラスを用意してあるから、紹介するね。※Android 2.3.3（API Level 10）以上、r16以上のSupport Library v4を使ってるよ。DBUnitJPAみたいな@Idと@Columnのアノテーションをデータクラスにつけて、簡単にDBのやりとりをするクラスだよ。SQLかかなくてすむわけじゃないけど、だいぶ楽になるんじゃないかな～。基本的には、継承し..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2012-09-15T15:22:35+09:00</dc:date>
<content:encoded><![CDATA[
<h3><a href="http://www.empressia.jp/jar/empressiaforandroid.jar">Empressia for Android</a>って</h3><p><a href="http://www.empressia.jp/jar/empressiaforandroid.jar">Android用のライブラリ</a>を作ってみたよ。<br />いくつか便利なクラスを用意してあるから、紹介するね。<br />※Android 2.3.3（API Level 10）以上、r16以上のSupport Library v4を使ってるよ。</p><h4>DBUnit</h4><p>JPAみたいな@Idと@Columnのアノテーションをデータクラスにつけて、<br />簡単にDBのやりとりをするクラスだよ。<br />SQLかかなくてすむわけじゃないけど、だいぶ楽になるんじゃないかな～。</p>基本的には、継承して使ってね。たとえばこんな感じ。<br /><pre class="prettyprint">public class HogeDBUnit extends DBUnit {<br />	@Override<br />	protected Class&lt;?&gt;[] getManagedClasses() {<br />		return new Class&lt;?&gt;[] { DataClass1.class, /* DataClass2.class, DataClass3.class */ };<br />	}<br />}<br /></pre><pre class="prettyprint">public class DataClass1 {<br />	@Id @Column<br />	public int ID;<br />	@Column<br />	public String Name;<br />}<br /></pre><p>これで、DBUnit使うときに適当なCREATE文が自動で作られてDBが準備されるよ。<br />アノテーションはsetterとかにつけてもok。</p>データを全部取ってくる場合は<br /><pre class="prettyprint">List list = this.DBUnit.findAll(DataClass1.class, null);</pre>これで全件取得できるよ。<br /><p>まぁ、JOINとかWHEREとかを書くときは、全部自力で書かないといけないんだけどね。<br />でもオブジェクトにマッピングするところは自動でできるよ。</p><h4>SharedPreferences</h4><p>Androidに用意されてるSharedPreferencesのキーってStringなんだよね。<br />ローカライズするものじゃないからだと思うけど、キーをリソースにしたくなったりしない？<br />そういう場合は、このクラス。</p><pre class="prettyprint">SharedPreferences prefs = new SharedPreferences(originalPrefs);<br /></pre>こんな感じでラップすればリソースをキーに使えるよ。<br /><pre class="prettyprint">SharedPreferences prefs = SharedPreferencesUtilities.getPrivate(context);<br /></pre>ってやれば、アプリ用の設定を一発で取ってくることもできるよ。<br />う～ん、微妙？<br />キーをリテラルで埋め込んでるよりはましかな～って思ったんだけど。&nbsp;<br /><h4>AbstractListAdapter</h4><p>ListAdapterでなんかViewのリサイクルとか考えるのが面倒だから、AbstractListAdapterって言うのを用意してみたよ。<br />createViewとupdateViewをそれぞれ実装すれば良いだけだから簡単だよ。</p><h4>AbstractListPagerAdapter</h4><p>ViewPager用のクラスを作ってみたよ。<br />ViewPagerは元々、いろんな見栄えのページを切り替えるために作られたと思うんだけど、<br />同じ見栄えのページをListViewみたいに連続して表示したいと思うことってない？</p>コンストラクタで、レイアウトと表示するデータのリストを渡すと、あとは自動で切り替えとかするよ。<br /><p>updateViewでViewを書き換えるだけで横に1ページごとに切り替えられるListViewのできあがり。<br />PagerAdateprとか、FragmentPagerAdapter、FragmentStatePagerAdapterから作るよりも<br />楽に作れてメモリも節約できるんじゃないかな～。</p><p>他にもいろいろクラス入ってるので試してみてね(&#39;-^*/<br />Javadocもあるんだけど、なんかメモとか混入してるからほしい人がいればってことで……（゜▽、゜</p><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://memory.empressia.jp/article/54628506.html">
<link>http://memory.empressia.jp/article/54628506.html</link>
<title>DoCoMoのアプリ落ちすぎ</title>
<description>SPモードなメールを見るようになってからだと思うんだけど、なんかすごくDoCoMoのアプリが落ちる。とりあえず、スタックトレースを2個拾ってみた。ATOKの時は発生率低かったけど、こっちは簡単に起きるね（笑）最初のやつは、黙って裏で吐いてるエラー（普通に使ってる人には見えない）。２番目のは、強制終了したとかメッセージが出てくる。バグレポートも送れないし、プリインストールするならもっとちゃんとしたの作ってほしいな。2個目のは、DoCoMoじゃないのかな？03-25 22:58:..</description>
<dc:subject>雁字</dc:subject>
<dc:creator>すふぃあ</dc:creator>
<dc:date>2012-03-25T23:22:23+09:00</dc:date>
<content:encoded><![CDATA[
<p>SPモードなメールを見るようになってからだと思うんだけど、なんかすごくDoCoMoのアプリが落ちる。<br />とりあえず、スタックトレースを2個拾ってみた。<br />ATOKの時は発生率低かったけど、こっちは簡単に起きるね（笑）</p><p>最初のやつは、黙って裏で吐いてるエラー（普通に使ってる人には見えない）。<br />２番目のは、強制終了したとかメッセージが出てくる。<br />バグレポートも送れないし、プリインストールするならもっとちゃんとしたの作ってほしいな。<br />2個目のは、DoCoMoじゃないのかな？</p><pre>03-25 22:58:06.861: E/DatabaseUtils(32600): Writing exception to parcel<br />03-25 22:58:06.861: E/DatabaseUtils(32600): android.database.sqlite.SQLiteAbortException: unavailable to link the app during the startup process.<br />03-25 22:58:06.861: E/DatabaseUtils(32600): 	at jp.co.nttdocomo.carriermail.service.AppLinkProvider.h(AppLinkProvider.java:157)<br />03-25 22:58:06.861: E/DatabaseUtils(32600): 	at jp.co.nttdocomo.carriermail.service.AppLinkProvider.query(AppLinkProvider.java:371)<br />03-25 22:58:06.861: E/DatabaseUtils(32600): 	at android.content.ContentProvider$Transport.bulkQuery(ContentProvider.java:174)<br />03-25 22:58:06.861: E/DatabaseUtils(32600): 	at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:111)<br />03-25 22:58:06.861: E/DatabaseUtils(32600): 	at android.os.Binder.execTransact(Binder.java:324)<br />03-25 22:58:06.861: E/DatabaseUtils(32600): 	at dalvik.system.NativeStart.run(Native Method)<br /></pre><pre>03-25 22:58:09.351: E/AndroidRuntime(32621): FATAL EXCEPTION: main<br />03-25 22:58:09.351: E/AndroidRuntime(32621): android.database.sqlite.SQLiteException: unable to close due to unfinalised statements<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.database.sqlite.SQLiteDatabase.dbclose(Native Method)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.database.sqlite.SQLiteDatabase.onAllReferencesReleased(SQLiteDatabase.java:327)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.database.sqlite.SQLiteDatabase.close(SQLiteDatabase.java:892)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.database.sqlite.SQLiteOpenHelper.close(SQLiteOpenHelper.java:220)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at jp.co.omronsoft.android.decoemojimanager_docomo.DBManager.close(DBManager.java:224)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at jp.co.omronsoft.android.decoemojimanager_docomo.DecoEmojiManager$1.handleMessage(DecoEmojiManager.java:159)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.os.Handler.dispatchMessage(Handler.java:99)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.os.Looper.loop(Looper.java:138)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at android.app.ActivityThread.main(ActivityThread.java:3701)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at java.lang.reflect.Method.invokeNative(Native Method)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at java.lang.reflect.Method.invoke(Method.java:507)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636)<br />03-25 22:58:09.351: E/AndroidRuntime(32621): 	at dalvik.system.NativeStart.main(Native Method)<br /></pre><a name="more"></a>

]]><![CDATA[
]]></content:encoded>
</item>
</rdf:RDF>
