Jaybanuan's Blog

どうせまた調べるハメになることをメモしていくブログ

AspectJを利用したプロジェクトのMavenでのビルドとテスト (Load-Time Weaving)

はじめに

あるOSSの解析のためにAspectJを利用した。 その際にMavenアスペクトのビルドとテストを行ったので、その記録を残しておく。 ここでは、テストメソッドにログ出力を織り込むことにする。

ディレクトリ構成

$ tree
.
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- redj
    |   |       `-- aspectj
    |   |           `-- LoggingAspect.java
    |   `-- resources
    `-- test
        `-- java
            `-- redj
                `-- aspectj
                    `-- LoggingAspectTest.java

LoggingAspect.java

アスペクトを準備。 パッケージredj.aspectjの任意のクラスにおいて、testをプレフィックスに持つメソッドの実行前にログを出力する。

package redj.aspectj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoggingAspect {

    @Pointcut("execution(* redj.aspectj.*.test*(..))")
    public void atTestMethod() {
    }

    @Before("atTestMethod()")
    public void logBeforeTest() {
        System.out.println("***** begin test");
    }
}

LoggingAspectTest.java

テストクラスを準備。 アスペクトが織り込まれるように、testをプレフィックスに持つメソッドを定義しておく。

package redj.aspectj;

import org.junit.Test;

public class LoggingAspectTest {

    @Test
    public void testFoo() {
        System.out.println("test foo");
    }

    @Test
    public void testBar() {
        System.out.println("test bar");
    }
}

pom.xml

testフェーズでLoad-Time Weavingを利用するようにpom.xmlを構成。 かなり面倒。

maven-dependency-plugin:build-classpathを利用してaspectjweaverのJarファイルのパスを組み立てて、maven-surefire-plugin:testに引き渡しているところがポイント。 maven-surefire-plugin:testでは、プロパテイを遅延評価する必要があるので@{ }を利用している。

