設定画面のEditor -> General -> Editor Tabsで設定する。
Tab AppearanceのMark modified tabs with asteriskのチェックをオンにすると、未保存のファイルのタブにアスタリスクが表示されるようになる。
表示のされ方はこんな感じ。
おわり。
お仕事で、リクエストボディのJSONの内容をログに出力したいんだと言われたので作ってみた。
1. FilterでServletRequestをラップする
2. ServletRequestのラッパーでは、InputStreamのgetterでログ出力機能をもったServletInputStream実装を返す
3. ログ出力機能を持ったServletInputStream実装では、read要求の中で読み込んだデータをログに出力する。
Filterでボディの内容を全部ログに出力してから、ServletRequestラップして後続に処理を移譲するでも良いんだけど、ボディが全部到達するまでFilterで処理止まってしまうんで、read要求の中でログ出力するようにしている感じです。
WildFly9.0.2で試したところ、デフォルトの構成だとServletRequestをラップすると怒られるので、以下コマンドでラップしたRequestやResponseを許容するようにしてあげる必要があるみたいです。
$ ./jboss-cli.sh [disconnected /] connect [standalone@localhost:9990 /] cd subsystem=undertow/servlet-container=default [standalone@localhost:9990 servlet-container=default] :write-attribute(name=allow-non-standard-wrappers, value=true)
ラッパーを許容しない設定の場合に発生する例外はこれ。
Exception handling request to /loggingfilter-1.0-SNAPSHOT/app: java.lang.IllegalArgumentException: UT010023: Request siosio.LoggingFilter$RequestWrapper@77ab9fb3 was not original or a wrapper
@WebFilter(urlPatterns = arrayOf("/app/*")) class LoggingFilter : Filter { override fun destroy() { } override fun init(filterConfig: FilterConfig?) { } override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { // ServletRequestをラップして後続のフィルタに処理を移譲 chain.doFilter(RequestWrapper(request as HttpServletRequest), response) } class RequestWrapper(val request: HttpServletRequest) : HttpServletRequest by request { override fun getInputStream(): ServletInputStream? { // ログ出力機能付きのServletInputStreamを返す return LoggingInputStream(request.inputStream) } } class LoggingInputStream(val inputStream: ServletInputStream) : ServletInputStream() { companion object { val logger: Logger = Logger.getLogger(LoggingInputStream::class.java) } override fun isReady(): Boolean { return inputStream.isFinished } override fun isFinished(): Boolean { return inputStream.isFinished } override fun setReadListener(readListener: ReadListener?) { inputStream.setReadListener(readListener) } override fun read(b: ByteArray?): Int { return inputStream.read(b) } override fun read(b: ByteArray, off: Int, len: Int): Int { // 読み込んだ内容やバイト数をログに出力する val result = inputStream.read(b, off, len) logger.info("read http body. offset:$off, length:$result, data:${b.copyOf(result).toString("UTF-8")} ") return result } override fun readLine(b: ByteArray?, off: Int, len: Int): Int { return super.readLine(b, off, len) } override fun read(): Int { return inputStream.read() } } }
こんな感じにログに出力されますよと。
16:59:52,263 INFO [siosio.LoggingFilter$LoggingInputStream] (default task-4) read http body. offset:0, length:31, data:{ "name": "あいうえお" }
おわり。
ドキュメントにこんな注意書きが増えてた。。。
Oracle12cで試してみたら、全角はパターン一致文字にならなかった。全く把握できてなかった(´・ω・`)
SQL> select * from test where value like 'ほ_'; ID VALUE -- ---------- 1 ほげ SQL> select * from test where value like 'ほ_'; レコードが選択されませんでした。
Arquillian REST Extension 1.0.0.Alpha3 Released · Arquillian Blog を見ながらやるとかなり簡単にできた。
シンプルなこんな感じのリソースクラスをテスト対象に。。。
public class HelloResource { @GET @Produces(MediaType.TEXT_PLAIN) public String get() { return "hello"; } }
testCompile 'org.jboss.arquillian.junit:arquillian-junit-container:1.1.9.Final' testRuntime 'org.jboss.arquillian.container:arquillian-glassfish-embedded-3.1:1.0.0.CR4' testRuntime 'org.glassfish.main.extras:glassfish-embedded-all:4.1' testRuntime 'org.jboss.arquillian.extension:arquillian-rest-client-impl-jersey:1.0.0.Alpha3'
テストメソッドにテスト対象のリソースクラスをinjectすることができるので、あとはテスト対象メソッドを呼び出して結果をアサートするだけ。
@RunWith(Arquillian.class) public class HelloResourceTest { @Deployment public static WebArchive createDeployment() { return ShrinkWrap.create(WebArchive.class, "hoge.war") .addClass(HelloResource.class); } @Test public void test(@ArquillianResteasyResource HelloResource resource) throws Exception { System.out.println("resource = " + resource); assertThat(resource.get(), is("hello")); } }
実行できました。
おわり。
Chunckステップ実行中にバッチジョブを停止する方法。
基本的な考え方やステータスの遷移は、Batchletの停止と同じ。違いは、Chunkの場合はBatchletのような停止用の実装を行う必要はないこと。
Chunkの場合は、Writerが処理中の場合はWriterの処理が終わり次の塊のデータを読み込んだあとにジョブが停止される。*1
もし、chunkのそれぞれの処理(readerやwriter、processorなど)が終わらない場合、ジョブの停止要求を出しても自動的にジョブは終わらないので、それぞれの処理が終わらない原因を取り除く必要がある。
例えば、データベースのロック待機などで永遠にクライアントに処理が戻ってこない場合には、停止要求を出してもジョブは停止しない。
ItemReadListenerでは、Itemリード後のコールバック処理でリードしたitemの内容とジョブのステータスを標準出力に書き込んでいる。
@Inject lateinit val jobContext:JobContext override fun afterRead(item: Any?) { item?.let { println("read item = ${item}, job status = ${jobContext.batchStatus}") } }
Chunkのitem-countを5で実行した場合で、4件目のレコードを読込中のジョブの停止命令を出した場合のログの内容。4件目のレコードを読み込んだあとのジョブのステータスは停止中を表すSTOPPINGになり、5件目のレコードは読み込まずに今まで読み込んだitemがwriterに渡され処理されているのがわかる。
23:09:27,474 INFO [stdout] (Batch Thread - 1) read item = 1, job status = STARTED 23:09:32,485 INFO [stdout] (Batch Thread - 1) read item = 2, job status = STARTED 23:09:37,487 INFO [stdout] (Batch Thread - 1) read item = 3, job status = STARTED 23:09:42,491 INFO [stdout] (Batch Thread - 1) read item = 4, job status = STOPPING 23:09:42,500 INFO [stdout] (Batch Thread - 1) it = 1 23:09:42,500 INFO [stdout] (Batch Thread - 1) it = 2 23:09:42,500 INFO [stdout] (Batch Thread - 1) it = 3 23:09:42,501 INFO [stdout] (Batch Thread - 1) it = 4
Chunkステップ処理中にジョブを停止すると、ジョブリポジトリ(step_executionテーブル)にステップの処理状態が記録される。
以下のように、何件目まで正常に処理できたかなどが記録される。この情報は、停止したジョブをリスタートした場合の再開ポイントとして利用される。
おわり。
*1:jberetの場合
JSR352-Batch Applicationを試してみた(ジョブの停止-Batchlet) - しおしおの続編。今回は、ジョブにパラーメータを渡す方法を試してみた。
以下のコードのように、JobOperator#startの2番めの引数にジョブ起動時のパラメータを指定する。
final Properties jobParams = new Properties(); jobParams.put("param1", "value1"); jobParams.put("param2", "100"); final JobOperator operator = BatchRuntime.getJobOperator(); final long executionId = operator.start(jobId, jobParams);
ジョブパラメータは、BatchletやChunk関連の実装クラス、リスナーなどにBatchPropertyとしてインジェクションする。自動的にインジェクションされるわけではないので、ジョブXMLでジョブパラメータをBatchPropertyにセットする設定が必要となる。
ジョブパラメータを使用する実装サンプル
@Dependent @Named public class JobParameterSampleBatchlet: AbstractBatchlet() { @Inject @BatchProperty var param:String? = null override fun process(): String { log.info("param: {}", param) return "success" } companion object { val log:Logger = LoggerFactory.getLogger(javaClass<JobParameterSampleBatchlet>()) } }
ジョブパラメータを設定するジョブXMLのサンプル
ジョブパラメータを設定するには、以下の例のように#{jobParameters['パラメータ名']}というEL式を使用する。
この例では、ジョブ起動時に指定されたパラメータの中で、パラメータ名がjob-paramの値を、paramプロパティに設定する。
<job id="jobParameter" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> <step id="myStep"> <batchlet ref="jobParameterSampleBatchlet"> <properties> <property name="param" value="#{jobParameters['job-param']}" /> </properties> </batchlet> </step> </job>
実行すると
こんなパラメータを設定してと
Properties properties = new Properties(); properties.put("job-param", "パラメータ");
ログにジョブパラメータが出力されたので、パラメータがBatchletに設定されているのがわかる。
13:34:13,554 INFO [siosio.batchlet.JobParameterSampleBatchlet] (Batch Thread - 1) param: パラメータ
おわり
Gradleのjarタスクで、gitのどのリビジョンをベースに作成したかをMANIFEST.MFに書き込むGradleプラグインを作ってみた。
最近、社内のMavenリポジトリに古いソースコードベースのartifactが頻繁にアップされてかなりハマったので。。。
jarがどのリビジョンベースで作られたのかサクッとわかると、個人的にはわりと便利な気がしたので作ってみた感じです。*1
こんな感じにプラグイン適用するだけな感じです。
buildscript { repositories { mavenLocal() } dependencies { classpath 'siosio:add-git-info-to-manifest:+' } } apply plugin: 'AddGitInfoToManifest'