tokuhirom's Blog

pthread のバージョンを確認したいでござる

pthread で NPTL が使われてるかどうか確認したかったのだけど、ただしく確認する方法がよくわからなかったので confstr で調べた。

#include <malloc.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    char *buf;
    size_t n;

    n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, (size_t) 0);
    buf = malloc(n);
    if (buf == NULL) {
        printf("Cannot allocate memory\n");
        return;
    }
    confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, n);
    printf("%s\n", buf);
}

昔の LinuxThreads では SIGUSR1, SIGUSR2 を使っていたりしていてユーザーが SIGUSER[12] 使えないなどの問題があったために、Linux 2.6 以後は NPTL に置き換えられており、もはや動作しているシステムでは殆どの場合、NPTL であると仮定してしまってよいように思う。とくに、新規で何かを作る場合、あえて LinuxThreads をサポートする必要はない。のだと思う。

h2o の fastcgi サポートで Plack::Handler::FCGI を動かす

h2o で PHP を FCGI で動かすという話題が最近見かけますが、もちろん h2o+FCGI+Perl も快適に動作します。 具体的には以下のように書けばよろしい。

listen: 9090
hosts:
  default:
    paths:
      /:
        file.dir: /path/to/htdocs/
        fastcgi.spawn: "exec /opt/perl-5.18/bin/plackup -s FCGI --nproc 10 /path/to/app/"

簡単すぎて、とくに解説することがないです。

h2o+FCGI で運用した場合のメリットとして、Starlet 等の application server を立ち上げる場合と異なり、application server のプロセスを daemon tools や systemctl 等で管理する必要がないというメリットがある。

一方で、TCP+HTTP/1.1 でやっていれば通信を見るのが簡単なのに、STDIN で通信する FCGI では通信経路を観測するのが難しいので、そのへんをどう見るか、というところでしょうか。

jps を自前で実装してみる

tools.jar の中に入ってるクラスを呼べば、同じようなことができるとのこと。

import java.net.URISyntaxException;
import java.util.Set;

import sun.jvmstat.monitor.HostIdentifier;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;

public class MyJps {
	public static void main(String[] args) throws URISyntaxException, MonitorException {
		String hostname = null;
		final MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(new HostIdentifier(hostname));
		final Set<Integer> lvmids = monitoredHost.activeVms();
		for (Integer lvmid: lvmids) {
			String vmidString = "//" + lvmid + "?mode=r";
			final VmIdentifier vmIdentifier = new VmIdentifier(vmidString);
			final MonitoredVm vm = monitoredHost.getMonitoredVm(vmIdentifier);
			final String mainClass = MonitoredVmUtil.mainClass(vm, true);
			System.out.printf("%d %s\n", lvmid, mainClass);
		}
	}
}

My latest hack around Server::Starter

In past few days, I sent some p-r to Sever-Stater. This is my note around these commits.

Added --stop option #28

I added --stop option. You can stop server process by this option. Then, we can write deployment script easier.

Do not close STDIN if fd=0 was duplicated. #29

Few days ago, I added --port=$PORT=$FD option. But it doesn't work with --daemon option. Because --daemon option closes STDIN after duplicate fd.

In this p-r, --daemon option don't close STDIN if file descriptor zero is duplicated fd.

Do not unlink status_file if the process doesn't create it. #32

https://github.com/kazuho/p5-Server-Starter/pull/32

server::starter unlink the status-file before exit. It remove another server-stater process' status file.

This issue caused by user's miss operation, but it's serious issue.

In this case, 2nd step unlinks foo.status. As a result, 3rd step wouldn't work.

Java の AST を解析できる javaparser がアツい!!!

https://github.com/javaparser/javaparser

javaparser は Java をパースして AST にしてくれるライブラリである。

この手のライブラリは数多あるのだが、ほとんどのものが Java 1.5 ぐらいでメンテナンスが止まっている。 実際このライブラリもメンテナンスが止まっていたのだが、Java 1.8 対応版とし開発が再開されたものだ。

このライブラリはパーサーライブラリであるから、文字列をパースして AST を構築してくれるというものになっている。

実際どのような AST が構築されるのかが気になるところなので、構築された AST をダンプできるツールを groovy で書いた。

#!/usr/bin/env groovy
@Grab('com.github.javaparser:javaparser-core:2.1.0')

import java.io.FileInputStream;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;

class Dumper {
  int indent;

  def Dumper() {
    this.indent = 0
  }

  def dumpit(CompilationUnit node) {
    dumpit((Node)node)
    print("\n")
  }
  def dumpit(Node node) {
    print("\n")
    print(" " * indent)
    print(node.getClass().getSimpleName() + ":" + asMap(node));

    this.indent++
    for (Node child: node.getChildrenNodes()) {
      dumpit(child)
    }
    this.indent--
  }

  public Map asMap(o) {
    o.class.declaredFields.findAll { it.modifiers == java.lang.reflect.Modifier.PRIVATE }.
      collectEntries { [it.name, o[it.name]] }
  }
}


def parse(InputStream is) {
  CompilationUnit cu = JavaParser.parse(is);
  new Dumper().dumpit(cu);
}

