2018年7月15日日曜日

Concourse CIで成果物をMavenリポジトリにアップする

Concourse CIのMaven Resourceを使ってビルド成果物をMavenリポジトリにデプロイする方法です。

ジョブの構成

ジョブは、下の画像のようにアプリケーションをビルドしてnexusにデプロイする構成としています。

ジョブ定義

  • resource_typesで、maven-resourceを定義します。
  • resourcesは、resource_typesで定義したmaven-resourceの設定(urlや接続情報やartifactなど)を行います。
  • jobのtask定義ではリポジトリにデプロイする対象をoutputsで定義します。
  • put定義では、デプロイ対象の成果物(jarとpom)を定義します
# maven-resourceの定義
resource_types:
- name: maven-resource
  type: docker-image
  source:
    repository: nulldriver/maven-resource
    tag: latest

resources:
- name: app
  type: git
  source:
    uri: http://gitbucket:8080/git/root/sample-app.git

# maven-resourceのリソース定義
# urlや接続情報などを設定します
- name: nexus
  type: maven-resource
  source:
    url: http://nexus:8081/repository/maven-releases/
    snapshot_url: http://nexus:8081/repository/maven-snapshots/
    artifact: sample:sample-app:jar
    username: admin
    password: admin123
jobs:
- name: build
  plan:
  - get: app
    trigger: true
  - task: build
    config:
      platform: linux
      image_resource:
        type: docker-image
        source:
          repository: maven
          tag: '3.5.4-jdk-8'
      inputs:
        - name: app
      # putでmavenリポジトリに成果物をデプロイできるようにするためのoutputsの定義
      outputs:
        - name: target
      run:
        path: bash
        args:
        - -c
        - |
          cd app
          mvn clean package -D maven.repo.local=/tmp/.m2/repository
          cp -r target ..
  # jarとpomを指定してリポジトリにデプロイします
  - put: nexus
    params:
      file: target/sample-app*.jar
      pom_file: app/pom.xml
      

ジョブの実行と結果の確認

ジョブを実行すると、下のようなログが出力されてリポジトリに成果物がアップされていることがわかります。

[INFO] --- maven-deploy-plugin:2.7:deploy-file (default-cli) @ standalone-pom ---
Downloading: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/maven-metadata.xml
Uploading: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/sample-app-1.0.0-20180715.115944-1.jar
Uploaded: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/sample-app-1.0.0-20180715.115944-1.jar (1.8 kB at 8.4 kB/s)
Uploading: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/sample-app-1.0.0-20180715.115944-1.pom
Uploaded: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/sample-app-1.0.0-20180715.115944-1.pom (403 B at 5.2 kB/s)
Downloading: http://nexus:8081/repository/maven-snapshots/sample/sample-app/maven-metadata.xml
Uploading: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/maven-metadata.xml
Uploaded: http://nexus:8081/repository/maven-snapshots/sample/sample-app/1.0.0-SNAPSHOT/maven-metadata.xml (766 B at 9.9 kB/s)
Uploading: http://nexus:8081/repository/maven-snapshots/sample/sample-app/maven-metadata.xml
Uploaded: http://nexus:8081/repository/maven-snapshots/sample/sample-app/maven-metadata.xml (276 B at 4.1 kB/s)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

nexusでも成果物がアップされていることが確認できます。


mvnコマンド実行時にローカルリポジトリの場所を指定する

mvnコマンド実行時にローカルリポジトリの場所を指定したい場合はmaven.repo.localシステムプロパティを使用します。

例えば、カレントディレクトリ配下にローカルリポジトリを置きたい場合、mvn test -Dmaven.repo.local=./m2/repositoryのように指定します。

実行例

システムプロパティで指定した場所にローカルリポジトリが作られているのがわかります。

$ ls .m2
ls: '.m2' にアクセスできません: そのようなファイルやディレクトリはありません

$ mvn package -Dmaven.repo.local=./.m2/repository
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------------< sample:sample-app >--------------------------
[INFO] Building sample-app 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
*************************************** 省略 ***************************************
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:25 min
[INFO] Finished at: 2018-07-15T06:37:10+09:00
[INFO] ------------------------------------------------------------------------

$ ls .m2
repository

2018年7月11日水曜日

JacksonでイミュータブルなオブジェクトにJsonをマッピングする

com.fasterxml.jackson.annotation.JsonCreatorアノテーションを使うこと、コンストラクタかファクトリメソッドを使った構築ができる。

コンストラクタを使った場合

  • コンストラクタにJsonCreatorアノテーションを設定する。
  • 引数には、JsonPropertyアノテーションを設定し、Jsonのキー名を指定する。
public class Sample {

    public final String name;

    public final short age;

