Heroku上でJersey + Jetty + Gradleを動かす

はじめに


JavaでRestfulといえば、JAX-RSが有名ですね。

それを扱いやすくしたFrameworkといえば、Jerseyになります。
これをHerokuで動かすようにしてみます。

ついでにオワコン臭漂うmavenを辞めて、Gradleでビルドができるようにもしてみます

目標


以下ができていること

  • Heroku上でJerseyが動くこと
  • HerokuではJettyがサーブレット・コンテナとして動くこと
  • ビルドツールには、デフォルトのmavenではなくGradleであること

Jersey


まずはmaven ArchTypeから必要なファイルを準備

ここを参考にすると、ArchTypeは2種類あることが分かります。

今回はJettyで動かすので、サーブレットコンテナ用のものを使います

1
mvn archetype:generate -DarchetypeGroupId=org.glassfish.jersey.archetypes -DarchetypeArtifactId=jersey-quickstart-webapp -DarchetypeVersion=2.17

必要項目を聞かれますので、以下のような感じで入力していきます

1
2
3
4
Define value for property 'groupId': : kurobara
Define value for property 'artifactId': : kurobara
Define value for property 'version': 1.0-SNAPSHOT:
Define value for property 'package': kurobara: kurobara

成功すれば、以下の構成になります

1
2
3
4
5
6
7
8
9
10
11
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── kurobara
│       │       └── MyResource.java
│       ├── resources
│       └── webapp
│           ├── WEB-INF
│           │   └── web.xml
│           └── index.jsp

mavenの削除


普通に考えると、mavenを使わなければpom.xmlを残してもよいかと思います(自分も最初は思いました) Herokuではmavenがデフォルトで使われますので、削除してしまいます

1
rm pom.xml

どうやら、mavenとGradleの両方のビルドファイルが存在するとHerokuではmavenのものが最優先で使われるようです

Gradleの追加


以下の内容をbuild.gradleに記載します

このファイルのミソはtask stage(dependsOn: ['clean', 'installApp'])を記載していることです。 Herokuではこのタスクが必要なようです。

公式を参考にしました。

多分、必要無いと思いますが念のためgradlewも用意しました(自分は一応、これもファイルに追加しています)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apply plugin: "java"
apply plugin: "war"
apply plugin: "jetty"
apply plugin: 'application'

group = "kurobara"
version = 1.0
sourceCompatibility = 1.7
mainClassName = "kurobara"
applicationName = "kurobara"

def defaultEncoding = 'UTF-8'
[
  compileJava,
  compileTestJava,
  javadoc
]*.options*.encoding = defaultEncoding

repositories {
  mavenLocal()
  maven { url "http://maven.seasar.org/maven2" }
  mavenCentral()
}

dependencies {
  compile 'org.eclipse.jetty:jetty-server:9.2.10.v20150310'
  compile 'org.eclipse.jetty:jetty-webapp:9.2.10.v20150310'
  compile 'org.glassfish.jersey.core:jersey-server:2.17'
  compile 'org.glassfish.jersey.containers:jersey-container-servlet-core:2.17'

  testCompile 'junit:junit:4.10'
}

task stage(dependsOn: ['clean', 'installApp'])

[jettyRun, jettyRunWar]*.httpPort = 8090
[jettyRun, jettyRunWar]*.contextPath = 'kurobara'


task wrapper(type: Wrapper) {
  gradleVersion = '1.6'
}

Jettyの準備


最低限度の内容でJettyサーバが動くようにします

mainメソッドにサーバの実装を記載します。

Gradleに記載したように、これがmainClassになります(mainClassNameを参照)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class Main {

  public static void main(String[] args) throws Exception {
      Server server = new Server(Integer.valueOf(System.getenv("PORT")));

      WebAppContext context = new WebAppContext();
      context.setServer(server);
      context.setContextPath("/");
      context.setResourceBase("src/main/webapp");
      context.setClassLoader(Main.class.getClassLoader());
      server.setHandler(context);

      server.start();
      server.join();
  }
}

以下の構成になるようにMainクラスを配置します

1
2
3
4
5
6
7
8
9
10
11
12
├── build.gradle
├── src
│   └── main
│       ├── java
│       │   └── kurobara
│       │       ├── Main.java <- これを追加
│       │       └── MyResource.java
│       ├── resources
│       └── webapp
│           ├── WEB-INF
│           │   └── web.xml
│           └── index.jsp

動作確認