if (args.length == 0) {
  parse(System.in);
} else {
  FileInputStream fis = new FileInputStream(args[0]);
  try {
    parse(fis);
  } finally {
    fis.close();
  }
}

これを実行すると以下のような結果が得られる。

Java 8 で導入されたラムダ式もちゃんとパースできていることがわかる。

CompilationUnit:[pakage:null, imports:null, types:[public class A {

    public void x() {
        Arrays.stream().map( it->it * 2);
    }
}]]
 ClassOrInterfaceDeclaration:[interface_:false, typeParameters:null, extendsList:null, implementsList:null, javadocComment:null]
  MethodDeclaration:[modifiers:1, typeParameters:null, type:void, name:x, parameters:[], arrayCount:0, throws_:[], body:{
    Arrays.stream().map( it->it * 2);
}, isDefault:false, javadocComment:null]
   VoidType:[:]
   BlockStmt:[stmts:[Arrays.stream().map( it->it * 2);]]
    ExpressionStmt:[expr:Arrays.stream().map( it->it * 2)]
     MethodCallExpr:[scope:Arrays.stream(), typeArgs:null, name:map, args:[ it->it * 2]]
      MethodCallExpr:[scope:Arrays, typeArgs:null, name:stream, args:null]
       NameExpr:[name:Arrays]
      LambdaExpr:[parameters:[ it], parametersEnclosed:false, body:it * 2;]
       Parameter:[type:, isVarArgs:false]
        VariableDeclaratorId:[name:it, arrayCount:0]
        UnknownType:[:]
       ExpressionStmt:[expr:it * 2]
        BinaryExpr:[left:it, right:2, op:times]
         NameExpr:[name:it]
         IntegerLiteralExpr:[:]

さて、これで、どのあたりにどのノードがあって、どのようなアトリビュートを取得可能かが一目瞭然となったので、ビジターを書いて解析してみる。

javaparser では AST をトラバースするためのビジタークラスが用意されているので、これを継承し、処理したいノードを捕まえればよろしい。

例として、クラスとメソッドのリストを出力するコードを書いた。注意すべき点は特に無いが、super.visit を呼び忘れると下位ノードにビジターが回らないので注意。

#!/usr/bin/env groovy
@Grab('com.github.javaparser:javaparser-core:2.1.0')

import java.io.FileInputStream;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.visitor.*;

class MethodVisitor extends VoidVisitorAdapter<Object> {
    @Override
    public void visit(final ClassOrInterfaceDeclaration n, final Object arg) {
        System.out.println(n.getName());
        super.visit(n, arg)
    }

    @Override
    public void visit(MethodDeclaration n, Object arg) {
        System.out.println('  ' + n.getName());
        super.visit(n, arg)
    }
}

def parse(InputStream is) {
  CompilationUnit cu = JavaParser.parse(is);
  new MethodVisitor().visit(cu, null);
}

if (args.length == 0) {
  parse(System.in);
} else {
  FileInputStream fis = new FileInputStream(args[0]);
  try {
    parse(fis);
  } finally {
    fis.close();
  }
}

さて、ここまで見てくると、javafmt 的なコマンドが欲しくなってくると思う。 javafmt 的なものを実装するにはどうすればよいか。

答えは「何もしなくてよい」である。Node クラスの .toString() メソッドが、そもそも適当にインデントつけてそれなりの見た目で 出力してくれる。Node#toString の実装は、com.github.javaparser.ast.visitor.DumpVisitor であり、これをベースに調整していけば、簡単に好みのフォーマッタを構築できることだろう。

@Grab('com.github.javaparser:javaparser-core:2.1.0')

import java.io.FileInputStream;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;

def parse(InputStream is) {
  CompilationUnit cu = JavaParser.parse(is);
  // prints the resulting compilation unit to default system output
  System.out.println(cu.toString());
}

if (args.length == 0) {
  parse(System.in);
} else {
  FileInputStream fis = new FileInputStream(args[1]);
  try {
    parse(fis);
  } finally {
    fis.close();
  }
}

AST は書き換え可能なので、AST をいじってから書き戻すとか、javapoet 的にコード生成に使うとか、いろいろできそうです。

以上、簡単ですが javaparser の紹介とさせていただきます。

supplementary groups をサポートする setuidgid であるところの setusergroups.c 書いた

https://github.com/tokuhirom/setusergroups/

supplementary groups をサポートした setuidgid が欲しいケースがある。 というのもあると思うけど、とにかく依存がない setuidgid が欲しい、みたいなケースもあると思う。

1ファイルの C コードで動くような。

なんかそういうユースケースがそれなりにあるような気がしていたので、まあ書いた。

元となっているのは kazuho さんの perl code で、 https://gist.github.com/kazuho/6181648 です。 これは、簡潔な perl code でよいのだが、Unix::Groups ってやつが rpm package になってなかったりしてめんどかった。

setuidgid は daemontools のいろんなヘッダに依存しててめんどかった。

まあ、そんなところです。

Paths.get(uri) について

Paths.get(uri) で Path オブジェクトを取得可能だが、これはどんな URI をサポートしているのかという話。

結論からいうと file:// 以外での利用は微妙。

