自動生成されたプロジェクトの構造
作成されたフォルダ構成は、通常のmaven-webapp-archetypeに則ったものですが、次表に示すとおり、いくつかのApp Engine用のファイルが追加されています。
| 追加されたファイル | 意味 |
|---|---|
| src/main/webapp/WEB-INF/appengine-web.xml | App Engine用の定義ファイル |
| eclipse-launch-profiles/DevAppServer.launch | 開発サーバを起動するEclipseの実行構成ファイル |
| eclipse-launch-profiles/UpdateApplication.launch | App EngineにデプロイをするためのEclipseの実行構成ファイル |
さらに、pom.xml内にはappengine-maven-pluginという、App Engineに関する操作を行うためのプラグインの依存関係も追記されています。今回使用するCloud Endpointsに関する機能も、このプラグインを使用します。
このプラグインはApp Engineに関する様々な操作をゴール(goal)として提供しており、次のようなmavenコマンドを実行することで、全てのゴールを確認できます。
mvn help:describe -Dplugin=com.google.appengine:appengine-maven-plugin
Cloud Endpointsのための設定情報
作成されたプロジェクトには、通常のmaven-webapp-archetypeにApp Engineの設定が追加されていました。それだけではなく、Cloud Endpointsの設定もsrc/main/webapp/WEB-INF/web.xmlに追加されています。
<servlet>
<servlet-name>SystemServiceServlet</servlet-name>
<servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>com.shin1ogawa.YourFirstAPI</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SystemServiceServlet</servlet-name>
<url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>
SystemServiceServletというサーブレットクラスの定義が追加されていますが、これがCloud Endpointsの機能を担い、/_ah/spi/から始まるパスへのリクエストが届いたときに、このサーブレットクラスがリクエストを受け取る設定になっています。
SystemServiceServletのinit-param要素に定義されているservicesというパラメータには"YourFirstAPI"というAPIクラスが設定されていますが、ここに記述されたクラスがAPIクラスになります。開発者がAPIを作成した場合は、このservicesパラメータにもAPIクラスを追加していくことになります(カンマ区切りで複数のクラスを設定できます)。
APIを実装する
それでは、APIを1つ実装してみましょう。
データを保持するモデルクラスを作成する
まずは、サーバ側で何かのデータを保存することを想定し、モデルクラスを1つ作成します。このモデルクラスは、id、message、createdAtというフィールドと、それらへのアクセサを持つPOJOにします。クライアントからmessageを受け取り、idとcreatedAtをサーバ側で設定する想定です。
public class MyModel {
String id;
String message;
Date createdAt;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
}
Cloud Endpointsでは、JSONとJavaクラスの変換に「Jackson」を使用していますが、Jacksonの機能であるJsonSerializerやJsonDeserializerは使えません。ですので、値を変換したいフィールドや通信に含めたくないフィールドを持つPOJOを使う場合には、APIのインタフェースとして使用するCloud Endpoints用のモデルクラスを別途作り、本来のモデルクラスと変換してやる必要があります。
APIクラスを実装する
次に、先ほど作成したモデルクラスを使ってデータをやりとりするためのAPIクラスを実装します。先に説明したとおり、Cloud Endpointsで使用するAPIクラスはweb.xmlの設定でSystemServiceServletに認識させる必要がありますが、maven-archetype:generateでプロジェクトを生成すると"YourFirstAPI"というクラスが自動生成され、さらにSystemServiceServletに認識されるようweb.xmlに登録されるので、それをそのまま利用します。
Cloud Endpointsで使用するAPIクラスは@Apiアノテーションで修飾する必要があります。元々作成されていた"YourFirstAPI"クラスを開き、@Apiアノテーションで修飾します。また、このアノテーションには、APIの名前とバージョンを示すnameとversionパラメータが必要です。
@Api(name = "myFirstAPI", version = "v1")
public class YourFirstAPI {
}
APIのメソッド名は、Google APIの慣例にならい、データ追加用メソッドをinsert、データの一覧取得用メソッドをlistとすることにします。
また、Cloud Endpointsで使用するAPIのメソッドは@ApiMethodアノテーションで修飾する必要があります。APIメソッドの名前と対応するパスを示すname,とpathパラメータも必要です。
なお、作成しているのは学習用のサンプルアプリケーションですから、実装をシンプルにするためにデータベースなどは使用せず、データはstatic変数に保持するようにしています。
static ConcurrentHashMapmyModels = new ConcurrentHashMap<>(); @ApiMethod(name = "myFirstAPI.insert", path = "myFirstAPI/insert") public MyModel insert(MyModel myModel) { Date now = new Date(); myModel.setId(String.valueOf(now.getTime())); myModel.setCreatedAt(now); myModels.put(myModel.getId(), myModel); return myModel; } @ApiMethod(name = "myFirstAPI.list", path = "myFirstAPI/list") public List list() { return new ArrayList<>(myModels.values()); }