まずは、テストが動くかどうか確認

1
2
$gradle clean
$gradle test

次にアプリをビルドし、サーバを起動

1
2
3
$gradle clean
$gradle build
$gradle jettyRunWar

以下のコマンドでGot it!が返却されるか確認

1
curl -v http://localhost:8090/kurobara/webapi/myresource

Heroku環境用の準備


Herokuで動かすJavaのバージョンを指定するsystem.propertiesを作成
(以下のようにとりあえず確実に動作するバージョンを指定)

1
java.runtime.version=1.7

Procfileに記載するスクリプトファイルの確認

1
$gradle stage

こういう形にビルド結果が出力されているので、アプリケーション起動スクリプトのファイルパスを確認する

1
2
3
4
5
6
7
8
9
10
11
12
├── build
│   ├── classes
│   │   └── main
│   │       └── kurobara
│   │           ├── Main.class
│   │           └── MyResource.class
│   ├── dependency-cache
│   ├── install
│   │   └── kurobara
│   │       ├── bin
│   │       │   ├── kurobara <- これが起動用ファイル
│   │       │   └── kurobara.bat

生成したスクリプトファイルパスをProcfileに記載

1
web: ./build/install/kurobara/bin/kurobara

不要ファイルを無視するように.gitignoreを作成
(Herokuへのデプロイはgitのため)

1
2
build/
.gradle/

最終的に以下の構成になります

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── .gitignore
├── Procfile
├── build.gradle
├── src
│   └── main
│       ├── java
│       │   └── moonstruckdrops
│       │       ├── Main.java
│       │       └── MyResource.java
│       ├── resources
│       └── webapp
│           ├── WEB-INF
│           │   └── web.xml
│           └── index.jsp
└── system.properties

Herokuへdeploy


herokuへloginする

1
2
$ brew install heroku
$ heroku login

サーバへデプロイする

1
2
3
4
5
$ git init
$ git add .
$ git commit -m "Ready to deploy"
$ git remote add heroku your_heroku_path
$ git push heroku master

以下のコマンドでGot it!が返却されるか動作確認

1
$curl -v http://your_app.herokuapp.com/webapi/myresource

感想


書くと簡単でしたが、実際にやってみると思った以上に嵌まりどころが多かった

特に以下が盛大な罠でした

  • Procfileに書くファイルはどれなのか
  • heroku logsを確認するとapp crashと表示されて何がなんだかわからんかったこと
  • app crash起因ですがweb.xmlはいじらなくてもいいのか?なんて思ったこと

一度環境さえ準備できれば開発に専念できるようになるので、非常に楽ですね

なぜApacheとTomcatを連携させるのか

はじめに


同僚とApacheとTomcatって何で連携させるんだっけ?
という話が出たので備忘録的に記載しておきます

ぶっちゃけ、今更感がすごい内容ですが・・・

Apache


ざっくり言うと、クライアントのブラウザからアクセスし、サービスを提供するためのWebサーバソフトウェア

Tomcat


こちらもざっくり言うと、以下のもの

  • 「サーブレット・コンテナ」(Servlet等を動作させるために必要なWebアプリケーションサーバ)
  • Servletのインスタンス管理やセッションの管理
  • Webサーバ機能有り(主に開発とデバッグ用)

連携理由


開発、デバッグ用途としてWebサーバ機能があるので、問題はないのですが そもそもとして、ApacheなどのWebサーバの性能と同等に捌けるかは疑問があったりするところ

なので、以下の処理分担としたほうが効率よく捌けるため連携をさせることが多い

  • 静的コンテンツ(HTMLや画像ファイルなど)の処理はApacheが担当(何故なら、高速でTomcatに比べて高速に処理できるから)
  • 動的コンテンツに関しての処理をTomcatが担当

所謂、餅は餅屋ということですな

APIを作る際のAPIバージョンに関するメモ

はじめに


APIを作るときにどうするか?を開発チーム内で議論したので、備忘録的なメモ

発端となった会話は、こんな感じだったはず

「マイクロアーキテクチャなサービスを作ったときってAPIを作ると思うけど、ライフサイクルって考えてる?」
「APIを利用するフロントは更新できるけど、APIは依存されるものが多いから簡単には更新できないよね?」
「APIってモバイルアプリからも利用した場合、修正しづらくない?バージョン管理でもするの?」

APIの種別


以下の3つぐらいになりそう

  • 外部公開向け
  • モバイルアプリ向け
  • webサービス向け