file:// 以外の場合、インストール済みのファイルシステムプロバイダの中からサポートしているものを利用することになる。サポートしてるものがなければなにも起きない。

FileSystemProvider.installedProviders() でインストール済みのプロバイダは取得できる。 特に何も jar を読んでいなければ、これは以下のようなものが入っている。

sun.nio.fs.MacOSXFileSystemProvider@4f2410ac
com.sun.nio.zipfs.ZipFileSystemProvider@722c41f4

前者は普通の file system からの読み込みに使うやつで、後者は zip からの読み込み。 zip からの読み込みってことは、zip ファイルの読み込みつまり jar からの読み込みに対応しているのかな?と思いきやこれがそうではない。

jar:file:/Users/tokuhirom/.m2/repository/junit/junit/4.11/junit-4.11.jar!/LICENSE.txt

のような形式をサポートしていることは確かなのだが、実際には ZipFileSystemProvider#newFileSystem であらかじめ jar file を登録しておかないとダメなのでまったく使い物にはならない。

FileSystemProvider の static にデフォルトで導入されている provider はグローバル変数なので、ここにわざわざ導入すると removeFileSystem でまたはずさなくてはならず、あまりやりたくない。

ZipFileSystemProvider 以外でも、サービスローダで登録することによって任意の URL を読み込み可能ではあるが、FileSystemProvider というグローバルなオブジェクトに対してサービスローダでガンガン追加していくというのもあんまり好ましくないように思う。

といったところで、個人的には Paths.get(uri)file:// 以外では利用しないほうが良いように思う。

gradle で maven central にあげる

ちょっと前に gradle で maven central にあげようとしたら極めて困難な印象で挫折していたのだが、最近試したらすんなりといったのでご報告申し上げます。

単に gradle が難しいというよりは、maven central 等の toolchain 的な部分に関する知識がだいたいわかったので、すんなりわかったという点も多いんですが。

まず、gradle に maven central にアップロードする方法は、実は公式サイトにまとめられています。 http://central.sonatype.org/pages/gradle.html

基本的にはこの通りに設定すればいいのだが、このドキュメント、gnupg とかについてはすでに設定済みだ、というような前提に基づいているので注意が必要。

アカウントの取得等についてはモリスさんのエントリを読めばよろしい。 http://tagomoris.hatenablog.com/entry/20141028/1414485679

で、GPG 鍵の設定についてはこのへん参考にすればよい。 http://int128.hatenablog.com/entry/20130409/1365434513

で、gradle の設定については、基本的には gradle init と打てばなんとなくマイグレーションが行われる。gradle init しても maven central 関連のデータが出来上がるわけではまったくないことに注意すべき。

必要な設定については http://central.sonatype.org/pages/gradle.html の公式ドキュメントに載っている通りなのだが、いくつか変更してる。

具体的には

といったところを変更している。

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'signing'
apply plugin: 'findbugs'
apply plugin: 'checkstyle'

group = 'me.geso'
archivesBaseName = "regexp-trie"
version = '0.1.11-SNAPSHOT'

description = """regexp-trie"""

[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
sourceCompatibility = 1.7
targetCompatibility = 1.7

repositories {
     maven { url "http://repo.maven.apache.org/maven2" }
}

dependencies {
    testCompile group: 'junit', name: 'junit', version:'4.11'
}

findbugsMain {
  ignoreFailures = false
  effort = "max"
}

task javadocJar(type: Jar) {
    classifier = 'javadoc'
    from javadoc
}

task sourcesJar(type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

artifacts {
    archives javadocJar, sourcesJar
}

signing {
    sign configurations.archives
}

try {
  uploadArchives {
    repositories {
      mavenDeployer {
        beforeDeployment {
          MavenDeployment deployment -> signing.signPom(deployment)
        }

        repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
          authentication(userName: ossrhUsername, password: ossrhPassword)
        }

        snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
          authentication(userName: ossrhUsername, password: ossrhPassword)
        }

        pom.project {
          name 'regexp-trie'
          packaging 'jar'
          // optionally artifactId can be defined here 
          description 'Trie based regexp generator'
          url 'http://github.com/tokuhirom/regexp-trie/'

          scm {
            url 'https://github.com/tokuhirom/regexp-trie/'
            connection 'scm:git:git://github.com/tokuhirom/regexp-trie.git'
            developerConnection 'scm:git:[email protected]:tokuhirom/regexp-trie.git'
          }

          licenses {
            license {
              name 'MIT License'
              url 'http://www.opensource.org/licenses/mit-license.php'
            }
          }

          developers {
            developer {
              id 'tokuhirom'
              name 'Tokuhiro Matsuno'
              email '[email protected]'
            }
          }
        }
      }
    }
  }
} catch (MissingPropertyException mpe) {
  if (System.env['CI']) {
    println("Run on CI");
  } else {
    throw mpe;
  }
}

ossrhPassword などの設定は、~/.gradle/gradle.properties に書く。

org.gradle.daemon=true

signing.keyId=MY_KEY_ID
signing.password=GPG_KEY_PASSPHRASE
signing.secretKeyRingFile=/path/to/.gnupg/secring.gpg

