Payara Microの実行可能JAR(Uber JAR)がとても簡単な件
先日、Payara 4.1.1.162がリリースされました!
その組み込みサーバー版であるPayara Micro 4.1.1.162には、「Uber JAR」というSpring BootやWildFly Swarmのような単体で実行可能なJARを作る機能が追加されました。
Payara 4.1.1.162 がリリースされました - GlassFish Japan
従来のPayara Microでは、一旦アプリケーションを普通にWARにして、Payara Micro起動→WARをデプロイする必要がありました。
今回の新機能により、本当に単体で実行可能なJARを作れるように
手順は蓮沼さんのブログに書かれてある通りなのですが、自分でもやってみてあまりに簡単で感動したので、このブログにも書くことにしました。
手順
普通にWebアプリケーションを作る
本当に普通に作ってください。pom.xmlのpackageは「war」です。
組み込みじゃない据え置きのGlassFish/Payaraにデプロイするアプリケーションと全く同じ形で作ってください。
下記は、今月のJJUG CCC 2016 Springで紹介する予定の、MVC 1.0のサンプルアプリケーションです。
アプリケーション側は全く変えることなく、据え置きサーバー・実行可能JARの両方に対応できます。
jjug-action-based-mvc/jjug-mvc10 at master · MasatoshiTada/jjug-action-based-mvc · GitHub
そして、MavenでWARファイルにビルドします。
cd jug-mvc10 mvn clean package
jjug-mvc10/targetフォルダにjjug-mvc10-1.0-SNAPSHOT.warが作成されます。
実行可能JARを作成する
上記のWARファイルを元に、Payara Microで実行可能JARを作ります。
あらかじめ、Payara Micro 4.1.1.162のJARは下記からダウンロードし、適当なフォルダに保存してください。
Payara Server & Payara Micro - Downloads
そして、次のコマンドを実行します。
java -jar ~/Java/ap-server/payara-micro-4.1.1.162.jar\ --deploy target/jjug-mvc10-1.0-SNAPSHOT.war\ --outputUberJar target/jjug-mvc10.jar
--outputUberJar
というオプションがポイントです。
こうすると、targetフォルダにjjug-mvc10.jarというJARファイルが作成されます。これが実行可能JARです。
ちなみに、このオプションがない場合、これまでのPayara Microと同じ挙動になります。
-jar の後は先ほどダウンロードしたPayara MicroのJARファイルのパス、--deployの後はWARファイルのパスです。
ちなみに、jar tf target/jjug-mvc10.jar
とすると、実行可能JARの中身を確認することができます。
とても長いので全部は載せませんが、Payara Microの中身が解凍・再パッケージされていることが確認できます。
META-INF/services/javax.enterprise.inject.spi.CDIProvider META-INF/services/org.glassfish.tyrus.core.ComponentProvider META-INF/services/javax.management.remote.JMXConnectorProvider (中略) META-INF/ META-INF/MANIFEST.MF __cp_jdbc_ra.rar __dm_jdbc_ra.rar __ds_jdbc_ra.rar __xa_jdbc_ra.rar META-INF/configuration/ META-INF/hk2-locator/ META-INF/maven/ META-INF/maven/org.glassfish.main.concurrent/ META-INF/maven/org.glassfish.main.concurrent/concurrent-connector/ org/ org/glassfish/ org/glassfish/concurrent/ org/glassfish/concurrent/config/ META-INF/configuration/context-service-conf.xml META-INF/configuration/managed-executor-service-conf.xml META-INF/configuration/managed-scheduled-executor-service-conf.xml META-INF/configuration/managed-thread-factory-conf.xml META-INF/maven/org.glassfish.main.concurrent/concurrent-connector/pom.properties META-INF/maven/org.glassfish.main.concurrent/concurrent-connector/pom.xml org/glassfish/concurrent/config/ConcurrencyResource.class org/glassfish/concurrent/config/ContextService$ContextServiceConfigActivator.class org/glassfish/concurrent/config/ContextService$Duck.class org/glassfish/concurrent/config/ContextService.class org/glassfish/concurrent/config/ContextServiceInjector.class org/glassfish/concurrent/config/LocalStrings.properties (以下略)
一番最後の方に、自分で作ったWARファイルが見えます。
... META-INF/deploy/ META-INF/deploy/jjug-jersey-mvc-1.0-SNAPSHOT.war META-INF/deploy/payaramicro.properties
META-INF/deployに直接WARファイルが入ってるんですね!
実行する
java -jar target/jjug-mvc10.jar
これで実行できます。
後はブラウザで開いてださい。
http://localhost:8080/jjug-mvc10-1.0-SNAPSHOT/
ちなみに、僕はここまでの処理は1つのシェルスクリプトにまとめて、実行する時はそれを叩くだけにしています。
jjug-action-based-mvc/build-jar.sh at master · MasatoshiTada/jjug-action-based-mvc · GitHub
まとめ
WARに固めてから実行可能JARにする、というのが少し違和感があるかもしれませんが、アプリケーション側に何も変更なく実行可能JARが出来るというのは非常に面白いと感じました。
ぜひご自分でも試してみてください!
Jersey MVCでThymeleafを使おう!
この記事について
JJUG CCC 2016 Spring「ネクストStruts/Seasr2としてのJava EEアクションベースMVC入門」の補足記事です。
Jersey MVCで、JSPの代わりのビューとしてThymeleafを使う方法を解説します。
Jersey MVCの基本については、下記の記事を参照してください。
Java EE 7でもアクションベースMVC! -MVC 1.0への移行を睨んだJersey MVCの活用- #javaee - Java EE 事始め!
環境
- Payara Web ML 4.1.1.162
- Thymeleaf 2.1.4.RELEASE
ソースコード
jjug-action-based-mvc/jjug-jersey-mvc at master · MasatoshiTada/jjug-action-based-mvc · GitHub
TemplateProcessorクラスを作る
Jersey MVCで公式にサポートされているビューはJSP/Freemarker/Mustacheの3種類です。
それ以外のビューを使うには、Jersey MVCで提供されているTemplateProcessor
インタフェースを実装する必要があります。
Jersey MVCには、TemplateProcessor
を実装したAbstractTemplateProcessor
という抽象クラスがありますので、これを継承して作成します。
クラスの作成、リクエストとレスポンスの取得
@Provider public class ThymeleafTemplateProcessor extends AbstractTemplateProcessor<String> { @Inject private javax.inject.Provider<Ref<HttpServletRequest>> requestProviderRef; @Inject private javax.inject.Provider<Ref<HttpServletResponse>> responseProviderRef; // 後に続く
まず、AbstractTemplateProcessor
を継承し、クラスには@Provider
を付加します。
次に、フィールドインジェクションでHttpServletRequest
とHttpServletResponse
を取得するのですが、@Provider
が付加されたクラスはJAX-RSの仕様では、デフォルトでシングルトンと定められています。
なので、直接HttpServletRequest
とHttpServletResponse
をインジェクションすると、スレッドセーフでない可能性があると考えました。
僕はスレッドセーフか否かといったあたりはあまり詳しくないので、直接取得でも大丈夫な可能性もありますが・・・。
そこで、Jersey MVCのJSP用TemplateProcessor
である下記のクラスを参考に作りました。
jersey/JspTemplateProcessor.java at 2.x · jersey/jersey · GitHub
これで本当にスレッドセーフになっているのかは、僕には判断がつかないので、この点はご自分で判断してください。
TemplateEngineの作成
// 続き private TemplateEngine templateEngine; @Inject public ThymeleafTemplateProcessor(Configuration config, ServletContext servletContext) { super(config, servletContext, "html", "html"); TemplateResolver templateResolver = new ServletContextTemplateResolver(); templateResolver.setPrefix((String) config.getProperty(MvcFeature.TEMPLATE_BASE_PATH)); // setSuffix()は指定しない templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); } // 後に続く
フィールドにTemplateEngine
(Thymeleafのクラス)を宣言し、コンストラクタ内で初期化します。
ポイントは、setSuffix()
という、通常は拡張子を指定するメソッドを呼んでいないことです。
これは、MVC 1.0との互換性のために、コントローラーメソッドで拡張子を指定したいからです。
各メソッドのオーバーライド
// 続き @Override protected String resolve(String templatePath, Reader reader) throws Exception { return templatePath; } @Override public void writeTo(String templateReference, Viewable viewable, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream out) throws IOException { HttpServletRequest httpServletRequest = requestProviderRef.get().get(); HttpServletResponse httpServletResponse = responseProviderRef.get().get(); httpServletResponse.setCharacterEncoding("UTF-8"); WebContext webContext = new WebContext( httpServletRequest, httpServletResponse, super.getServletContext(), httpServletRequest.getLocale()); Object model = viewable.getModel(); if (model instanceof Map) { Map<String, Object> variables = (Map<String, Object>) model; webContext.setVariables(variables); } else { Map<String, Object> variables = new HashMap<>(); variables.put("model", model); webContext.setVariables(variables); } try (Writer writer = new OutputStreamWriter(out)) { templateEngine.process(viewable.getTemplateName(), webContext, writer); } } }
resolve()
は、引数のtemplatePath
をそのまま返せばOKです。
writeTo()
が、最終的にレスポンスを書き込むメソッドです。
webContext.setVariables()
は、ビューに渡す値をマップ形式で指定します。
コントローラーメソッドでViewable
に渡された値は、viewable.getModel()
で取得できます。
この値がMapだった場合は、それを直接webContext.setVariables()
に渡し、そうでない場合はJersey MVCデフォルトのmodel
という名前でMapにputしています。
こうすることで、ビューから値を参照するときに、${model.employee.id}
のようにmodel
を付けなくてもいいようにしています。
writeTo()
の最後では、templateEngine.process()
を呼んでいます。これが、Thymeleafのレスポンスを書き出しています。
自作Viewableクラスの作成
これはThymeleafを使う上では必須ではないのですが、MVC 1.0との差異をなるべく吸収するために作りました。
前述のブログの通り、Jersey MVCには「/」で始める絶対パスと「/」で始めない相対パスがあり、それぞれビューの保存フォルダが異なります。
ビューの保存フォルダがMVC 1.0とほぼ同じなのは絶対パスですが、MVC 1.0(というか現在のOzark)では「/」で始めるとコンテキストルートからのパスになってしまいます。
なので、「/」で始めない相対パスで指定しつつ、かつビューの保存フォルダが絶対パスになるように、Viewable
のサブクラスを自作しました。
public class ThymeleafViewable extends Viewable { public ThymeleafViewable(String templateName) throws IllegalArgumentException { super(templateName); } public ThymeleafViewable(String templateName, Map<String, Object> models) throws IllegalArgumentException { super(templateName, models); } @Override public Map<String, Object> getModel() { return (Map<String, Object>) super.getModel(); } @Override public String getTemplateName() { String templateName = super.getTemplateName(); if (templateName.startsWith("/") == false) { return "/" + templateName; } else { return templateName; } } @Override public boolean isTemplateNameAbsolute() { return true; } }
コードを読んでいただければ大体わかると思いますが、getTemplateName()
が必ずスラッシュで始まるパス(=絶対パス)を返すようにし、かつisTemplateNameAbsolute()
でtrue
を返して、Jersey MVCに絶対パスと認識させます。
また、コンストラクタはビューのパスとMapを引数で受け取るようにします。
コントローラーメソッド
@GET @Path("index") public ThymeleafViewable index() throws Exception { return new ThymeleafViewable("employee/index.html"); }
@GET @Path("result") public ThymeleafViewable result(@BeanParam EmployeeIdForm form) throws Exception { Integer id = Integer.valueOf(form.getId()); Employee employee = employeeService.findByEmpId(id).orElse(null); HashMap<String, Object> models = new HashMap<>(); models.put("employee", employee); return new ThymeleafViewable("employee/result.html", models); }
パスは「/」で始めない、MVC 1.0と同じ形式にしています。
また、拡張子「.html」を明示的に指定しています。これもMVC 1.0と同じです。
ビュー
<p th:if="${employee == null}">該当する社員はいませんでした。</p> <table border="1" th:unless="${employee == null}"> <tr><th>社員ID</th><th>氏名</th><th>入社年月日</th><th>部署ID</th><th>部署名</th></tr> <tr th:object="${employee}"> <td th:text="*{empId}">99999</td> <td th:text="*{name}">Taro Yamada</td> <td th:text="*{joinedDate}">2020-01-01</td> <td th:text="*{department.deptId}">99</td> <td th:text="*{department.name}">Admin</td> </tr> </table>
model
を指定せず、直接employee
で値を参照していることが分かります。
これにより、Jersey MVCからMVC 1.0に変更した際に、ビューを全く変更しなくて済むようにしています。
まとめ
TemplateProcessor
と自作Viewable
により、極力MVC 1.0と同じ感覚で、ビューとコントローラーを実装できるようにしました。
参考にしたWebサイト
@bufferingsさんのブログ。
jersey-thymeleaf using ViewProcessor - Mitsuyuki.Shiiba
@backpaper0さんのコード。
sealion/ThymeleafProvider.java at master · backpaper0/sealion · GitHub
Jersey MVC + Thymeleafしたら文字化けた時の対策
環境
- OS X 10.11.4
- Google Chrome
- Payara Web ML 4.1.1.161.1 (Jersey 2.22.1)
- Thymeleaf 2.1.4.RELEASE
プログラム概要
@bufferingさんの記事を参考に、Thymeleaf用のTemplateProcessorを作成。
@Provider public class ThymeleafTemplateProcessor extends AbstractTemplateProcessor<String> { @Inject private javax.inject.Provider<Ref<HttpServletRequest>> requestProviderRef; @Inject private javax.inject.Provider<Ref<HttpServletResponse>> responseProviderRef; private TemplateEngine templateEngine; @Inject public ThymeleafTemplateProcessor(Configuration config, ServletContext servletContext) { super(config, servletContext, "html", "html"); TemplateResolver templateResolver = new ServletContextTemplateResolver(); templateResolver.setPrefix((String)config.getProperty(MvcFeature.TEMPLATE_BASE_PATH)); templateResolver.setSuffix(".html"); templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); } @Override protected String resolve(String templatePath, Reader reader) throws Exception { return templatePath; } @Override public void writeTo(String templateReference, Viewable viewable, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream out) throws IOException { HttpServletRequest httpServletRequest = requestProviderRef.get().get(); HttpServletResponse httpServletResponse = responseProviderRef.get().get(); WebContext webContext = new WebContext( httpServletRequest, httpServletResponse, super.getServletContext(), httpServletRequest.getLocale()); Object model = viewable.getModel(); if (model instanceof Map) { Map<String, Object> map = (Map) model; webContext.setVariables(map); } else { Map<String, Object> variables = new HashMap<>(); variables.put("model", viewable.getModel()); webContext.setVariables(variables); } templateEngine.process(viewable.getTemplateName(), webContext, httpServletResponse.getWriter()); } }
リソースクラス。
@Path("employee") @RequestScoped @Produces(MediaType.TEXT_HTML) public class EmployeeResource { @GET @Path("index") public Viewable index() throws Exception { return new Viewable("/employee/index"); } }
Thymeleafのビュー。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>入力画面</title> <link rel="stylesheet" href="../../css/style.css"/> </head> <body> <p>社員IDを入力してください(77, 88, 99で例外発生)</p> <!-- 検証エラーメッセージの表示 --> <ul class="error" th:if="${violations != null}"> <li th:each="violation : ${violations}" th:text="${violation.message}">ダミーのメッセージ</li> </ul> <form action="./result" method="get"> 社員ID:<input type="text" name="id" value="" th:value="${param.id == null} ? '' : ${param.id[0]}"/> <input type="submit" value="検索"/> </form> </body> </html>
現象
画面の全日本語メッセージが文字化けている。
原因と対策
レスポンスヘッダーやブラウザが現在開いている文字コードを見ると、正しくUTF-8になっていた。
よって、そもそもレスポンスを書き込む際に文字コードが正しく設定されていないっぽい。
TemplateProcessorにhttpServletResponse.setCharacterEncoding("UTF-8");
を追加したら、正しく表示された。
@Provider public class ThymeleafTemplateProcessor extends AbstractTemplateProcessor<String> { // 中略 @Override public void writeTo(String templateReference, Viewable viewable, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream out) throws IOException { HttpServletRequest httpServletRequest = requestProviderRef.get().get(); HttpServletResponse httpServletResponse = responseProviderRef.get().get(); // これを追加 httpServletResponse.setCharacterEncoding("UTF-8"); WebContext webContext = new WebContext( httpServletRequest, httpServletResponse, super.getServletContext(), httpServletRequest.getLocale()); Object model = viewable.getModel(); if (model instanceof Map) { Map<String, Object> map = (Map) model; webContext.setVariables(map); } else { Map<String, Object> variables = new HashMap<>(); variables.put("model", viewable.getModel()); webContext.setVariables(variables); } templateEngine.process(viewable.getTemplateName(), webContext, httpServletResponse.getWriter()); } }
ちなみに
Ozark + Thymeleafだと、この記述が無くても正しく表示された。
(ViewEngineクラスにもそれらしい記述は無い)
理由は不明。。。
Cloud Foundryワークショップに参加してきました! #cfws
2016-04-05(火)に開催された、Pivotalさん主催のCloud Foundryワークショップに参加してきました!
クラウド関連のことはあまり知識がないので、クラウドの世界ってどんなものなんだろう?ということ知りたかったというのが動機です。
講師は@makingさん。冒頭は、3月のJJUGナイトセミナーの資料でCloud Foundryの概要説明でした。
理解できた範囲で要約すると、
- IaaS上にPaaSを構築・管理するためのソフトウェア群
- Pivotal社単独ではなく、「Cloud Foundry Foundation」という団体が仕様を策定している
- Pivotal Web Servicesは、AWS上にCloud FoundryでPaaSを構築済みのもの。アカウントを作成すればすぐに利用できる
- PCF Devは、ローカルPC上にCloud Foundry環境を構築するもの
- Cloud Foundry上へのアプリケーションのデプロイなどは、CLIツールをインストールして
cf
コマンドで行う
その他、Cloud Foundry内部の仕組みや、Blue-Green Deployなどの説明もありました。 (なんか理解が間違ってたらツッコミください・・・)
その後、下記の資料でハンズオン。
GitHub - Pivotal-Japan/cf-workshop: Cloud Foundry Workshop
Cloud Foundryワークショップ資料 - Qiita
(上記のGitHubとQiitaは、両方同じ資料です)
特に印象深かったのは、「5. スケールアウト」と「7. Blue-Greenデプロイ」です。
例えば、より多くのクライアントに対応するためにインスタンス数を増やして負荷分散したいという場合は、ローカルPCから下記のコマンドを叩くだけ。
$ cf scale -i 4 hello-redis-tada
上記だとインスタンス数が4になります。かかった時間も、ものの数秒でした。で、ロードバランシングとかはCloud Foundry内のRouterがよろしくやってくれる、と。
Blue-Greenデプロイは、名前だけは聞いたことがあったものの、どんなものかはよく知りませんでした。
新しいバージョンのアプリケーションをデプロイする際に、ダウン時間をなるべくゼロに近づけるための手法だと理解しました。
これも、Cloud Foundryを使えば非常に簡単ですね。
「クラウドって実際に触ったことがない」という人にはとてもオススメのワークショップです!
これから何回か開催予定ということなので、是非参加してみてはいかがでしょうか。
ハンズオンはボリュームがそこそこありますので、当日は余裕を持って話を聞きたいという方は「3. 簡単なアプリケーションをデプロイ」くらいまでは事前に予習しておくといいかもしれません。
TOEIC受験記(2016年3月)
なぜ受験しようと思ったか
僕の仕事は研修講師(主にプログラミング)なので、色々と調べ物をする時にどうしても英語の仕様書・書籍・ブログ記事などを読む必要が出てきます。やはり、ITの本場はアメリカです。
なので、もっとリーディング力を高めたいと思ったのがきっかけです。
勉強前の英語レベルは?
中学までは英語は得意科目でしたが、高校になるとマークシート形式ならなんとか、という感じでした(センター試験だと8〜9割くらい)。でも、記述式はまったくダメでした。
10年くらい前、まだ大学生の頃、試しにTOEICを受けたことがありましたが、たしか440〜450点だったと思います。
結果は?
Listening | Reading | Total |
---|---|---|
395 | 430 | 825 |
勉強法は?
僕の勉強法はいたってシンプルで、市販書籍でインプットとアウトプットをひたすら繰り返すのみです。
勉強時間は、主に通勤時間と土日です。家族に感謝。
改訂版 TOEIC(R) TEST 英文法 出るとこだけ!
Amazon.co.jp | 音声DL付 改訂版 TOEIC(R) TEST 英文法 出るとこだけ! | 本 ・TOEIC 通販
まずはじめに読んだ本です。文法のインプット・アウトプットに使いました。ページは少ないけど文法を理解するコツがしっかりと解説されていて、記憶力も根気も無い自分にはピッタリの本でした。あえて受験テクニック的な見出しが付いていますが、文法的な背景も解説されています。
僕は暗記物がとても苦手なので、いきなり単語集をやると確実に挫折します。というか、実際に挫折しました・・・。なので、文法で理論的な部分をある程度身につけてから、単語に取り掛かりました。その方がモチベーションも続きます。
この本は3回くらい繰り返しました。
改訂版 TOEIC(R)TEST 英単語 出るとこだけ!
Amazon.co.jp | CD-ROM付 改訂版 TOEIC(R)TEST 英単語 出るとこだけ! | 本 ・TOEIC 通販
文法の勉強をしたはいいが、やはり単語が分からないことにはリーディングができないことに気付き、文法の本と同じ小石裕子さんの本を読みました。
TOEICは出る単語が(文法もですが)決まっている感じがするので、TOEICを受けるならTOEIC専用の単語集を使ったほうがいいと思います。
この本は、5回くらい繰り返しました。
TOEICテスト公式プラクティス リーディング編
Amazon.co.jp | TOEICテスト公式プラクティス リーディング編 | 本 ・TOEIC 通販
TOEICを実施しているETSが出している、公式の練習問題集です。これは2回くらい繰り返しました。
改訂版 TOEIC(R) TEST リスニング 出るとこだけ!
Amazon.co.jp | CD付 改訂版 TOEIC(R) TEST リスニング 出るとこだけ! | 本 ・TOEIC 通販
今回はリーディングに絞って勉強するつもりだったのですが、時間に少し余裕が出来たので、直前の2週間だけリスニングの勉強をしました。
読んだのは、やはり小石裕子さんの本です。これは1回通りやるのが精一杯でした。
TOEICテスト新公式問題集< Vol.6>
Amazon.co.jp | TOEICテスト新公式問題集< Vol.6> | 本 ・TOEIC 通販
本番の試験と同じ形式の模試が、2回分入っています。1回目は普通に問題集として解き、2回目を実際の時間(2時間)を測って解きました。
下記サイトによると、Vol.6が最も本番の難易度に近いそうです。 僕はこのVol.6しか解いていませんが、確かに本番の試験と難易度は似ていると感じました。
勉強してどうだったか
英語の文書を読むのが少し楽になったように感じます。
これは、勉強したことにより地力が鍛えられたこともありますが、スコアを取ったことによる自信も大きいと思います。
勉強する→結果が出る→自信がつく→モチベーションが湧く→勉強する→・・・
このサイクルを回し続けることが肝心です。
今後、また受験するかは未定ですが、英語文書をより読んでいって更に英語力を鍛えていこうと考えています。
使ってないけどやっておいたほうが良かったかなと思う本
TOEICテスト公式プラクティス リスニング編
Amazon.co.jp | TOEICテスト公式プラクティス リスニング編 | 本 ・TOEIC 通販
公式プラクティスのリスニング版です。今回は時間の都合上やりませんでした。
TOEICテスト 公式問題で学ぶボキャブラリー
Amazon.co.jp | TOEICテスト 公式問題で学ぶボキャブラリー | 本 ・TOEIC 通販
これも時間の都合上やりませんでした。
TOEICテスト新公式問題集〈Vol.4〉
Amazon.co.jp | TOEICテスト新公式問題集〈Vol.4〉 | 本 ・TOEIC 通販
前述のサイトによると、公式問題集の中でも一番難しいものだそうです。ハイスコアを目指す人はやっておくといいかもしれません。
改訂版 TOEIC(R) TEST 文法・語彙出るとこだけ! 問題集
Amazon.co.jp | 音声DL付 改訂版 TOEIC(R) TEST 文法・語彙出るとこだけ! 問題集 | 本 ・TOEIC 通販
これも時間の都合上やりませんでした。
新TOEICテスト中学英文法で600点!
Amazon.co.jp | 新TOEICテスト中学英文法で600点! | 本 ・TOEIC 通販
中学英語に自信が無い人は、これから始めるといいかもしれません。
Jersey MVCのレスポンスのContent-Typeが「*/*」になる問題対策
詳細は後ほど追記します。
ContianerResponseFilterだと何故かできなかった・・・。
Jersey MVCは、MessageBodyWriterの中でContent-Typeを上書きしています。
ContianerResponseFilterはMessageBodyWriterよりも前に実行されるので、そりゃあ効かないですね・・・。
なので、必ずWriterInterceptorの「後処理」として書く必要があります。
MessageBodyWriterでなぜ「*/*」に上書きされるのかは、現在ソースを読んで調査中です。
package com.example.rest.interceptor; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.WriterInterceptor; import javax.ws.rs.ext.WriterInterceptorContext; import java.io.IOException; @Provider public class ContentTypeWriterInterceptor implements WriterInterceptor { @Override public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { // デバッグログ System.out.println("======== BEFORE : " + context.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); // レスポンスへの書き込み実行(必須) context.proceed(); // デバッグログ System.out.println("======== AFTER : " + context.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); // レスポンスヘッダーに "Content-Type: text/html"を上書きする MultivaluedMap<String, Object> headers = context.getHeaders(); headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML); } }
Payara 161.1 で @Transactional のバグが修正されました!
この記事は、半分ポエムです。
最初のきっかけ
下記の@opengl_8080さんの記事でした。
CDIビーンのメソッドに@Transactionalアノテーションを付加しても、非チェック例外発生時にロールバックされない、というバグがあるのこと。
(JTAの仕様としては、チェック例外ならばコミット、非チェック例外ならばロールバックされます)
早速、自分でも試してみたのですが、やっぱりロールバックされませんでした。
はじめてのISSUE報告(2015/11/1)
僕は、トランザクション管理はJava EEの要だと思っていますので、PayaraのGitHubにISSUEを書きました。
英語あんまり得意じゃないですが・・・(^^;
テスト用プログラム(上記の@opengl_8080さんの記事を参考に作りました)
GitHub - MasatoshiTada/TransactionalSample-Doma
ISSUE
いま読み返しても、ひどい英語だ・・・。でも、もうこれは気合いでした。
Payaraの中の人から反応が!(2015-11-15)
Payaraの中の人たちは、すぐに反応してくれました。
テスト用プログラムのおかげで再現性がすぐに認められ、バグ修正も速やかに行われました!
最初にバグを見つけたのは@opengl_8080さんですし、僕はISSUEを書いただけです。
しかし、僕は現役のエンジニアではなく、僕が作ったプログラムが世の中で動くわけではないので、
本当に、ほんのちょっとでもPayaraに貢献できたことが、本当に嬉しかったんです。
修正されたPayaraがリリース!しかし・・・(2016-01)
上記の修正は、Payara 4.1.1.161に含まれました。
ウキウキして早速試してみました。
すると・・・
直ってないやん(--;)
TxType
がREQUIRED
の挙動は直っていたんですが、REQUIRES_NEW
の挙動は直っていませんでした。
再びISSUE報告(2016-02)
数日後、改めてISSUEを書きました。
#505 is not fixed when TxType is REQUIRES_NEW · Issue #667 · payara/Payara · GitHub
すると、やはりすぐに反応が返ってきました。
前回のことを覚えてくれていたのか、すぐに修正も行われました。
ちなみに、2回とも修正対応してくれたsmillidgeさん、Payaraを開発している英国C2B2社の創業者の方だそうです。
もし今年のJava Day Tokyoとかにいらっしゃったりすれば、是非お話してみたいなあ・・・。
リリースが思わぬ速さで!(2016-03)
Payaraは通常、四半期に1回のリリースなんですが、たまに四半期の間でもパッチ版がリリースされます。
僕は、「修正版が出るのは次の四半期かなー」と思っていたんですが、つい先日、パッチ版のPayara 4.1.1.161.1がリリースされました!
(「1」が多いので、以降は「Payara 161.1」と書きます)
このリリースに、上記バグの修正が含まれています。
このバグ修正の何が嬉しいのか?
JPA以外のORマッパーをPayara上で使いつつ、かつ@Transactionalでトランザクション管理を行うことができます。
(上記バグは、以前からJPAでは発生しませんでした)
最近、「100%Java EE標準」にこだわらないのであれば、JPA以外のORマッパー(Doma・MyBatis・jOOQ・DBFluteなど)を使う選択肢もアリだと思っています。
このような構成でも、安心してPayaraを使うことができます。
最後に
このバグを見つけてくれた@opengl_8080さんには、本当に感謝です。
繰り返しますが、僕はISSUEを書いただけです。
でも、将来的には、自分でバグ修正までしてプルリクエストを送れるくらいになりたいなあ・・・。