JavaでWebサーバー作って何が辛かった?

  • 7
    Like
  • 0
    Comment

イントロ

この記事はOIC ITCreate Club Advent Calendar 2016 9日目の記事です。

http://www.adventar.org/calendars/1484

お疲れ様です。がっちゃんこと古賀です。
どこぞの羽生えた赤ちゃんみたいにクピポー!って言ったり家食べちゃったりはしないです。
平仮名でがっちゃんです。

はい、行数稼ぎの自己紹介もこれぐらいにして本題に入って行きましょう。(2回目)

最近やってること

最近は Webサーバの処理を書くといえば、いろんな有名なフレームワークとかいろんな言語でやるじゃ無いですか。んで、いろいろ流行ってるじゃないですか。
例えばこんなのとか。

  • Ruby on Rails
  • Laravel
  • CakePHP
  • Node.js(Express, Meteor)
  • AngularJS
  • Spring

とかとか…

いっぱいあるし、それぞれ良い所・悪いところあるじゃないですか。
で、フレームワークに則った書き方しかできないって良くないかなって思ったんですね。

なので Java EE をフレームワーク(大規模なもの)を使わずにWebサーバ実装 を(チームメンバーを道連れにして)やることにしました。

予定の構成

実装する機能

基本的なWebサービスの機能

  • DB 利用
  • API 利用
  • ページルーティング
  • セッションを使ったログイン認証

使用する技術

  • DB => JPA
  • API => JAX-RS(Jersey)
  • ルーティング => Servlet
  • セッションを使ったログイン認証 => Servlet

ライブラリ等 環境

  • Jettyサーバ (@WebServlet が使いたかったので、Ver 9以降)
  • JPA (EclipseLink)
  • JAX-RS(Jersey)
  • Servlet 3.0
  • Gson
  • lombok
  • Gradle(ビルドツール)

辛かったこと・他にハマる人が出てきてほしくないこと

ビルドツール関係で辛かった

ビルドツールって?

Java って書いたソースコードを実行形式にコンパイル(対象の実行環境で動く形に変換すること)して動かす言語なんですね。
ソースコードの中で利用したい外部ライブラリがあった場合が大変で、外部ライブラリのファイルをダウンロードしてきた後、ライブラリや全体の設定ファイルなども含めてまとめてコンパイルする必要があるんですよ。

その作業(ライブラリを引っ張ってくるところから全て)を設定ファイルをセコセコ書くだけで自動でやってくれる&いい感じにしてくれるツールがビルドツールです。

代表的なものには Ant, Maven, Gradle などなどがあります。

やってたこと

Gradle を使って依存ライブラリ関係を何とかするために、 build.gradle をセコセコと書き続けていくわけです。

で、JettyというWebサーバ 兼 サーブレットコンテナを組み込み(Javaコード内で設定を書かずにGradleなどのビルドツールに設定を書くことで利用できる方法)で利用したく、プラグインを読み込んでbuild.gradle に組み込みJettyの設定を書いていました。

build.gradle
apply plugin: 'jetty'

jettyRun {
    contextPath = "" // ルートパス(例: http://localhost:8080/)
    httpPort = 8080 // ポート番号
    reload = "automatic" // ホットデプロイ
    scanIntervalSeconds = 1 // ホットデプロイの感覚
}

これで
$ ./gradlew jettyRun
で起動できるようになりました。

問題発生

上記のコマンドでとりあえずサーバは起動するようになりました。

でも、Git, GitHubを使って複数人開発したく、全員で web.xml ファイルをいじくるのはしたくないと思っていたので、 Servlet 3.0 から追加された機能(@WebServletアノテーション)で書くつもりでした。

しかし、@WebServlet実装したURLにアクセスしても404が返ってくる
でも、web.xml でURLマッピングを行ったものにはアクセスできる。

「…なぜ?」

いろいろ調べまわったりした結果、

Screenshot from 2016-12-08 10:52:33.png

