tokuhirom's blog

Perl5's File::Path::mkpath equivalent in perl6

Shell::Command::mkpath provides same feature.

> use Shell::Command;
> mkpath("/tmp/x/y/z");

(Shell::Command is bundled)

Created: 2015-09-02 07:11:51
Updated: 2015-09-02 07:11:51

サーバーサイドのテンプレートエンジンに mustache を使うのはあんまよくないようだ

mustache は速いしいいんじゃないの? と思ったのだが、実際使ってみると微妙だ。

機能が少ない

やはり機能がしょぼい。それはそれでいいんだけど。特に自分でテンプレートを記述する時というのはつまりゴリゴリと管理画面を書く時が多いんで、まあなかなか mustache だとダルい。それなりに複雑な画面になりがちなので。

結局 freemarker ぐらいの機能は会ったほうが楽っぽい。

client side のテンプレートエンジンとコンフリクトする

これがでかくて。

client side のテンプレートエンジンとマークアップがかぶってダメになることがままあり、まあなんかめんどくさいですね。

Created: 2015-08-29 12:38:35
Updated: 2015-08-29 12:38:35

elasticsearch と testing

Java で elasticsearch を利用したアプリケーションを開発する場合、NodeBuilder など用いて、JVM 内で elasticsearch を立ち上げるのが順当に見えるが、実際はそうではないようだ。

NodeBuilder で作った elasticsearch に plugin を導入するのがわりとダルい。 アプリケーションの testing deps に plugin を入れる必要があるし、そういうふうにするのがいいのかはちょっと疑問。

なので、gradle で elasticsearch を起動してそれを利用するようにするか、どこか社内クラウドで起動しておくのが良さそう。

Created: 2015-08-29 12:15:14
Updated: 2015-08-29 12:15:14

perl6 で TSV を SQL に変換する

もはやこういったものは Perl6 で書いた方が楽かもしれない。

my @header = lines.shift.split(/\t/);

say "INSERT INTO member ({@header.map({ "`$_`" }).join(",")}) VALUES";
lines.map({
    "(" ~ .split(/\t/).map({ qq!"$_"! }).join(",") ~ ")"
}).join(",\n").say;
say ";";

これを moarvm で動かすと想像以上に速く動く。便利である。

個人的には Perl5 よりも Ruby よりも書いていて気持ちがいい。 読んでいて気持ちがいいかは知らない。

Created: 2015-08-29 11:20:31
Updated: 2015-08-29 11:20:31

YAPC::Asia 2015 に行ってきた

YAPC::Asia なのに、うわさの Perl6 の話題が一個もないってのもなんだねえ、ということで Perl6 + JVM のあたりの話をした。 思ったより「遊べる」状況にはなってるんで、遊んでみたらいいと思う。

Created: 2015-08-25 18:33:12
Updated: 2015-08-25 18:33:12

gradle でブランチ名からバージョン番号を指定する

うちの職場では、jar を jenkins でビルドして、nexus enterprise にアップすることにより、実際にデプロイされたコードの履歴を保持するというようなことをやっている。

なので、jar にバージョン番号を指定する必要がある。

現在、ブランチ設計は、master がリリース用。develop-1.1, develop-2.0 のようにバージョン番号がついた開発ブランチで SNAPSHOT バージョンを作成し、複数の alpha 環境にリリースしている。

build.gradle の中にバージョン番号を書いたりしていたのだが、まあ問題があって、バージョン番号を直ガキしている上に 1.1.0-SNAPSHOT などと書いていると、develop-1.1 → develop-2.0 ブランチにマージなどしているとそこがコンフリクトしがちである。

そこで、build.gradle では develop-1.1 などのブランチ名から取り出したバージョン番号を採用することにする。 具体的には以下のとおり。

// Release version.
final String RELEASE_VERSION = "2.1.0"

String gitBranch = ["sh", "-c", "cd ${project.rootDir} ; git rev-parse --abbrev-ref HEAD"].execute().in.text.trim()

def m = gitBranch =~ "develop-([0-9]+\\.[0-9]+)"
if (m.matches()) {
    version = m.group(1) + '.0-SNAPSHOT'
    println("version: ${version}")
} else {
    version = RELEASE_VERSION
}

まあ、そんな感じです。

Created: 2015-08-25 17:30:15
Updated: 2015-08-25 17:30:15

BetterTouchTool が便利