ossrhUsername=MY_JIRA_ID
ossrhPassword=MY_JIRA_PASSWORD

checkstyle の設定は config/checkstyle/checkstyle.xml に置く。 このルールは、普段使っているルールからパッケージ名の宣言等を抜いたもの。

<property name="severity" value="error"/>

とすることにより、すべてのルールをエラーレベルにしている。 開発時にコーディングルールを順守しないコードをマージしてもろくなことがないので、エラーレベルにしておくのが良い。

そして、travis ci の設定もカエル。簡単。travis の設定としてはほぼ一緒だが gradlew を実行しているところが違う。 また、teppeis さんのエントリを参考に、sudo を false にしている。 このフラグ変更により、コンテナで実行されるようになり、圧倒的に早く処理が開始されるようになる。 また、~/.gradle/ をキャッシュする設定をいれることにより、gradlew が毎回ダウンロードしなくなるので実行が速くなる。

language: java
sudo: false
jdk:
  - oraclejdk8
install: ./gradlew check
cache:
  directories:
    - $HOME/.gradle/

ちょっと前に調べた時は、maven central にあげるための workaround みたいなものを無限にしろ! って書いてある記事が多くて萎えてやめたのだが、今や普通に使えるようだ。

2015-04-30 追記

正直、ウェブアプリの開発では、開発ツールなどを invoke するために gradle にすることに意味があるが、一般的な java library の開発では maven で十分だと思う。

jetty で vhost でいい感じにアプリケーション動かしていく

jetty で vhost 使う方法みたいな話が、意外と情報ない。

例えば、web 画面と admin 画面があるようなサイトで、実際には vhost を利用して運用したいという場合、一般的な ide や servlet container の想定では PATH_INFO でアプリケーションを切り替えて頑張る、みたいなのが多いのだが、こういったアプローチはあまり現代的ではないと思う。

named vhost でやりたいよ!!

最近、以下のような構造のでアプリケーションを構築している。

- pom.xml
+ common ← jar
  - pom.xml
+ admin ← war
  - pom.xml
  - src/main/webapp/WEB-INF/web.xml
+ web   ← war
  - pom.xml
  - src/main/webapp/WEB-INF/web.xml

このような状況で、local で開発したいというシナリオだと思いねえ。

ってわけで、jetty でやるには、vhost を設定したWebAppContext を HandlerCollection に詰め込んでハンドラとして設定すればよろしい。

private Server startServer(int port) throws Exception {
	Server server = new Server(port);
	HandlerCollection handler = new HandlerCollection();
	for (final String module : Arrays.asList("web", "admin")) {
		handler.addHandler(createContext(module));
	}
	server.setHandler(handler);
	server.start();
	return server;
}

それで、WebAppContext は以下のような形で作れば、IntelliJ から起動する分には意外といい感じに動く。mvn exec:java からだとなんかうまくイカなくて困ってるけど。。本当は war を作って explode したほうが正しいが、そのへん工夫してる暇ないのでこういう感じになっている。

private WebAppContext createContext(String module) throws Exception {
	String descriptorPath = appBase + "/" + module + "/src/main/webapp/WEB-INF/web.xml";
	if (!new File(descriptorPath).exists()) {
		log.error("There is no {}", descriptorPath);
		throw new RuntimeException("Invalid descriptor path: " + descriptorPath);
	}

	String resourcePath = appBase + "/" + module + "/src/main/webapp/";
	if (!new File(resourcePath).exists()) {
		log.error("There is no {}", resourcePath);
		throw new RuntimeException("Invalid resourcePath " + resourcePath);
	}

	String hostname = module + "-proj.example.com";

	log.info("Starting {} server: {}, {}, {}", module, descriptorPath, resourcePath, hostname);

	WebAppContext context = new WebAppContext();
	context.setDescriptor(descriptorPath);
	context.setResourceBase(resourcePath);
	context.setContextPath("/");
	context.setParentLoaderPriority(true);
	context.setVirtualHosts(new String[] {hostname});
	return context;
}

mvn の exec:java が終わるまでの時間が15秒ぐらい待ってて遅いってとき

daemon thread の終了を待ってるせいで遅いっていう話があるんで雑に殺せば良いっていう気分のときは以下のオプションを与えればよい。

-Dexec.cleanupDaemonThreads=false

ref. http://stackoverflow.com/questions/13471519/running-daemon-with-exec-maven-plugin

maven で特定の module だけ処理させたくない。

たとえば maven で multi module でプロジェクトを構築していて、開発用ツールが入っている module があるとする(localdev や devtools などという名前だったりする)。

localdev や devtools などといったものは、nexus enterprise に deploy はしたくないので、処理対象から除外させてやりたい。

maven 3.2.1 以後であれば mvn -pl '!localdev' deploy というように、特定のモジュールを除外してビルドさせることができる。 うちの場合、Jenkins で deploy 処理を行っているので、jenkins の設定に -pl '!localdev' のように記述すればよい。

ref. http://stackoverflow.com/q/8304110/756865

[java] モダンな Annocation Processor の開発手順まとめ

APT とは Annotation Processing Tool のことで、Java でコードの自動生成を行う際に利用される。 APT を利用すると、Java クラスやリソースの自動生成が可能となる。