ということに気づきました。
「組み込みJettyのServletコンテナが Servlet3.0 に対応していないのではないか」と。

その方向でいろんな技術ページをさまよったり、やったこともないのに Stack OverFlow に拙い英語で質問書いたりして。

なんだかんだと二週間ぐらい調べ倒した結果…

Screenshot from 2016-12-08 03:05:00.png

Qiitaのこの方の記事( http://qiita.com/nisin/items/782d6fe1ef7e94c456a4 )に 2行ぐらいでちょこっと書いてました…!!!
組み込みJettyに絶対的な信頼を置いていた弊害…!
そして「組み込みjetty servlet 3.0」とかで検索していたので、ここに流れ着くのにめちゃめちゃ時間がかかった…

よって、Gradle で 組み込みJetty9 (Servlet3.x 対応バージョン)を利用するために、
Gretty( https://github.com/akhikhl/gretty )を使っていくことに

build.gradle
apply plugin: 'war'
apply plugin: 'org.akhikhl.gretty' // jetty プラグインではなくこちらを利用

repositories {
   mavenCentral()
   jcenter() // gretty が存在する場所の宣言
}

gretty {
    servletContainer = 'jetty9' // ここでバージョン指定
    contextPath = ""
    httpPort = 8080 //使用ポート番号
    fastReload = true
    scanInterval = 4
}

dependencies {
    // 諸々のライブラリ
}

buildscript { // gretty のために必要なビルドスクリプト
    repositories { jcenter() }
    dependencies {
        classpath 'org.akhikhl.gretty:gretty:+'
    }
}

これで同じように
$ ./gradlew jettyRun
Jetty9 の組み込み版 が実行されるようになりました!
感謝!拍手!

とにかくこれはドハマりしたので伝えておきたいことです…

DBアクセス関係で辛かった(現在進行形でも辛い)

JPAって?

Javaでデータの永続化を行うため(DBとの接続をラップする)ライブラリ

辛いこと(解決法がいまいちわかってない辛いこと)

persistence.xml を使ってJPAの設定(どのDBアクセスするのか、どのユーザでアクセスするのか。など)を記述するのですが、JPAで自動的にMySQLの中にテーブルを作ってくれない…?

なぜ…?

persistence.xml
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
    <persistence-unit name="[unitName]" transaction-type="RESOURCE_LOCAL">
        <class>jp.example.database.EmployeeMasterDb</class>
        <!-- 他にもいくつかDBエンティティを作成-->
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url"    value="jdbc:mysql://localhost/[tableName]" />
            <property name="javax.persistence.jdbc.user" value="[userName]" />
            <property name="javax.persistence.jdbc.password" value="[userPassWord]" />
            <property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
            <property name="eclipselink.ddl-generation.output-mode" value="database" />
        </properties>
    </persistence-unit>
</persistence>

テーブルが存在しない場合、自動的に作ってくれる(?)ようなことを読んだ記事の中で見たので、期待していたのですが、何故かうまく行かない…。
現在は先にスキーマを作成してからそこにデータをJPAを使って登録する方法を取っています。

解決したい…

他にも、JPAの公式ドキュメントがない…(調べきれていないからかも)?ため、調べるのに苦労するところとかとか、だいぶとツライです…w

まとめ

組み込みJettyの問題に関してはひたすら解決方法を探して解決したので、方法を記事に出来ましたが、JPAに関してはまだ解決方法が見つかっておらず、そちらを期待して読まれた方はすみません。調べてまたいつか記事にします…

こんな感じで Gradle や JAX-RS を使って Java でWebサーバを書いたりしていますが、まだまだ勉強不足 & フレームワークって偉大…って最近常々感じてます。
もっともっと勉強してフレームワークとか環境にとらわれずに何でも作れるようになりたいと思ってるので、頑張ります…!

GitHub とか Twitter とかいろいろやってるので、よろしくお願いします!
GitHub: https://github.com/ahaha0807
Twitter: https://twitter.com/ahaha0807_alg

最後まで読んでくださり、ありがとうございました!