もともと、Better Snap Tool を使っていたのだが、BetterSnapTool は有料なので、BetterTouthTool のほうがいいかも。

ウィンドウの移動が楽になるので。

金はすでに払ってるんで bettersnaptool でもよいのだが

    brew cask install bettertouchtool

でインストールできるのが良い。

Created: 2015-08-17 19:35:00
Updated: 2015-08-17 19:35:00

log4jdbc は logback の設定をミスってるとすげー遅い

log4jdbc を使うと jdbc でアクセスしたクエリのログが簡単にとれて便利なのだが、設定を誤るとパフォーマンスを超絶劣化させてくるので注意が必要である。 log4jdbc はデバッグレベルだと Throwable のインスタンスを取得してスタックトレースを取って、呼び出し元の情報を表示する。 これが極めて遅い。本当に驚くほど遅い。

↓具体的にはこのへんである。

  private static String getDebugInfo()
  {
    Throwable t = new Throwable();
    t.fillInStackTrace();

    StackTraceElement[] stackTrace = t.getStackTrace();

    if (stackTrace != null)

https://github.com/arthurblake/log4jdbc/blob/master/src-jdbc4/net/sf/log4jdbc/Slf4jSpyLogDelegator.java#L433-L440

このメソッドは isDebugEnabled でチェックした上で、実行されるんで、debug レベルで log4jdbc のログを出していると遅くなる。

logback ではデフォルトのログレベルが debug なので、ちゃんと <root> の設定をしていないと遅くなる。

具体的には以下のように記述すればよいだろう。デフォルトで INFO レベルのメッセージを表示して、jdbc.audit 等の無用なものは off にする。

<!-- Suppress useless log4jdbc messages -->
<logger name="jdbc.audit" additivity="false" level="off"/>
<logger name="jdbc.resultset" additivity="false" level="off"/>
<logger name="jdbc.sqlonly" additivity="false" level="off"/>

<!-- Show info level messages by default -->
<root level="info">
  <appender-ref ref="STDOUT"/>
</root>
Created: 2015-08-14 17:52:53
Updated: 2015-08-14 17:52:53

groovy で ant task を記述したい

ant でロジックを記述するのはなかなか大変であるし、今となっては潰しのきかないスキルでもあり、未来ある若者に ant を使わせるべきではない。

しかし、世の中には ant を利用することを強いられている若者もいると聞く。

そういった中で、タスクを groovy でこなす方法を紹介しよう。

groovy でタスクを定義できれば、コードの可読性があがりハッピーになれるはずだ。

groovy でタスクを定義するためには groovy 本体を自動で取得する必要があるが、これは maven ant tasks で行う。maven ant tasks は、ant から maven の依存関係解決を呼べるようにしたライブラリである。

maven ant tasks はすでにインストール済みである場合も多いと思うが、入っていない場合は以下のようにすればよい。

mkdir ~/.ant/lib/
wget http://ftp.meisei-u.ac.jp/mirror/apache/dist/maven/ant-tasks/2.1.3/binaries/maven-ant-tasks-2.1.3.jar -P ~/.ant/lib/

さて、準備は整った。あとは以下のように記述すればよいだけである。 極めて簡単である。

<project name="demo" default="dist" xmlns:artifact="antlib:org.apache.maven.artifact.ant">

    <target name="dist">
        <!-- groovy-all を依存として宣言 -->
        <artifact:dependencies pathId="groovy.classpath">
            <dependency groupId="org.codehaus.groovy" artifactId="groovy-all" version="2.4.4" scope="compile"/>
        </artifact:dependencies>

        <!-- groovy-all を利用して groovy タグを有効化 -->
        <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="groovy.classpath"/>

        <!-- build.groovy を実行 -->
        <groovy src="build.groovy"/>
    </target>

</project>

以上、ant から groovy を呼び出す方法を紹介した。

groovy スクリプトを実行できるようになったのはいいが、普通に groovy script で記述していくのは辛い。しかし ant から呼ばれた場合、ant の機能をつめこんだ AntBuilder といオブジェクトが渡されてくる。これを利用して、ant の機能を利用してコードを書いていけば良い。

AntBuilder についての解説はこのへんを見れば良い。 http://docs.groovy-lang.org/latest/html/documentation/ant-builder.html

良いのだが、これを直接利用するのは辛いので、ヘルパークラスを定義してこれを利用する。

以下は基本的なオペレーションをラップしている。直接 Ant を利用するのではなく、このヘルパクラスを利用して操作を行っていく。

import groovy.util.AntBuilder
import groovy.xml.NamespaceBuilder 
import org.apache.tools.ant.BuildException
import org.codehaus.groovy.ant.AntProjectPropertiesDelegate

public abstract class AbstractDeployer {
    AntBuilder ant
    AntProjectPropertiesDelegate props
    def mvn

    def AbstractDeployer(AntBuilder ant, AntProjectPropertiesDelegate props) {
        this.ant = ant
        this.props = props
        // Maven Ant Tasks
        this.mvn = NamespaceBuilder.newInstance(ant, 'antlib:org.apache.maven.artifact.ant') 
    }

    /**
     * Copy files from {@code src} to {@code dst} recursively.
     */
    def copyRecursive(String src, String dst) {
        if (new File(src).exists()) {
            ant.copy(todir:dst, overwrite:"true") {
                fileset(dir:src)
            }
        } else {
            echo("There is no ${dst}")
        }
    }

    /**
     * Get property. If there's no value for the key, this method throws Exception.
     */
    String propOrDie(String key) {
        def val = props[key]
        if (val == null) {
            throw new BuildException("No such property: '${key}'")
        }
        return val
    }

    /**
     * Get property value for {@code key}.
     */
    String prop(String key) {
        return props[key]
    }

    /**
     * Fetch {@code groupId}:{@code artifactId}:{@code version} and extract it into {@code webappOutput}.
     */
    def fetchAndUnwar(String groupId, String artifactId, String version, String webappOutput) {
        // fetch war
        mvn.dependencies(
            filesetId:'dependency.fileset.war',
            versionsId:'dependency.versions.war',
            useScope:'compile') {
            dependency(
                groupId:groupId,
                artifactId:artifactId,
                version:version,
                type:'war',
                scope:'compile')
        }

        ant.copy(todir:'target/artifacts') {
            fileset(refid:"dependency.fileset.war")
            chainedmapper {
                mapper(
                    classname:"org.apache.maven.artifact.ant.VersionMapper",
                    from:'${dependency.versions.war}')
                mapper(type:'flatten')
            }
        }

        // extract war file
        ant.unwar(
            src:"target/artifacts/${artifactId}.war",
            dest:webappOutput)
    }

    /**
     * Display {@code message}.
     */
    def echo(String message) {
        ant.echo(message:message)
    }

    /**
     * load properties from {@code srcFile}.
     */
    def loadProps(String srcFile) {
        if (new File(ant.project.baseDir, srcFile).exists()) {
            echo("loading ${srcFile}")
            ant.loadproperties(srcFile:srcFile)
        } else {
            echo("There is no ${srcFile}")
        }
    }
}

実際のビルドスクリプトは以下のような形式でやればよろしい。本来ならば、もっと DSL っぽく記述したほうがよいのだが、結局のところコンパイル時にある程度チェックできたほうが生産性が高まるので、@CompileStatic を付けられることを重視してクラスを定義している。もっと綺麗に書く方法があれば教えていただきたい。

このコードを見れば、なにがどういう手順で行われるかは一目瞭然であり、カスタマイズも簡単である。

import groovy.transform.CompileStatic
import groovy.util.AntBuilder
import org.codehaus.groovy.ant.AntProjectPropertiesDelegate

import AbstractDeployer

@CompileStatic
class Deployer extends AbstractDeployer {
    def Deployer(AntBuilder ant, AntProjectPropertiesDelegate properties) {
        super(ant, properties)
    }

    def run() {
        echo("building...")

        fetchAndUnwar("org.glassfish.admingui", 'war', '10.0-b28', 'target/webapp')
        // overwrite files
        copyRecursive("resources", "target/webapp/resources")
    }
}

new Deployer(ant, properties).run()

以上、簡単ですが ant から groovy を用いてコードを記述する方法の紹介でした。

Created: 2015-08-14 10:08:32
Updated: 2015-08-14 10:08:32

Jetty でディレクトリリスティングを拒否したい

    webAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");

で、いけます。

ref. https://stackoverflow.com/questions/7217969/how-to-disable-directory-listing-for-jettys-webappcontext/7572313#7572313

Created: 2015-07-14 16:08:14
Updated: 2015-07-14 16:08:14