インターネットに情報は結構あるのだが、昔のものが多くて、Eclipse に JAR を追加して云々とかそういう感じのものが多くて辛いので調べたことをまとめておく。

アノテーションを作る

適当なアノテーションを作る。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target({
        ElementType.TYPE
})
public @interface Hello {
}

アノテーションプロセッサを実装する

AbstractProcessor を implement する。@SupportedAnnotationTypes で、処理対象のアノテーションを指定。@SupportedSourceVersion で、ソースのバージョンを指定。

ソースの生成には、テキスト処理用の template engine を利用してもよいが、いろいろやるなら javapoet などの Java コード生成用 DSL を利用したほうが、インデントや空行なども綺麗に揃うし、良い。

process() の引数にある RoundEnvironment を扱うための便利メソッドが google auto-common に入っているから、参考にするとよい。

package me.geso.sample.hello;

import java.io.IOException;
import java.io.Writer;
import java.util.Set;

import javax.annotation.Generated;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;

import com.google.auto.common.MoreElements;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("me.geso.sample.hello.*")
public class MyProcessor extends AbstractProcessor {
	@Override
	public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
		if (annotations.isEmpty()) {
			System.out.println("no annotations");
			return true;
		}
		log("HUAAAAAAAAAAAAAAAAAAAAA");

		final Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Hello.class);
		log(elements.toString());
		for (final Element element : elements) {
			final PackageElement aPackage = MoreElements.getPackage(element);
			final TypeSpec blah = TypeSpec.classBuilder("Blah")
				.addAnnotation(AnnotationSpec.builder(Generated.class)
					.addMember("value", "{$S}", getClass().getCanonicalName())
					.build())
				.addModifiers(Modifier.PUBLIC)
				.addMethod(MethodSpec.methodBuilder("hello")
					.addModifiers(Modifier.PUBLIC)
					.addCode(
						CodeBlock.builder()
							.add("return \"hello\";\n")
							.build()
					)
					.returns(TypeName.get(String.class))
					.build()
				).build();
			JavaFile javaFile = JavaFile.builder(aPackage.getQualifiedName().toString(), blah)
				.build();

			try {
				JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(aPackage.toString() + ".Blah");
				Writer writer = sourceFile.openWriter();
				javaFile.writeTo(writer);
				writer.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

		return true;
	}

	private void log(String msg) {
		if (processingEnv.getOptions().containsKey("debug")) {
			processingEnv.getMessager().printMessage(Kind.NOTE, msg);
		}
	}

}

src/main/resources/META-INF/services/javax.annotation.processing.Processor

src/main/resources/META-INF/services/javax.annotation.processing.Processor というファイルに、アノテーションプロセッサのクラス名を記述する。

me.geso.sample.hello.MyProcessor

maven pom.xml の設定

Annotation Processor 自体のコンパイル時に APT 有効にしていると、Annotation Processor 自体のコンパイルに Annotation Processor 使おうとしてわけわかめになるということがあるので、無効にしたほうがいいかもしれない(IntelliJ だけかも)。

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<!-- disable annotation processing -->
					<compilerArgument>-proc:none</compilerArgument>
				</configuration>
			</plugin>
		</plugins>
	</build>

テストコード書く

検索すると AptinaUnit 使えって書いてあるものが多いのだが、今どきは google compile-testing を使うとよさそう。 APT に限らず、コードコンパイルして云々するもの全般で使える。 開発もアクティブだし。

https://github.com/google/compile-testing

書き方は、javadoc か test code か google auto のコードを参考にする。

コンパイラオプションの指定をしたいところだが、現行バージョンでは指定できない。 実装はされててリリース待なので今後に期待。 https://github.com/google/compile-testing/pull/64

package me.geso.sample.hello;

import static com.google.common.truth.Truth.assert_;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;

import org.junit.Test;

import com.google.common.io.Resources;
import com.google.testing.compile.JavaFileObjects;

public class ProcessorTest {

	@Test
	public void testProcess() throws Exception {
		// Compiler option coming soon.
		// https://github.com/google/compile-testing/pull/64

		assert_().about(javaSource())
			.that(JavaFileObjects.forResource(Resources.getResource("HelloWorld.java")))
			.processedWith(new MyProcessor())
			.compilesWithoutError()
			.and()
			.generatesSources(JavaFileObjects.forSourceString("foo.bar.baz.Blah", "package foo.bar.baz;\n"
					+ "\n"
					+ "import java.lang.String;\n"
					+ "import javax.annotation.Generated;\n"
					+ "\n"
					+ "@Generated({\"me.geso.sample.hello.MyProcessor\"})\n"
					+ "public class Blah {\n"
					+ "  public String hello() {\n"
					+ "    return \"hello\";\n"
					+ "  }\n"
					+ "}"));
	}
}

参考になるコード

google auto

https://github.com/google/auto/

google auto は APT 関連のサンプルとしてよい。

auto commmon は、APT 用のユーティリティクラスとして便利。

APT の実際の使い道

コンパイル時に計算するとか、ボイラープレートコードの生成とかには役立つが、実際、既存のコードを変更できるわけではないので、できることは意外と少ない。

