しおしお

IntelliJ IDEAのことなんかを書いてます

IntelliJ IDEA16でMavenプロジェクトが開けない場合の対処方法

IntelliJ IDEAのバージョン16で、Mavenプロジェクトを開こううとした時に下のエラーが出た場合の対処方法。

com.intellij.execution.ExecutionException:
java.lang.UnsupportedClassVersionError: org/jetbrains/idea/maven/server/RemoteMavenServer : Unsupported major.minor version 52.0

原因

IntelliJ IDEA16からJava8でビルドさていているので、pomを開く時のJavaのバージョンも8にする必要があります。

pomを開くときにJava8でビルドされたMavenプロジェクトのAPIを呼び出しているのが原因なんだけど、
なんで対応してないバージョン普通に選べるのか謎な感じある。

対策

この手順で、pom開く際のjdkを変更する必要があります。

デフォルト設定画面を開く

Welcome画面から設定画面を開くとデフォルトの設定値を変更できるので、Welcome画面から設定画面を開く。
f:id:sioiri:20160223113310p:plain

MavenJDK for importerをJava8に変更する

画像のように変更する。
f:id:sioiri:20160223113311p:plain

おわり。

IntelliJ IDEAのタブ設定で未保存のファイルをわかるようにする

設定画面のEditor -> General -> Editor Tabsで設定する。
Tab AppearanceのMark modified tabs with asteriskのチェックをオンにすると、未保存のファイルのタブにアスタリスクが表示されるようになる。

表示のされ方はこんな感じ。
f:id:sioiri:20160127213851p:plain

おわり。

ServletInputStreamをラップして、リクエストのボディをログに出力してみる

お仕事で、リクエストボディのJSONの内容をログに出力したいんだと言われたので作ってみた。

実現方法

1. FilterでServletRequestをラップする
2. ServletRequestのラッパーでは、InputStreamのgetterでログ出力機能をもったServletInputStream実装を返す
3. ログ出力機能を持ったServletInputStream実装では、read要求の中で読み込んだデータをログに出力する。

Filterでボディの内容を全部ログに出力してから、ServletRequestラップして後続に処理を移譲するでも良いんだけど、ボディが全部到達するまでFilterで処理止まってしまうんで、read要求の中でログ出力するようにしている感じです。

WildFlyでの使う場合

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": "あいうえお" } 

おわり。

Oracle11gR2からはlikeのパターン一致文字に全角が含まれなくなったもよう

ドキュメントにこんな注意書きが増えてた。。。

f:id:sioiri:20151120075118p:plain

Oracle12cで試してみたら、全角はパターン一致文字にならなかった。全く把握できてなかった(´・ω・`)

SQL>  select * from test where value like 'ほ_';

ID VALUE
-- ----------
 1 ほげ

SQL>  select * from test where value like 'ほ_';

レコードが選択されませんでした。

jax-rsなリソースクラスをArquillianを使ってテストしてみた

Arquillian REST Extension 1.0.0.Alpha3 Released · Arquillian Blog を見ながらやるとかなり簡単にできた。

テスト対象のリソースクラス

シンプルなこんな感じのリソースクラスをテスト対象に。。。

public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String get() {
        return "hello";
    }
}

build.gradleに必要なライブラリを追加

  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"));
    }
}

テスト実行結果

実行できました。
f:id:sioiri:20150930150217p:plain

おわり。

JSR352-Batch Applicationを試してみた(ジョブの停止-Chunk)

Chunckステップ実行中にバッチジョブを停止する方法。

基本的な考え方やステータスの遷移は、Batchletの停止と同じ。違いは、Chunkの場合はBatchletのような停止用の実装を行う必要はないこと。

Chunkの場合は、Writerが処理中の場合はWriterの処理が終わり次の塊のデータを読み込んだあとにジョブが停止される。*1

もし、chunkのそれぞれの処理(readerやwriter、processorなど)が終わらない場合、ジョブの停止要求を出しても自動的にジョブは終わらないので、それぞれの処理が終わらない原因を取り除く必要がある。
例えば、データベースのロック待機などで永遠にクライアントに処理が戻ってこない場合には、停止要求を出してもジョブは停止しない。

ItemReadListenerで動きを確認してみる

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テーブル)にステップの処理状態が記録される。
以下のように、何件目まで正常に処理できたかなどが記録される。この情報は、停止したジョブをリスタートした場合の再開ポイントとして利用される。
f:id:sioiri:20150925145845p:plain

おわり。

*1:jberetの場合

JSR352-Batch Applicationを試してみた(ジョブにパラメータを指定する)

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: パラメータ

おわり