2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IBM Cloud FunctionsでJavaアプリケーションを作る

Last updated at Posted at 2018-02-04

IBM Cloud Functions自体の説明はIBM Cloud FunctionsでPHPアプリケーションを作る - Qiitaを参照。

Hello World

IntelliJ IDEA(以降IntelliJと略す)を使って作る。環境はMacを使用している。

IntelliJを開いて「Create New Project」をクリックするか「File」-「New」-「Project」からプロジェクトを作る。

左側のGradleを選択肢、右側のJavaにチェックをしてNext。

image.png

GroupidはMavenリポジトリにデプロイする予定がない場合は空でよい。  ArtifactIdはプロジェクト名を入れる。ここではsample-javaとする。Versionはデフォルトのままとする。

image.png

Use auto-importにチェックを入れる。

image.png

Project name, Project locationはそのままでFinish。

FinishするとGradleの初期処理が行われ、以下の方なディレクトリ構成になる。

image.png

src - main - javaのディレクトリを右クリックしてJava Classを作る。

image.png

名前は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にはテストのコンパイル時の追加依存関係を書く。
compiletestCompileで書き方が違っているが、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ウィンドウが表示される。

image.png

「Tasks」-「build」-「jar」を実行するとbuild/libssample-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で定義する。compileprovidedに依存するので、コンパイル時はprovidedに書いた依存も入る。

jarで、dependsOnしているが、これがなくてもうまくいったので、いるのかわからない。

fromを使って、jarに含めるファイルを設定している。fromに含めるのはconfigurations.runtimeからconfigurations.providedを引いたもの。dependency configurationsによるとruntimecompileを継承したものなので、runtimeにはcompileprovidedの依存がはいり、それを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に書く。

.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を選んでもよい。

今回作ったサンプル

参考資料

  1. 第23章 Javaプラグイン, The Java Plugin - Gradle User Manual。日本語は古いので注意。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?