例えば、Dagger2 のようなものにはよい。DI 対象がコンパイル時に確定するので、実行時の速度を抑えることができる。Android 等の始動速度が必要なことが有用なテクニックとなる。

生成されたコードは target/generated-sources/ を見れば確認できるので、実際の動作が想像しやすい。デバッグもしやすくなる。JVM bytecode の知識がなくても修正可能だし、デバッグ等もやりやすい。

lombok のような AST を処理するものよりはよほどやりやすい。

参考資料

http://www.slideshare.net/vvakame/apt-7568357

routes が /{id:[0-9]{5}}/{title:[a-zA-Z_]+} のような形式に対応していた

Java 用の simple な dispatcher ライブラリである routes の 0.5.0 が出ました。

今回のバージョンでは @moznion からのパッチにより、より柔軟なディスパッチングルールの定義が可能になります。

avans で利用する場合は以下のような形式で利用できるようになります。

@GET("/{id:[0-9]{5}}/{title:[a-zA-Z_]+}")
public void foo(@PathParam("id") long id, @PathParam("title") String title) {
    ...
}

変更点はこの辺りです。 https://github.com/tokuhirom/routes/commit/869dd5796562a09a567d7b1f57a9c6c3c73b6ac7

以上です。よろしくお願いします。

google guice の依存関係を visualize したいよ、って人

https://github.com/google/guice/wiki/Grapher

以下のように実装すればよい。graphviz 用の .dot ファイルが生成される。

PrintWriter out = new PrintWriter(new File(filename), "UTF-8");

Injector injector = Guice.createInjector(new GraphvizModule());
GraphvizGrapher grapher = injector.getInstance(GraphvizGrapher.class);
grapher.setOut(out);
grapher.setRankdir("TB");
grapher.graph(demoInjector);
out.close();

以下のように、png を生成する。

dot -T png my_injector.dot > my_injector.png

JMX にアクセスして値を取得したい

Java で何かを実装したい場合、Tomcat ちゃんやらなんやらの remote のプロセスからデータを取得したいという欲求を覚えることでしょう。

そういう時の取得方法についてサンプルプログラムを以下に貼ります。

以下のような感じで書くと、オブジェクトのリストを得ることができます。 remote.queryMBeans(null, null) と呼ぶとオブジェクトリストがとれるんですね。このへんはググれば出てきます。

import javax.management.remote.{JMXConnectorFactory, JMXServiceURL}

import scala.collection.JavaConverters._

object JMXListItems {
    def main(argv: Array[String]) = {
        if (argv.length != 1) {
            println("Options: jmx_url")
            System.exit(0)
        }

        val url = argv(0)
        val target = new JMXServiceURL(url)
        val connector = JMXConnectorFactory.connect(target)
        val remote = connector.getMBeanServerConnection
        val objectNames = remote.queryMBeans(null, null)
        objectNames.asScala foreach { it =>
            printf("%s %s\n", it.getClassName, it.getObjectName.getCanonicalName)
        }
    }
}

ほんで、実際の値の取得はこんな感じです。 MBeanServerConnection#getAttributes() というメソッドがあるのでこれを利用すればよいように見えるが、一部の MBean は Serialize 不能な値を返してくる……。そういう場合には getAttributes メソッドが例外を上げてくるので、まったく値を取れない。 そこで、現実的には一個一個例外をキャッチしつつ getAttribute するしかないのかな、と思っている。 が、そんなバカバカしい状態が Java SE でずっと放置されているわけもないと思うので、実はもっとちゃんとした方法があるのかもしれない!

import javax.management.ObjectName
import javax.management.remote.{JMXConnectorFactory, JMXServiceURL}

object JMXGetAttributes {
    def main(argv: Array[String]): Unit = {
        if (argv.length != 2) {
            println("Options: jmx_url object_canonical_name")
            System.exit(0)
        }

        val url = argv(0)
        val objectCanonicalName = argv(1)
        val target = new JMXServiceURL(url)
        val connector = JMXConnectorFactory.connect(target)
        try {
            val remote = connector.getMBeanServerConnection
            val name = ObjectName.getInstance(objectCanonicalName)
            remote.getMBeanInfo(name).getAttributes foreach { attribute =>
                val value = try {
                    remote.getAttribute(name, attribute.getName).toString
                } catch {
                    case e:Exception => "Unavailable"
                }
                printf("%s %s\n", attribute.getName, value)
            }
        } finally {
            connector.close()
        }
    }
}

ちなみに MBeanServerConnection は内部でスレッドを上げてくるので、ちゃんと close しないとすぐに Out Of Memory になるので注意が必要。

あと、サンプルコードは scala です。

avans での Freemarker の取り扱いを変えた。

既存コードには関係ない話ですが、今朝リリースした 1.5.3 のスケルトンジェネレータでの変更についてです。

avans ではいままで avans-freemarker を利用していましたが、これが将来的に Deprecate されて、新しい方法が採用されます。

今までのスケルトンでは Freemarker 関連のコードがゴチャゴチャしがちでしたが、これらを src/main/java/${pkg}/view/ 以下に配置するように変更。view 関連のコードをこのあたりにまとめるようになっています。

のようなクラスが生成されるようになっています。

