IBM Cloud Functions自体の説明はIBM Cloud FunctionsでPHPアプリケーションを作る - Qiitaを参照。
Hello World
IntelliJ IDEA(以降IntelliJと略す)を使って作る。環境はMacを使用している。
IntelliJを開いて「Create New Project」をクリックするか「File」-「New」-「Project」からプロジェクトを作る。
左側のGradleを選択肢、右側のJavaにチェックをしてNext。
GroupidはMavenリポジトリにデプロイする予定がない場合は空でよい。 ArtifactIdはプロジェクト名を入れる。ここではsample-javaとする。Versionはデフォルトのままとする。
Use auto-importにチェックを入れる。
Project name, Project locationはそのままでFinish。
FinishするとGradleの初期処理が行われ、以下の方なディレクトリ構成になる。
src - main - javaのディレクトリを右クリックしてJava Classを作る。
名前はSampleとする。
Cloud Functions アクションの作成と起動にあるJavaのサンプルのコードを参考にして、
import com.google.gson.JsonObject;
public class Sample {
public static JsonObject main(JsonObject args) {
String name = "stranger";
if (args.has("name"))
name = args.getAsJsonPrimitive("name").getAsString();
JsonObject response = new JsonObject();
response.addProperty("greeting", "Hello " + name + "!");
return response;
}
}
をSample.javaに書き込む。gsonのimportするために、build.gradleのdependenciesにgsonを追加する。
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.code.gson:gson:2.6.2' // 追加する
testCompile group: 'junit', name: 'junit', version: '4.12'
}
追加すると、gsonがインストールされる。
ここで、build.gradleについて見ていく。
versionでこのプロジェクトのバージョンを設定する。
apply plugin: 'java'
でJavaプラグインを設定する1。これでJavaで使う設定やtaskが使えるようになる。IntelliJのプロジェクトの作成で出来たディレクトリ構成が
└── src/
├── main/
│ ├── java/
│ └── resources/
└── test/
├── java/
└── resources/
になるが、これはJavaプラグインのデフォルトのプロジェクトレイアウト
src/main/java 製品のJavaソース
src/main/resources 製品のリソース
src/test/java テストのJavaソース
src/test/resources テストのリソース
になっている。
repositoriesでMavenセントラルリポジトリを使用する設定をする。
dependenciesに依存ライブラリを書く。compileにはコンパイル時の依存関係を、testCompileにはテストのコンパイル時の追加依存関係を書く。
compile
とtestCompile
で書き方が違っているが、compile
の書き方は
compile group: 'com.google.code.gson', name: 'gson', version: '2.6.2'
の省略版。Gradleの設定はGroovyで書かれるが、Groovyはメソッドの丸括弧を省略できるので、compile
に丸括弧は付けていない。引数がない場合は省略できないので、mavenCentral()
には付いている。
ここで設定しているversion, apply, sourceCompatibility, repositories, dependenciesはProjectで確認できる。
では、jarを作る。IntelliJの右側にあるGradleをクリックすると、Gradle projectsウィンドウが表示される。
「Tasks」-「build」-「jar」を実行するとbuild/libs
にsample-java-1.0-SNAPSHOT.jar
というjarができる。
もしくはプロジェクトのディレクトリにgradlew
というファイルがあるので、これを使い
$ ./gradlew jar
でもjarを作成することができる。gradlew
はgradle wrapperと呼ばれるもので、これを使うと必要であればgradleのバイナリをダウンロードしてくれる。
jarの中身を確かめてみよう。
$ jar -tf build/libs/sample-java-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
Sample.class
-t
をアーカイブの中身を表示するオプション、-f
はファイルの指定。
jarはzip形式で圧縮したファイルである。
$ file build/libs/sample-java-1.0-SNAPSHOT.jar
build/libs/sample-java-1.0-SNAPSHOT.jar: Zip archive data, at least v1.0 to extract
ちなみに圧縮されたファイルの内容はVimで見ることができる。
$ vim build/libs/sample-java-1.0-SNAPSHOT.jar
" zip.vim version v28
" Browsing zipfile /Users/tmsanrinsha/IdeaProjects/sample-java/build/libs/sample-java-1.0-SNAPSHOT.jar
" Select a file with cursor and press ENTER
META-INF/
META-INF/MANIFEST.MF
Sample.class
META-INF/MANIFEST.MF
の行にカーソルを置いて、Enterを押すと
Manifest-Version: 1.0
のように中身が表示される。
actionをアップロードする。
$ bx wsk action create sample-java build/libs/sample-java-1.0-SNAPSHOT.jar --main Sample
実行する。
$ bx wsk action invoke sample-java --result
{
"greeting": "Hello stranger!"
}
パラメータを設定して実行する。
$ bx wsk action invoke sample-java --result -p name reader
{
"greeting": "Hello reader!"
}
依存ライブラリがある場合
$ jar -tf build/libs/sample-java-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
Sample.class
でjarの中身を見るとGsonが入ってない。しかし、GsonはIBM Cloud Functionsで用意されているのでなくても動く。
Building OpenWhisk actions with Java and Gradle - IBM Cloud Blogに依存ライブラリがある場合の例が載っている。
Sample.javaを以下のように書き換える。これは受け取ったテキストをQRコードにして返すプログラム。
import java.io.*;
import java.util.Base64;
import com.google.gson.JsonObject;
import com.google.zxing.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
public class Sample {
public static JsonObject main(JsonObject args) throws Exception {
String text = args.getAsJsonPrimitive("text").getAsString();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream b64os = Base64.getEncoder().wrap(baos);
BitMatrix matrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, 300, 300);
MatrixToImageWriter.writeToStream(matrix, "png", b64os);
b64os.close();
String output = baos.toString("utf-8");
JsonObject response = new JsonObject();
response.addProperty("qr", output);
return response;
}
}
build.gradle
を以下のように書き換える。
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
configurations {
provided
compile.extendsFrom provided
}
dependencies {
provided 'com.google.code.gson:gson:2.6.2'
compile 'com.google.zxing:core:3.3.0'
compile 'com.google.zxing:javase:3.3.0'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
jar {
dependsOn configurations.runtime
from {
(configurations.runtime - configurations.provided).collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
このgradleの設定はFat Jars with Excluded Dependencies in Gradleを参考にしたもの。
まず、
configurations {
provided
compile.extendsFrom provided
}
でprovided
というものを定義し、extendsFrom
を使って、compileをprovidedから継承する。
dependencies
で、jarに含まなくていいものはprovided
で、含むものはcompile
で定義する。compile
はprovided
に依存するので、コンパイル時はprovided
に書いた依存も入る。
jar
で、dependsOn
しているが、これがなくてもうまくいったので、いるのかわからない。
from
を使って、jarに含めるファイルを設定している。from
に含めるのはconfigurations.runtime
からconfigurations.provided
を引いたもの。dependency configurationsによるとruntime
はcompile
を継承したものなので、runtime
にはcompile
とprovided
の依存がはいり、それをprovided
で引くので、compile
の依存だけが残る。
jarを作り、
$ ./gradlew jar
$ jar -tf build/libs/sample-java-1.0-SNAPSHOT.jar
をすればGson以外のライブラリが入っているのがわかる。
updateして
$ bx wsk action update sample-java build/libs/sample-java-1.0-SNAPSHOT.jar --main Sample
実行する
$ bx wsk action invoke sample-java --result -p text 'Hello world!'
{
"qr": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsAQAAAABRBrPYAAABHElEQVR42u3aPRKCMBCG4VBxDI5KjpojWFIR2ezyEx3Uwiyj86ZwgjxU32w2/IT8ybgFGAwGg8F+ik3BxpDnMOapy9H+6GB+TI9yHGSWepkdT8CcWJRgooalF0
h2sEvYHhbsOiY52QUwf5brQnm3vsEasK2JT1Y3b3o9rAGrRlxPvt73wr7NJgumRDTK4Zj6WX5gfkxGEpG0bo4VBPNidfsIS07lNm+ZZZgfs4iWsMoFs9ZNGDLMj+mYrUaShvW8lYI5MItt1JNldtbrYQ3YoV8Ys9u8HubItvYha5a2871uYG5
sf7xhD8D7sr3qYFcwa+JBbzBOw4I1Zw/vImBubH8ZtNZNeLG+wVqw6qFf/UIC5sX4HAIGg8Fgf8Pu1ACAFLpztaIAAAAASUVORK5CYII="
}
とちゃんと結果が帰ってくる。これをpngにするには
bx wsk action invoke sample-java --result -p text 'Hello world!' | jq -r .qr | base64 -d > qr.png
として、
$ open qr.png
すればQRコードが表示される。
デバッグ用のメソッドを作る
public static JsonObject main(JsonObject args)
のargs
をいじれるように、public static void main(String[] args)
を追加する。
public class Sample {
public static void main(String[] args) throws Exception {
String str;
if (args.length == 0) {
str = "{\"text\":\"Hello stranger\"}";
} else {
str = args[0];
}
JsonObject jsonArgs = new JsonParser().parse(str).getAsJsonObject();
System.out.println(main(jsonArgs).toString());
}
public static JsonObject main(JsonObject args) throws Exception {
// ...
}
}
これで、引数が与えられて実行された場合はその第一引数を、なければ、str
を使って、public static JsonObject main(JsonObject args)
が実行される。
public static void main(String[] args)
のメソッドを作ると、IntelliJのコードの左側に▶マークでついて、それをクリックすると実行できるようになる。
引数を設定したい時は「Run」-「Edit Configurations...」から設定できる。しかし、コードのstr
を修正したほうが楽だろう。
Gradleで実行したい場合はbuild.gradleに以下を追加する。
// ...
apply plugin: 'java'
apply plugin: 'application' // 追加
mainClassName = 'Sample' // 追加
sourceCompatibility = 1.8
// ...
Application Pluginを使うとアプリケーションを実行するrun
というタスクが使えるようになる。メインのクラス名はmainClassName
で設定する。
これで
$ ./gradlew run
で実行することができる。Gradle projectsウィンドウのTasks - application - runから実行しても良い。
しかしgradle task
で直接引数を渡すことが出来ない。Gradle task - pass arguments to Java application - Stack Overflowによると
run {
if (project.hasProperty("appArgs")) {
args Eval.me(appArgs)
}
}
をbuild.gradleに追加すると
$ ./gradlew run -PappArgs='"{\"text\":\"a\"}"'
で引数付きで実行できる。
Gitで管理する
以下のファイルはいらないので.gitignore
に書く。
.gradle/
.idea/
build/
インポートする場合は「File」-「New」-「Project from Version Control」でGitHubのURLなどを指定する。Unlinked Gradle project?と出てくるので、Import Gradle projectしてやる。
Version Controlからインポートするのではなく、git clone
してきて、「File」-「New」-「Project from Existing Sources...」を選んで、「Import Gradle project」でGradleを選んでもよい。
今回作ったサンプル
参考資料
- Gradle入門 - 公式ヘルプ | IntelliJ IDEA
- Cloud Functions アクションの作成と起動にあるJavaのサンプル
- 第23章 Javaプラグイン
- The Java Plugin - Gradle User Manual
- 第8章 依存関係管理の基本
- Groovyを知らない人のためのbuild.gradle読み書き入門 - Qiita
- Project - Gradle DSL Version 4.5
- Gradle入門 - Qiita
- Building OpenWhisk actions with Java and Gradle - IBM Cloud Blog
- Fat Jars with Excluded Dependencies in Gradle
- Write runnable and deployable code for the IBM Cloud Functions platform
- The Application Plugin - Gradle User Manual
-
第23章 Javaプラグイン, The Java Plugin - Gradle User Manual。日本語は古いので注意。 ↩