2017年6月16日金曜日

[Tomcat]StuckThreadDetectionValveを使って遅延した処理の情報をログに出力する

StuckThreadDetectionValveを使うことで、処理遅延が発生したスレッドを検知することができる。

使い方は下の通り

設定を追加する

conf/server.xmlにStuckThreadDetectionValveを使用するように設定をする。

thresholdを使って、どれぐらい遅延したら検知するかを設定する。
デフォルトは600秒となっている。下のように60と設定すると60秒となる。

  


ログの結果

単純に一定時間スリープするServletで試すと下のようなログが出力される。

16-Jun-2017 21:14:37.445 警告 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.valves.StuckThreadDetectionValve.notifyStuckThreadDetected Thread [http-nio-8080-exec-6] (id=[29]) has been active for [62,731] milliseconds (since [17/06/16 21:13]) to serve the same request for [http://localhost:8080/hoge] and may be stuck (configured threshold for this StuckThreadDetectionValve is [60] seconds). There is/are [1] thread(s) in total that are monitored by this Valve and may be stuck.
java.lang.Throwable
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at hoge.HogeServlet.doGet(HogeServlet.kt:11)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)

その後処理が完了すると、下のようなログも出力される

16-Jun-2017 21:15:37.465 警告 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.valves.StuckThreadDetectionValve.notifyStuckThreadCompleted Thread [http-nio-8080-exec-6] (id=[29]) was previously reported to be stuck but has completed. It was active for approximately [120,031] milliseconds.

これで、処理遅延などが発生した場合に自動的にスレッドダンプが取得できる。
障害時にスレッドダンプとってません問題も回避できそうな気もする。

2017年5月31日水曜日

AssertJ-DBを使ってデータベースの値をアサート

AssertJ-DBを使ってデータベースのテーブルの値などをアサートする方法

assertj-dbを依存に追加する

testCompile 'org.assertj:assertj-db:1.2.0'

テーブルの内容

ID,NAME
1,name
2,name_2



シンプルにテーブルの内容をアサートする例

Tableクラスを使うことでテーブルの内容全体に対してアサートを行なうことができます。

final OracleDataSource source = new OracleDataSource();
// 接続先情報は省略
final Table.Order order = Table.Order.asc("id");
final Table table = new Table(source, "test_table", new Table.Order[] {order});

assertThat(table)
        .column("id")
        .value().isEqualTo(1L)
        .value().isEqualTo(2L)
        .column("name")
        .value().isEqualTo("name")
        .value().isEqualTo("name_2");

SQLの結果に対してアサート

Requestクラスを使ってSQLを実行し、その取得内容をアサートすることができます。
final Request request = new Request(source, "select name from test_table where id = 2");

assertThat(request).column(0).value().isEqualTo("name_2");

テーブルの変更内容をアサートする

Changesクラスを使うことで、開始ポイントと終了ポイントを定義しその間の変更内容をアサートすることができます。
この例では、1レコードの変更が正しいことをアサートしています。
final Table table = new Table(source, "test_table");
final Changes changes = new Changes(table);
changes.setStartPointNow();

try (final Connection connection = source.getConnection();
     final PreparedStatement statement = connection.prepareStatement(
             "update test_table set name = ? where id = 2")) {
    statement.setString(1, "へんこうご");
    statement.execute();
}

changes.setEndPointNow();
assertThat(changes)
        .hasNumberOfChanges(1)                       // 1レコード変更されている
        .ofModification().hasNumberOfChanges(1)      // 変更内容は更新
        .change()
        .columnAmongTheModifiedOnes()
        .hasColumnName("name")
        .hasValues("name_2", "へんこうご");          // name_2からへんこうごに変更されていること

2017年4月13日木曜日

文字列からLocalDateを作る方法

DateTimeFormatterを使って文字列をパースする。

final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
final LocalDate localDate = LocalDate.parse("2017/04/13", formatter);

2017年1月11日水曜日

[JMockit]特定のテストでだけモックを定義する

JMockitで特定のテストでモックを使う方法

使用バージョン

    
      org.jmockit
      jmockit
      1.30
      test
    

サンプルコード

この例では、initialCapacityを受け取るコンストラクタとaddメソッドにモック実装を定義しています。
new MockUp<ArrayList<String>>() {
    @Mock void $init(int initialCapacity) {
        System.out.println("initialCapacity = " + initialCapacity);
    }
    
    @Mock boolean add(String s) {
        System.out.println("s = " + s);
        return true;
    }
};
final List<String> strings = new ArrayList<>(10);
strings.add("hoge");

実行結果

initialCapacity = 10
s = hoge

2016年12月19日月曜日

[H2]ロックタイムアウト時間の変更

ロックタイムアウトのデフォルトは、1000ミリ秒(1秒)となっています。
この値を変更するには、SET LOCK_TIMEOUTを使う。

接続時に値を変更する場合は、下のようにURLの後にLOCK_TIMEOUTを指定する。

jdbc:h2:test;LOCK_TIMEOUT=10000

2016年11月3日木曜日

[Jackson]jsonをMapとして読み込んだ際の数値型を変更する

jsonをMapとして読み込んだ時のデフォルトのデータ型を変更する方法。

読み込むjson

{
  "number": 123456789012,
  "decimal": 1.1
}

デフォルトの設定で読み込んだ結果

こんな結果になる。整数の場合は、読み込む内容によってIntegerに変わったりします。
result = {number=123456789012, decimal=1.1}
number = java.lang.Long
decimal = java.lang.Double

データ型を変更する

下のようにUSE_BIG_INTEGER_FOR_INTSとUSE_BIG_DECIMAL_FOR_FLOATSを有効にします。
objectMapper.enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);

変更後の読み込んだ結果

result = {number=123456789012, decimal=1.1}
number = java.math.BigInteger
decimal = java.math.BigDecimal

読み込みコードの全体

final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);

final TypeFactory factory = objectMapper.getTypeFactory();
final Map result =
    objectMapper.readValue(inputStream, factory.constructType(Map.class));
System.out.println("result = " + result);

outputType(result, "number");
outputType(result, "decimal");

2016年10月1日土曜日

[Jackson]Enumの値を読み書きする

Jacksonを使って、json内の値をEnumとして読み書きする方法。

Jacksoのバージョン

compile 'com.fasterxml.jackson.core:jackson-databind:2.8.3'

jsonにマッピングするオブジェクトの定義

json→Enumの変換は@JsonCreatorアノテーションを設定したstaticメソッドで行う。
Enum→jsonの変換は@JsonValueアノテーションを設定したメソッドで行う。
public class SampleBean {

    private Enum value;

    public Enum getValue() {
        return value;
    }

    public void setValue(final Enum value) {
        this.value = value;
    }
}

enum Enum {
    A(1),
    B(2),
    C(3),;

    private int value;

    Enum(final int value) {
        this.value = value;
    }

    @JsonValue
    public int toValue() {
        return value;
    }

    @JsonCreator
    public static Enum fromValue(int value) {
        return Arrays.stream(values())
              .filter(v -> v.value == value)
              .findFirst()
              .orElseThrow(() -> new IllegalArgumentException(String.valueOf(value)));
    }
}

jsonの内容

{"value": 2}

json→Beanに実装

final ObjectMapper objectMapper = new ObjectMapper();

final SampleBean bean = objectMapper.readValue("{\"value\": 2}", SampleBean.class);
System.out.println("bean.getValue() = " + bean.getValue());

final String json = objectMapper.writeValueAsString(bean);
System.out.println("json = " + json);

結果

bean.getValue() = B
json = {"value":2}