この変更により、以下のようにすんなりとコントローラコードを記述することが可能になっています。またビュー関連のコードが一箇所にまとまって管理しやすくあんっております。

public class RootController extends BaseController {
    @GET("/")
    public WebResponse index() throws IOException, TemplateException {
        return this.freemarker("index.html.ftl")
                .param("name", "John<>")
                .render();
    }
}

以前のバージョンでは ImmutableMap を使ったりしていてダルい感じだったけど、イカス感じになった!!

また、Helper クラスが最初から用意されるようになっています。これは Amon2 で言うところの ViewFunctions にあたるものです。 これを定義しておけば、テンプレートから呼び出したい関数を好き勝手に呼べるようになっています。便利。

まあそういう感じですのでよろしくお願いします。

apimock 0.2.0 を出した

https://github.com/tokuhirom/apimock/

外部の API を呼ぶときにそのモックをつくるやつの 0.2.0 を出した。

外部の API を呼ぶようなコードを、呼び出し部分のクラスをモッキングしてもいいのだが、それよりも API サーバーそのものをモッキングしたほうが、より広い範囲のテストを同時に行うことができて良い。

というわけで、最近は Embedded Tomcat を使って実際にサーバーをたちあげてテストしている。 想像されるほど遅くはないのでよい。

基本的な使い方は以下のようになっており、mock#get と mock#post が用意されていて、ここにエンドポイントを登録してモッキングする。webscrew のレスポンス型以外のオブジェクトを返すと Jackson で適当に JSON にしてくれるので便利だ。

try (APIMockTomcat mock = new APIMockTomcat()) {
    mock.get("/", c -> Collections.emptyMap());
    mock.start();
    URI uri = mock.getURI();

    // Your test code here.
    HttpResponse resp = Request.Get(uri.resolve("/"))
            .execute()
            .returnResponse();
    assertEquals(200, resp.getStatusLine().getStatusCode());
    assertEquals("{}", EntityUtils.toString(resp.getEntity()));
}

で、便利に使っていたのだが、今までは APIMockContext#readJSON のようなメソッドがなくて、出力はいいけど入力が辛い、ということになっていたので直した。

mock.get("/", c -> {
    V v = c.readJSON(V.class);
    return ImmutableMap.builder()
        .put("x", v.getX()*2)
        .build();
});
mock.start();

まあ、それだけですが。

最近の Web 開発は、外部 API との通信を行うケースが増えているので、こういうライブラリの需要が高まっていると思う今日このごろです(Perl でやってると fork しなきゃいけないのでちょっと管理が面倒だったけど Java だと thread なので楽でいい)。

ちなみに、実際にはこういうふうなのを毎回書くわけではなくて以下の様なヘルパメソッドを用意している。

protected void mockTwitter(Consumer<APIMockTomcat> server, Consumer<Mech2WithURI> client) {
    try (APIMockTomcat mock = new APIMockTomcat()) {
        server.accept(mock);
        mock.start();
        URI uri = mock.getURI();

        Mech2WithURI mech2 = new Mech2WithURI(new Mech2(), uri);
        client.accept(mech2);
    }
}

JavaMail をモッキングしてテストしたい。

Java でメールを送るなら JavaMail で送るのが普通らしい。

とはいえ、生の JavaMail で書いてるとダルいので適宜以下のようにラッパをかましたりする。 本文は Freemarker で処理する、的な。Fluent interface っぽくてクール。

本当はいぜん Perl で書いていたような、Template file 側に Subject 入れられるようなのを書きたいが、書く時間がない。 誰か書くのを待つか、だれかが教えてくれるのをじっと待とう。

public class OreMailer {
    private final MimeMessage message;
    private final Configuration freemarkerConfiguration;

    public OreMailer(Class<?> klass) throws IOException {
        Properties properties = System.getProperties();
        if (properties.getProperty("mail.smtp.host") == null) {
            properties.setProperty("mail.smtp.host", "localhost");
        }
        Session session = Session.getDefaultInstance(properties);

        this.message = new MimeMessage(session);
        this.freemarkerConfiguration = this.buildFreemarkerConfiguration(klass);
    }

    public MimeMessage getMessage() {
        return this.message;
    }

    public void send() throws MessagingException {
        Transport.send(this.message);
    }

    public OreMailer to(String... addresses) throws MessagingException {
        for (String address : addresses) {
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
        }
        return this;
    }

    public OreMailer from(String address) throws MessagingException {
        message.setFrom(address);
        return this;
    }

    public OreMailer renderBodyWithFreemarker(String tmplName, Object dataModel) throws IOException, TemplateException,
            MessagingException {
        Template template = this.freemarkerConfiguration.getTemplate(tmplName);
        final StringWriter writer = new StringWriter();
        template.process(dataModel, writer);
        final String bodyString = writer.toString();
        this.message.setText(bodyString, "UTF-8");
        return this;
    }

    /**
    * Create new Freemarker configuration.
    */
    private Configuration buildFreemarkerConfiguration(Class<?> klass) throws IOException {
        final Configuration cfg = new Configuration();
        cfg.setClassForTemplateLoading(klass, "/somailer");
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setIncompatibleImprovements(new Version(2, 3, 20)); // FreeMarker
        return cfg;
    }