    @JsonCreator
    public Sample(
            @JsonProperty("name") final String name,
            @JsonProperty("age") final short age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Sample{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

ファクトリメソッドを使った場合

ファクトリメソッドの場合には、コンストラクタに設定していたアノテーションをファクトリメソッドにそのまま持ってくれば良い。
public class Sample {

    public final String name;

    public final short age;

    private Sample(final String name, final short age) {
        this.name = name;
        this.age = age;
    }

    @JsonCreator
    public static Sample create(
            @JsonProperty("name") final String name,
            @JsonProperty("age") final short age) {
        return new Sample(name, age);
    }

    @Override
    public String toString() {
        return "Sample{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

動作結果

動作確認に使ったコード
final ObjectMapper objectMapper = new ObjectMapper();
final Sample sample = objectMapper.readValue("{\"name\": \"なまえ\", \"age\": 100}", Sample.class);
System.out.println("sample = " + sample);

標準出力の結果
sample = Sample{name='なまえ', age=100}

2018年6月10日日曜日

zshのviモードでCtrl+Rでコマンドの履歴検索をできるようにする

これでCtrl+Rでコマンドの履歴検索ができるようになる。

bindkey '^R' history-incremental-pattern-search-backward

2018年5月28日月曜日

Spring MVCとThymeleafの組み合わせでcheckboxのデフォルト値を設定する

Spring MVCとThymeleafの組み合わせでcheckboxを使った場合の動きを理解する。

checkboxの値をBooleanで受け取る場合

Form

public class Form {

    private Boolean check;

    // ほかは省略
}

Controller

checkboxの値を確認できるように標準出力に値を出力するだけに
@PostMapping("hello")
public String hello(Form form) {
    System.out.println("form.check = " + form.check);
    return "input";
}

View

<form th:action="@{/hello}" method="post" th:object="${form}">
  <input type="checkbox" th:field="*{check}" />
  <button type="submit">submit</button>
</form>

動かしてみた結果

出力されるhtmlには、checkboxとcheckboxに対応したhiddenが出力されます。このhiddenがあることでcheckboxがチェックされなかった場合でもFormに値が設定されます。(Booleanなのでfalseが設定される)
これは、thymeleafのth:fieldで自動的に出力されます。
<form action="/hello" method="post">
  <input type="checkbox" id="check1" name="check" value="true" /><input type="hidden" name="_check" value="on"/>
  <button type="submit">submit</button>
</form>

checkboxをチェックしないで送信した時は標準出力に↓が出力されます。
form.check = false

ちなみに、th:fieldを使わないと下のようにhiddenが出力されないので、checkboxがチェックされなかった場合はFormには値が設定されないのでnullのままとなります。
<form action="/hello" method="post">
  <input type="checkbox" name="checkbox" />
  <button type="submit">submit</button>
</form>

checkboxの値をBoolean以外で受け取る場合

単一のcheckboxの値をBooleanで受け取ることはないと思うけど、Stringで受け取った場合の動きを確認してみます。

Stringの場合も、Booleanと同じようにth:fieldを使うと、↓が出力されます。だけど、Stringの場合はth:fieldが出力するアンダースコア付きのhiddenに対応していないので、このままだと未チェック時はFormの値はnullのままになります。

<form action="/hello" method="post">
  <input type="checkbox" value="on" id="check1" name="check"/><input type="hidden" name="_check" value="on"/>
  <button type="submit">submit</button>
</form>

未送信時に送信する値は、別途↓のようにhiddenで定義します。デフォルト設定だと、対応するcheckboxのnameの先頭に!をつけた名前でhiddenを定義する必要があります。
プレフィックスの値はWebDataBinderのJavadocが参考になります。
<form th:action="@{/hello}" method="post" th:object="${form}">
  <input type="checkbox" th:field="*{check}" value="on" />
  <input type="hidden" name="!check" value="off" />
  <button type="submit">submit</button>
</form>

これで、未チェック時にはhiddenで指定した値をFormで受け取れるようになります。
form.check = off

2018年5月2日水曜日

Spring Securityで認証後にかならず決まったページに遷移させる

defaultSuccessurlの2番目の引数にtrueを指定すると、ログイン成功後に必ず最初に指定したパスにリダイレクトされる。
@Override
protected void configure(final HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .defaultSuccessUrl("/index", true)
        .and()
        .logout()
        .invalidateHttpSession(true);
}

2018年4月3日火曜日

Spring BootとFlywayでテーブルなどがあるスキーマに対してマイグレーションを実行する

application.propertiesに下を追加して、既存の状態をバージョン001とすることでマイグレーションが可能になる。
アプリ側のSQLファイルのバージョンは、V002__Create_hoge_table.sqlのようにベースバージョンより大きいバージョンにする。

spring.flyway.baseline-on-migrate=true
spring.flyway.baseline-version=001



なお、デフォルト状態だとFound non-empty schema "PUBLIC" without metadata table!のエラーが出て実行できない。