外部公開向け


  • API変更の影響が大きい
  • ドキュメント更新必須
  • 利用者が離れるのでドラスティックな変更はできない
  • 利用者への周知が必須

モバイルアプリ向け


  • アプリのみが影響を受けるので、API変更における影響は少ない
  • アプリバージョンによるので自由にAPI更新ができない
  • OSバージョンによってアプリのアップデートができないことが多いので、更新によって使えなくなる可能性有
  • 更新API対応のアプリであっても、アプリが更新されることは無いと思ったほうがよい

webサービス向け


  • クライアントもAPIに合わせて最新にできるので、変更しやすい
  • キャッシュされたクライアントのコードとのデータ不整合が起きやすい
  • キャッシュに振り回される(iOSのwebview等)

バージョニング方式


APIのバージョン管理方式は以下のものが採用される傾向にある

  1. アクセス先URIそのものを大きく変更する
  2. URIにバージョンを埋め込む
  3. クエリに使用APIバージョンを入れる
  4. メディアタイプにバージョンを指定する

世間的には、2のパターンが比較的多く採用される傾向

URIにバージョンを埋め込み方式


  • 「v1」のように「v」をつけてバージョンを明確にすることが多い
  • 日付やリリースバージョン(ハッシュ等)をバージョンにするAPIもある(twillo等)
  • URIに組み込むバージョンは、メジャー番号のみを含める
  • APIの修正は、後方互換を保ちつつ対応する

バージョニングは以下のセマンティックバージョニング方式を取ることが多い

  • パッチバージョンは、ソフトウェアのAPIに変更がないバグ修正を行ったときに増える
  • マイナーバージョンは後方互換がある機能変更、特定の機能追加のときに増える
  • メジャーバージョンは後方互換が無い変更の時に増える

メディアタイプにバージョンを指定する


  • 「Accept: application/vnd.example.v2+json」のような形で規定する
  • URIがリソースを表しているので、HTTPの文法に則っている
  • 「Content-Type」の指定誤りで、サーバ、クライアント側ともにエラーになりやすい傾向

バージョン変更指針


  • APIは基本変更しないほうがよい
  • 変更は後方互換を保ちつつ対応できる場合は、マイナーバージョンアップ(可能な限りこちらを選択)
  • 後方互換が保てない修正の場合はメジャーバージョンアップ
  • レスポンスデータ整合性/整理のためであれば、バージョンは上げない(後方互換を維持し続け、ドキュメント整理で対応)

メジャーバージョンアップ指針


後方互換が保てない場合のみ実施し、バージョンアップのルールを整理してから行う

  • セキュリティ/権限などのAPI使用ルール変更
  • 認証方式の変更
  • 乱雑に作った(ルールが無い)APIを使いやすくするための整備

APIの提供終了


  • API提供終了前に提供終了日時のアナウンスをする(提供終了後、半年ほどは動かしておくこと)
  • 提供終了までにAPIを利用できなくするブラックアウトテストを数回実施する
  • API提供終了の仕様をドキュメントに盛り込んでおく(HTTPのステータスコード410(Gone)を返すなど)
  • 利用期限をAPI提供開始時に決めておく
  • 古いAPIを叩いたときに、新APIへのリダイレクトは、混乱を招くので避けたほうがよい

# モバイル向けAPIの場合、ユーザー体験に直結するので仕様として予め考えておく(モバイル向けの場合、APIの停止はトレンドを見つつ行うこと) # アプリのアップデートを促すようにしておく(強制アップデートは好まれないが、使えなくなるよりも遥かにマシ)

Linuxのログイン履歴確認

はじめに


毎度なんだっけ?ってなって思い出すのが面倒なので、備忘録的にログイン履歴確認方法を記録

最近ログインしたアカウント一覧


以下のコマンドで確認できる

1
$last

実行結果(上に行くほど最新)

1
2
3
root     tty1                          Tue May  6 18:59 - crash  (00:16)
root     tty1                          Tue May  6 18:58 - 18:59  (00:00)
reboot   system boot  2.6.32-431.11.2. Tue May  6 17:16 - 21:21  (04:04)

ログファイルから確認する


もう少し細かく見る場合はログファイルを確認する
バイナリなので中を見たいときはwhoコマンドを使用する

1
$who /var/log/wtmp

実行結果(ログファイルなので、ファイル末尾が新しい)

1
2
3
root     tty1         2014-05-06 18:58
root     tty1         2014-05-06 18:59
root     tty1         2014-05-06 19:19

