しおしお

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

sphinxからjavadocへのリンク簡単に貼れる拡張

Sphinxのドキュメントから、Javadocページ編のリンクを貼るのを簡単にするjavasphinx User’s Guide — javasphinx 0.9.8 documentationという拡張です。

この拡張を使うと、Javadocのトップページへのパスをconf.pyに設定して置けるので、
ここのページではクラスの完全修飾名を書くだけでよく割りと簡単にリンクが作成できます。

インストー

拡張のページに有るようにpipかeasy_installを使ってインストールします。

pip install javasphinx

easy_install -U javasphinx

javasphinx拡張を有効にする

拡張を使うためには、conf.pyのextensionsにこの拡張を追加する必要があります。

初期状態の場合、conf.pyに空の配列が設定されたextensionsがあるので以下のようにします。

extensions = ['javasphinx']

パッケージとJavadocページとのマッピング設定

この拡張機能は、パッケージ単位にJavadocのURLを設定します。
この設定は、conf.pyに対して行います。

例えば、spring(org.springframework)のJavadocページヘのマッピングは、以下のようになります。

javadoc_url_map = {
  'org.springframework' : ('http://docs.spring.io/spring-framework/docs/current/javadoc-api/', 'javadoc'),
}

リンクを書いてみる

SpringのRestTemplateへのリンクの場合、以下のように書きます。

:java:extdoc:`org.springframework.web.client.RestTemplate`

生成されたSphinxのドキュメントは、以下のようになります。
f:id:sioiri:20160403000039p:plain

リンクテキストを変えたい場合は、以下のように「リンクテキスト <クラスの完全修飾名>」とします。

:java:extdoc:`RestTemplate <org.springframework.web.client.RestTemplate>`

f:id:sioiri:20160403000219p:plain

メソッドへのリンクは、完全修飾名の後にドット(.)+メソッド名で書きます。
メソッドの引数も正確に書く必要があります。

* :java:extdoc:`String#toString <java.lang.String.toString()>`
* :java:extdoc:`String#replace <java.lang.String.replace(java.lang.CharSequence,%20java.lang.CharSequence)>`

生成されたSphinxのドキュメントは、以下のようになります。
f:id:sioiri:20160403001216p:plain

おわり。

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の場合