突然Apache JMeterの独自サンプラのプラグインを作って負荷試験する必要になったときに読む記事

# 対象読者

- Apache JMeterを使った負荷試験の計画を練っている最中、既存のサンプラだけでは不十分な状況に陥ったので、独自のサンプラをサッと書きたい。
- Javaはなんとなく読み書きできるけど詳しくない。Javaのビルド設定の仕方が分からない。


# この文章について

Apache JMeterで独自サンプラをJavaでサッと書くための手順を説明します。Javaに詳しくない人向けの文章であるためビルド設定などを重点的に説明します。

JMeterのサンプラの内、**JavaSampler**向けのプラグインを記述します。

## 手順

### 1. InteliJ IDEAのインストール

普段Javaを書かない方、Javaの統合開発環境である[InteliJ IDEA](https://www.jetbrains.com/ja-jp/idea/)をインストールしましょう。無償版と有償版があります。（筆者はJetBrainsのAll in packを購入していますが、無償版でも充分かと思います。）

以下、IntelliJ IDEA 2023.2.2を前提に記述します。


### 2. プロジェクトの作成

InteliJを起動したら新規プロジェクトの作成を始めます。

- JDKは(2023年9月21日現在)19以下にします。JDKのインストールが無くてもJDKのプルダウンメニューからインストールできます。
- BuildSystemは、ここではGradleを選び、Gradle DSLはGroovyにします。
- Advanced SettingsのGroupIdは任意の値を指定します。ちょうどドメイン名を逆転したような書き方をします。たとえば`com.<会社名の英語>`など。

![Screenshot 2023-09-21 at 2.08.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/a74ef02a-7faf-ef92-d8f7-d813520704bd.png)

プロジェクトを作成します。

### 3. 依存関係の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/82514b69-e614-44be-e8fe-75e4fc51932c.png)

`build.gradle`を開いて、`dependencies`にApache JMeterのライブラリを追加するために、下記を追記します。ここではバージョン`5.6.2`を指定していますが、どのバージョンがあるかを[MavenCentral](https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_java)で確認できます。

```
dependencies {
    testImplementation platform('org.junit:junit-bom:5.9.1')
    testImplementation 'org.junit.jupiter:junit-jupiter'
    // ↓追記
    implementation group: 'org.apache.jmeter', name: 'ApacheJMeter_java', version: '5.6.2'
}
```

`build.gradle`ファイルを変更するとエディタの右上くらいにこんなアイコンが表示されるのでクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/1a5ccaa9-51bc-0ac7-6e5f-f72bbed4bc13.png)

### 4. サンプラの記述

`./src/main/java`を右クリックし、`Package`を選択してパッケージを作成します。例えば`com.<会社名の英語>.samplers`など、プロジェクト作成時に指定したGroupIdの後に任意の名前を指定します。

作成したパッケージ配下に下記のようなJavaクラスを作成します。サンプラクラスは`JavaSamplerClient`インターフェイスを実装する必要があります。

```java:OriginalSampler
package com.hiroyky.samplers;

import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;

public class OriginalSampler implements JavaSamplerClient {

    static final String URL = "Enter request URL";
    static final String  USERNAME = "Enter user name";
    static final String PASSWORD = "Enter password";

    @Override
    public void setupTest(JavaSamplerContext context) {

    }

    @Override
    public SampleResult runTest(JavaSamplerContext context) {
        /* サンプラの処理本体を書きます。 */

        // 設定項目の値取得
        String url = context.getParameter(URL);
        String userName = context.getParameter(USERNAME);
        String password = context.getParameter(PASSWORD);

        // 結果に出力するメッセージを作成します。
        StringBuilder responseMessage = new StringBuilder();
        responseMessage.append("URL: ").append(url).append("\n");
        responseMessage.append("User name: ").append(userName).append("\n");

        SampleResult result = new SampleResult();
        try {
            // 時間計測開始
            result.sampleStart();

            /*
            * TODO: 行いたい処理を書いてください。
            * */

            // 時間計測終了
            result.sampleEnd();

            // 結果に成功を登録
            result.setResponseCodeOK();
            result.setSuccessful(true);
            result.setResponseMessage(responseMessage.toString());
        } catch (Exception e) {
            // 時間計測終了
            result.sampleEnd();

            // 失敗を登録
            result.setResponseCode("500");
            result.setSuccessful(false);
            responseMessage.append("error: ").append(e.getMessage()).append("\n");
            result.setResponseMessage(responseMessage.toString());
        }
        return result;
    }

    @Override
    public void teardownTest(JavaSamplerContext context) {
    }

    @Override
    public Arguments getDefaultParameters() {
        // 設定項目を定義します。第二引数はデフォルト値の文字列
        // ここの設定項目がGUI上に表示されます。
        Arguments arguments = new Arguments();
        arguments.addArgument(URL, "http://localhost/");
        arguments.addArgument(USERNAME, "taro");
        arguments.addArgument(PASSWORD, "password");
        return arguments;
    }
}
```

### 5. ビルド
画面右の`Gradle`から`build`をクリックしてビルドします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/4be75067-31cd-61dc-081a-7615bd562ad8.png)

ビルドが成功すると `build/libs/`以下にJarファイルが生成されます。これが目的のビルド成果物です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/4f14e354-e7fc-ce74-28a8-e379005d68fa.png)

### 6. インストール

5．で生成したjarファイルをApache JMeterの `./lib/ext/`にコピーします。コピーしたらJMeterを(再)起動します。

### 7. 使ってみる

Samplerから`Java Request`を作成します。Java Requestの中で作成した独自サンプラが追加されているはずなので選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/d548d176-1a67-0667-84d1-40d6942e16c2.png)

以上です。

## トラブルなど

### 依存関係が足らないとき

独自サンプラが別のライブラリに依存した場合、それもJMeterの`./lib/ext`もしくは`./lib/`配下に設置する必要があります。例えば、`NoClassDefFoundError`が出た場合はまさに依存関係の問題が発生しています。もし一括してそれらを取得したい場合は`build.gradle`に以下の記述をします。

依存関係を一括ダウンロードして、`build/libs/`に保存するタスクを定義します。

```
task copyDependencies(type: Copy) {
    from configurations.runtimeClasspath
    into 'build/libs'
}
```

このアイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/1a5ccaa9-51bc-0ac7-6e5f-f72bbed4bc13.png)

すると右側のタスクメニューに`other->copyDependencies`が追加されるのでクリックします。依存関係が`build/libs`にダウンロードされるので全てのJarファイルをJMeterの `./lib/ext`以下に入れるととりあえずは良いでしょう。


## おわり

Apache JMeterの独自サンプラをサッと作ってビルドする方法でした。