各アカウントの最終ログインの一覧


普段利用していないアカウントでログインされていないか確認できる
以下のコマンドで確認できる

1
$lastlog

実行結果(各アカウントの最終ログインが表示)

1
2
3
4
5
6
7
8
ユーザ名         ポート   場所             最近のログイン
root             tty1                      **一度もログインしていません**
bin                                        **一度もログインしていません**
daemon                                     **一度もログインしていません**
adm                                        **一度もログインしていません**
lp                                         **一度もログインしていません**
sync                                       **一度もログインしていません**
shutdown                                   **一度もログインしていません**

Octopressでの画像の表示

はじめに


常々、画像ファイルってどう表示するのだっけ?と思っていたのでやってみた感じ

ファイルの格納先


octopress/source/imagesディレクトリに画像ファイルを格納する

themeで表示している画像が最初から格納されているので、ビビらず画像ファイルを格納すること

指定方法


公式を参照すればできる模様

そのため前述の格納先を相対URLで指定すると、以下の形になる

1
![hoge](/images/hoge.jpg)

こんな感じでやると画像を表示できる

TimeCapsuleのethernetを確認する

備忘録的に


TimeCapsuleのIPアドレスを固定にしたかったので、MACアドレスを調べる必要が出てきた

また同じことにならないようにメモしておく

確認方法


下記の図のように、AirMacユーティリティを開き機器のタイトルを選択すると表示されます

AirMac

AndroidのPull to Refreshをちょっとだけ調べた

はじめに


ここの内容は完全に備忘録です。
何故かというと、自分がちょっと実現したい機能があったけどできるのだろうかと調べてみたメモだからです。

普通のPull To Refresh


所謂、下向きに引っ張るとListViewが更新されるやつですね。

これはAPI Level19のSupport V4 Libraryにて機能提供されているので、簡単に実現できます。
(ちょっと癖はあるかもしれませんが・・・)

以下にデモとReferenceを記載

# デモを見ればわかりますが、扱うだけなら非常に楽に使えるような感じになっています(カスタムは面倒臭さそう)
# ぐぐれば、いっぱい記事は見つかるので詳細はそちらに譲ります

やりたかったこと


下から上にListViewを引っ張ると、ListViewが更新されること
俗に言う、bottomup to refreshってやつです

調べてみた


ライブラリは?

Android-PullToRefreshが、対応している 但し、作者がdeprecated宣言

カスタム実装は?

少し試してみた感じだと、標準機能を駆使して実現(Classの継承など)は、まず無理かも

# そもそも、そんな感じで簡単にrefreshする機能を上下を入れ替え作られているような感じにはなってなかった

ソースコードを見れば?

結論から言うと、onTouchEventでアニメーションの実施の有無など詳細な条件判定とかやっているので今のところ難しそう

SwipeRefreshLayout.java

# 追いかけた結果を書くのは割愛します

他の人はどうなの?

同じことを考える方はいらっしゃるようですね。

SwipeRefreshLayout - Pull From Bottom

ざっくり書くと、こんな感じ

  • 今のAPIだと簡単には無理
  • OSSのコードをコピーして、自前で実装した
  • ここを参考に実装すればもしかしたらできるかも

# ソースコード見た感触とあまり変わらない(´・ω・`)

ちょっと調べてとりあえず分かったこと