aspectj-maven-plugin:compileを利用してaop-ajc.xmlを生成する。 aop-ajc.xmlには、aspectjweaverに認識させるアスペクトの定義情報が書かれている。 ただし、aspectj-maven-pluginのcomplianceLevelのデフォルトは1.4なので、明示的に1.5以上に設定しないと、アノテーションを利用したアスペクトコンパイルでエラーを起こす。 さらに、AspectJのバージョンがずれていると警告が出るので、aspectj-maven-pluginにaspectjtoolsへの依存を明示的に書いて、バージョンを合わせる。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>redj</groupId>
    <artifactId>aspectj</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <properties>
        <!-- maven-compiler-plugin -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <!-- versions of plugins -->
        <maven-dependency-plugin.version>3.0.0</maven-dependency-plugin.version>
        <aspectj-maven-plugin.version>1.10</aspectj-maven-plugin.version>
        <maven-surefire-plugin.version>2.20</maven-surefire-plugin.version>

        <!-- versions of dependencies -->
        <aspectj.version>1.8.10</aspectj.version>
        <junit.version>4.12</junit.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>${maven-dependency-plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-classpath</goal>    <!-- binds by default to generate-sources -->
                        </goals>
                        <configuration>
                            <outputProperty>aspectj-weaver-path</outputProperty>
                            <includeGroupIds>org.aspectj</includeGroupIds>
                            <includeArtifactIds>aspectjweaver</includeArtifactIds>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>${aspectj-maven-plugin.version}</version>
                <dependencies>
                    <dependency>    <!-- upgrade AspectJ -->
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>    <!-- binds by default to compile -->
                        </goals>
                        <configuration>
                            <complianceLevel>1.8</complianceLevel>
                            <outxml>true</outxml>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <executions>
                    <execution>
                        <id>default-test</id>
                        <goals>
                            <goal>test</goal>   <!-- binds by default to test -->
                        </goals>
                        <configuration>
                            <argLine>-javaagent:@{aspectj-weaver-path}</argLine>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

ビルド

テストメソッドの実行前にログが出力されていることが確認できる。

$ mvn clean install
(略)
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running redj.aspectj.LoggingAspectTest
***** begin test
test bar
***** begin test
test foo
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.153 s - in redj.aspectj.LoggingAspectTest
(略)

参考

Javaのシステムプロパティの一覧表示

過去に何度か同じ使い捨てのコードを書いたので、また必要になった時のために残しておく。

public class Main {

    public static void main(String[] args) {
        System.getProperties()
                .entrySet()
                .stream()
                .sorted((x, y) -> x.getKey().toString().compareTo(y.getKey().toString()))
                .forEach(entry -> System.out.println(entry.getKey() + " = " + entry.getValue()));
    }
}

出力例は以下。 line.separatorの次の行が空行になっているのは、line.separatorの値が改行であり、そのため改行を2連続で出力したため。

awt.toolkit = sun.awt.X11.XToolkit
file.encoding = UTF-8
file.encoding.pkg = sun.io
file.separator = /
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
java.awt.printerjob = sun.print.PSPrinterJob
java.class.path = target/classes/
java.class.version = 52.0
java.endorsed.dirs = /opt/java/jdk1.8.0_131/jre/lib/endorsed
java.ext.dirs = /opt/java/jdk1.8.0_131/jre/lib/ext:/usr/java/packages/lib/ext
java.home = /opt/java/jdk1.8.0_131/jre
java.io.tmpdir = /tmp
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.runtime.name = Java(TM) SE Runtime Environment
java.runtime.version = 1.8.0_131-b11
java.specification.name = Java Platform API Specification
java.specification.vendor = Oracle Corporation
java.specification.version = 1.8
java.vendor = Oracle Corporation
java.vendor.url = http://java.oracle.com/
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
java.version = 1.8.0_131
java.vm.info = mixed mode
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
java.vm.specification.name = Java Virtual Machine Specification
java.vm.specification.vendor = Oracle Corporation
java.vm.specification.version = 1.8
java.vm.vendor = Oracle Corporation
java.vm.version = 25.131-b11
line.separator = 

os.arch = amd64
os.name = Linux
os.version = 4.4.0-79-generic
path.separator = :
sun.arch.data.model = 64
sun.boot.class.path = /opt/java/jdk1.8.0_131/jre/lib/resources.jar:/opt/java/jdk1.8.0_131/jre/lib/rt.jar:/opt/java/jdk1.8.0_131/jre/lib/sunrsasign.jar:/opt/java/jdk1.8.0_131/jre/lib/jsse.jar:/opt/java/jdk1.8.0_131/jre/lib/jce.jar:/opt/java/jdk1.8.0_131/jre/lib/charsets.jar:/opt/java/jdk1.8.0_131/jre/lib/jfr.jar:/opt/java/jdk1.8.0_131/jre/classes
sun.boot.library.path = /opt/java/jdk1.8.0_131/jre/lib/amd64
sun.cpu.endian = little
sun.cpu.isalist = 
sun.desktop = gnome
sun.io.unicode.encoding = UnicodeLittle
sun.java.command = Main
sun.java.launcher = SUN_STANDARD
sun.jnu.encoding = UTF-8
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
sun.os.patch.level = unknown
user.country = JP
user.dir = /var/src/class-loader-introspector
user.home = /home/redj
user.language = ja
user.name = redj
user.timezone = 

Windows版のDocker Engineのリスト

Microsoftのドキュメント「Windows Containers on Windows Server」の手順では、OneGetを利用してDocker Engineを取得する。 OneGetを実行すると、Azure StorageからDocker Engineの実行バイナリを取得するようだ。 直接以下のAPIを実行すると、配置されているDocker Engineの一覧を取得することができる。

http://dockermsft.blob.core.windows.net/dockercontainer?resType=container&comp=list

執筆時点でのAPIの実行結果(自分で整形)は以下。 JSON形式では返せない模様。

<?xml version="1.0" encoding="utf-8"?>
<EnumerationResults ContainerName="http://dockermsft.blob.core.windows.net/dockercontainer">
  <Blobs>
    <Blob>
      <Name>DockerMsftIndex.json</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/DockerMsftIndex.json</Url>
      <Properties>
        <Last-Modified>Wed, 05 Apr 2017 22:38:24 GMT</Last-Modified>
        <Etag>0x8D47C74784ED01A</Etag>
        <Content-Length>5032</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>TAIVvNh/G6LpvBFeeohrWA==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-1-12-1-cs1.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-1-12-1-cs1.zip</Url>
      <Properties>
        <Last-Modified>Thu, 02 Mar 2017 19:33:49 GMT</Last-Modified>
        <Etag>0x8D461A30D591EF4</Etag>
        <Content-Length>13838077</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>PzDfmGUp5SqfRVy2/hofog==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-1-12-2-cs1.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-1-12-2-cs1.zip</Url>
      <Properties>
        <Last-Modified>Thu, 02 Mar 2017 19:43:54 GMT</Last-Modified>
        <Etag>0x8D461A475DDC980</Etag>
        <Content-Length>13863262</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>p6z/SiiR7R17iABzuNDMxg==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-1-12-2-cs2-ws-beta.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-1-12-2-cs2-ws-beta.zip</Url>
      <Properties>
        <Last-Modified>Wed, 12 Oct 2016 23:48:15 GMT</Last-Modified>
        <Etag>0x8D3F2FA3C4E334E</Etag>
        <Content-Length>14183015</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>2b14ZgGoGyBVrtkzwtd34w==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-1-13-0-rc3.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-1-13-0-rc3.zip</Url>
      <Properties>
        <Last-Modified>Thu, 02 Mar 2017 19:47:12 GMT</Last-Modified>
        <Etag>0x8D461A4EBCFFD99</Etag>
        <Content-Length>13911681</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>+OR6vKBZbU0aAzjme/hBtw==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-1-13-0-rc4.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-1-13-0-rc4.zip</Url>
      <Properties>
        <Last-Modified>Thu, 02 Mar 2017 19:50:29 GMT</Last-Modified>
        <Etag>0x8D461A56102B607</Etag>
        <Content-Length>13934374</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>8iZy1ZG44wwnF11+KOThIA==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-1-13-1.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-1-13-1.zip</Url>
      <Properties>
        <Last-Modified>Fri, 10 Feb 2017 02:51:11 GMT</Last-Modified>
        <Etag>0x8D4515FABC5013F</Etag>
        <Content-Length>14025912</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>dkVeRNm5LvDycVg7M2ucWw==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-17-03-0-ee.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-17-03-0-ee.zip</Url>
      <Properties>
        <Last-Modified>Thu, 02 Mar 2017 19:03:54 GMT</Last-Modified>
        <Etag>0x8D4619EDF782BE1</Etag>
        <Content-Length>14027010</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>U8846AI0qZef+mg8PbhxhA==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
    <Blob>
      <Name>docker-17-03-1-ee.zip</Name>
      <Url>http://dockermsft.blob.core.windows.net/dockercontainer/docker-17-03-1-ee.zip</Url>
      <Properties>
        <Last-Modified>Wed, 05 Apr 2017 22:34:08 GMT</Last-Modified>
        <Etag>0x8D47C73DFDE700A</Etag>
        <Content-Length>14029894</Content-Length>
        <Content-Type>application/octet-stream</Content-Type>
        <Content-Encoding/>
        <Content-Language/>
        <Content-MD5>OJHI8eUJ9OR0oxEktszJJQ==</Content-MD5>
        <Cache-Control/>
        <BlobType>BlockBlob</BlobType>
        <LeaseStatus>unlocked</LeaseStatus>
      </Properties>
    </Blob>
  </Blobs>
  <NextMarker/>
</EnumerationResults>

参考

Blob Service REST API - List Containers

JSONの整形

はじめに

LinuxでのJSONの整形方法をメモ。

コマンドjqを利用

$ cat in.json | jq

Ubuntuでjqをインストールする場合は以下。

$ sudo apt-get install jq

CentOSでjqをインストールする場合は以下。

$ sudo yum install jq

Pythonのモジュールを利用

$ cat in.json | python -m json.tool

etcdでよく利用するAPI

はじめに

自分がよく利用するetcdのAPIをメモしておく。

キーの値を取得

キーがfoo/barの場合。

curl http://localhost:2379/v2/keys/foo/bar

キーの値を再帰的に取得

キーがfoo/barの場合。

curl http://localhost:2379/v2/keys/foo/bar/?recursive=true

Vagrantを利用して同じ環境のVMを複数作成

以下のようにループを利用して必要な台数分の定義を生成するVagrantfileを準備すればよい。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  MAX_VM_COUNT = 3
  
  (0 ... MAX_VM_COUNT).each do |counter|
    config.vm.define "node#{counter}" do |server|
      server.vm.box = "willyhu/ubuntu-16.04-server-amd64"
      server.vm.network "private_network", ip: "192.168.10.#{counter+100}", netmask: "255.255.255.0"
    end
  end
end

ここでは、node0 〜 node2の3台のVMを作成し、それぞれにIPアドレス192.168.10.100 〜 192.168.10.102を割り当てている。

Ubuntu 16.04で最新のAnsibleをインストール

Ubuntu 16.04の標準のリポジトリからapt-getで取得できるAnsibleは若干古い。 最新版をインストールするには、PPAのAnsibleのリポジトリを追加してapt-getすればよい。

$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

参考

Ansible Document - Instllation