    public OreMailer subject(String s) throws MessagingException {
        this.message.setSubject(s, "UTF-8");
        return this;
    }
}

で、テストどうするかってことなんだけど、mock-javamail ってのを使うのがいいようだ。

<dependency>
    <groupId>org.jvnet.mock-javamail</groupId>
    <artifactId>mock-javamail</artifactId>
    <version>1.9</version>
    <scope>test</scope>
</dependency>

とか pom.xml に書いておいて、以下のようにテストコードを書いていけばいいだけ。なんか依存にいれるだけでいい感じに送信メールをキャプチャしてくれるようだ。

List<Message> inbox = Mailbox.get("[email protected]");
assertEquals(inbox.size(),1); // was the e-mail really sent?

mock-javamail だいぶ長らくリリースされてないし、どうなのかな~と思ったけど、 JavaMail 自体べつに進化してないので問題ないようだ。 バックワードコンパチビリティ最高!!!

ちなみに開発環境で動作確認するときは以下のように python でモックサーバーあげればよい。

sudo python -u -m smtpd -c DebuggingServer :25

参考文献

JS とかの管理を grunt+bower でやるようにしてみた

bower 的なものを使わないと、「これどこから拾ってきたんや……」という感じになりがちなので、一応やったほうがいいでしょうと。

bower は直接使っていると、git のレポジトリをぶちまけるばかりで、あきらかに不要なファイルを配信することになり気持ち悪いなーと思っていたのですが、grunt なり gulp なりを使うと、いらないファイルは展開されないようです(ちゃんと main というプロパティを bower.json に記述している場合)。

Perl なりなんなりで書いていると、node.js のツールを一部だけ使うのとかだるくてアレですが、Java の案件だとみんな何かしら LL を併用しながらやるので、まあいいかな、と。

package.json に以下のように記述します。そうすれば grunt の依存とかもなんとなく入る。

    {
        "name": "jmx-dashboard",
        "version": "0.0.0",
        "description": "",
        "main": "",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "author": "tokuhirom",
        "license": "MIT",
        "devDependencies": {
            "grunt": "~0.4.1",
            "grunt-bower-task": "~0.2.3"
        }
    }

で、Gruntfile.js に grunt のルールを以下のような感じでかく。

module.exports = function (grunt) {
    grunt.initConfig({
        bower: {
            install: {
                options: {
                targetDir: './src/main/webapp/components/',
                layout: 'byComponent',
                install: true,
                verbose: false,
                cleanTargetDir: true,
                cleanBowerDir: true
                }
            }
        },
    });
    grunt.loadNpmTasks('grunt-bower-task');
    grunt.registerTask('default', ['bower:install']);
};

あとは bower.json に依存管理を実際、以下のように記述すればよろしい。

{
    "name": "jolokia dashboard",
    "version": "0.0.1",
    "dependencies": {
        "bootstrap": "~3.3.2",
        "underscore" : "~1.7.0",
        "dispatch": "~1.3.0"
    }
}

最後に README.md に以下のように記述すればよい。

## 静的リソースの管理

    > npm install -g grunt
    > grunt

JS を1ファイルにまとめるとかもやってもいいのだが、実際にはデバッグしづらくなったりして時間のロスになりがちでもあろうし、だいたい僕が書くJSとか、社内の人か半径5kmぐらいの人しかつかわないので、頑張る必要がない。 ということで、そういう感じにしている。なんかうまいことやる方法あれば script タグなどが簡潔になるならそれはそれで嬉しいので、やりたいかもしれない。

glup でもよかったのだが、なんかうまく動かなかったのでとりあえず grunt で。得にこれ以上のことは得にする予定がないのでいいかな的な。

てかこの程度のこと bower コマンド単体でもできてよさそうなんだけど、なんかできないっぽくて辛い。

> bower -L src/main/webapp/components/ --main

とかでできるようにするのが普通なのでは…… 俺が知らないだけでなんか方法があるのでは??という思いがあります。

(bower は仕様がそれなりに固まってきてるようなので、Pure Perl でクライアント書いてどやこや、とかもできなくはないと思う)

P.S. http://yosuke-furukawa.hatenablog.com/entry/2013/06/04/085537 ↑このエントリを参考にしました。

jolokia につないで雑に情報を見るツールを書いた

https://github.com/tokuhirom/jolokia-dashboard

jmx/jolokia なサーバーがいっぱいたってるんだけど、いちいち jconsole の URL とか調べるのがダルい、という問題があり、そういったものをささっとアクセスできるようなウェブUIでも作っておくか、ということで作ってみた。

作ったのはいいんだけど、もっといい方法が発明されたのでこれはオクラ入りとなった。


UI は Angula.JS で開発されているが、なんだかんだいって AngularJS で書かれたアプリケーションは見通しがよく、これよりもよい JS のフレームワークが見当たらない。まあ、コンセプトを提示することが重要なので慣れてるやつでいい。


結果的に、数時間かけてコンセプトコード提示した結果、いい感じにデータをとれるやつを作ってもらったのでよかった。 (たぶん作成時間はそんなにかかってない)