API叩いて直ぐ実現可能というわけではないようですね(´・ω・`)

  • クラスを継承した独自のLayoutにすればなんとかできそうかもしれない
  • 但し、onTouchEventを自前で実装するのと処理中のアニメーションを挟むため、一筋縄ではいかない
  • 実現方法の参考実装はあるので、うまくできそうかやってみればいいかもしれない
  • deprecated宣言しているが、ライブラリを使えば楽にはできる(その後のメンテナンスコストは払う必要は出そうだが・・・)

Emacsのバージョンで分岐をさせたい

発端


Emacsのバージョンを最新にしたことで色々設定が楽に書けることがわかった
そのため、最新内容を反映させた

とはいえ、下位互換もある程度保ちたかった
(全部、追従すればいいのだがそうもいかない環境もあるので)

最初に試してみた


普通にバージョンを取得してみた

1
2
3
(emacs-version)
"GNU Emacs 24.4.1 (x86_64-apple-darwin14.0.0, NS apple-appkit-1343.16)
 of 2015-01-04 on hoge.local"

文字列のマッチング処理が多くなりそうで結構めんどくさい・・・

結果的にこうした


普通のmajor/minor バージョン判定を複合した条件判定するだけの関数を用意

24.3以下の判定

1
(defvar is-less-than-or-equal-24-3 (and (<= emacs-major-version 24) (<= emacs-minor-version 3)))

Web API the Good Parts2章

前置き

近況


ざっくりと書くと、お前エンジニアなのに勉強しなくていいの?という状態

  1. 免許が取れたので単車を乗り回している(運転が楽しくて毎日、運転しているような)
  2. 昨年よりも比較的、仲がいい友人(男女問わず)と遊ぶ機会が多くなったかも…
  3. 気の合う仲間と組んで、(Swiftで)アプリ作っている
  4. その気の合う仲間への技術指導していたり…

なんだか、教えることが多くなって自分が学習している時間が比較的減ってきているそんな感じががが

そういう訳なので


本屋でざっくりと見かけてツボに嵌った本を読んでいこうかと

「Web API The Good Parts」

なのですが、上記3,4が結構あるので中々進まないという(ぉぃ

ということなので、なるたけ気になるところ(覚えておくべきところ)だけまとめていこうかなと

斜め読みした感じでは、結構ツボに嵌る内容なので自分のバイブルに入選してもいいかなと思ってたり。

エンドポイント設計

  1. 公開する機能を先に設計する
  2. 覚えやすく、どんな機能を持つURIかひと目で分かるようにする
  3. HTTPメソッドの活用
  4. 設計の注意点
  5. クエリパラメータ
  6. ログインや認証
  7. ホスト名とエンドポイント

1に関して


どんなAPIが必要かどうかを先に洗い出しておく

2に関して


  • 短く入力しやすいURI
  • 人間が読んで理解できるURI
  • 大文字、小文字が混在しないURI
  • 改造しやすいURI
  • サーバ側アーキテクチャが反映されていないURI
  • ルールが統一されたURI

3に関して


HTTPメソッドを有効活用

  • Restfulな形で活用すれば、一般的な形

4に関して


  • 複数形の名詞を利用する
  • 利用する単語に気をつける((ProgramableWeb)[http://www.programmableweb.com/]等、他のサービスを参考に)
  • スペースやエンコードが必要な文字は避ける
  • 単語をつなげる場合はハイフンにする(SEO的に有利なことも)

5に関して


  • 相対位置を使用する場合、データ数が多くなると取得件数が減ること、更新頻度が高いものは不整合が起きやすくなることに注意
  • 絶対位置を使用する場合、クエリパラメータが「x以降」,「この日付よりも古いもの」という形でクエリパラメータをもたせたほうがよい
  • 日付の形式は、RFC3339に規定されている「1970-01-01T00:00:00Z」の形にする
  • 絞り込みのパラメータは、「q」のような形は曖昧検索に取られやすいので完全一致の場合は適切なパラメータにすること
  • 一意なリソースを表すのに必要な情報: パス
  • 省略可能: クエリパラメータ

6に関して


  • OAuthを使用して、標準的な形にする

標準的なOAuthに則るとエラーメッセージ(RFC6749, RFC6750)は、以下のような形式となる (当然ながらhttpステータスは401)

1
2
3
{
  "error": "invalid_token"
}

7に関して


  • ホスト名にapiを入れるのが主流
  • 企業で複数のサービスをホストする場合、例外的なホスト名
  • サービスとしてのapiの場合はホスト名にapiをいれたほうがよい(外部から使われるため)
  • プラットフォームの場合、service等をホスト名にいれる

まとめ


  • 一般的なURI設計がそのまま適用できる
  • APIならではのルール、デファクトスタンダードがある
  • URIはリソースを表すものなので、URIとHTTPメソッドの組み合わせで処理の対象と内容を設計をする

Good

  • 覚えやすく、どんな機能を持つかが一目で分かるようにエンドポイントにする
  • 適切なHTTPメソッドを利用する
  • 適切な英単語を利用し、単数形、複数形にも注意する
  • 認証はOAuth2.0を使う

iOSで複数のmodalを同時に閉じる

こんな状況


[A→B→C→Dの順で画面を開いた状況で、D->Aへ戻りたいという状況

やり方


戻りたいところのmodalを指定すると、その前のものは全て閉じることができました

1
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)

画面遷移の状態を覚えてないと辛い感じですね

元ネタ


https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/dismissViewControllerAnimated:completion: