javac で class ファイルを作成しないようにする

Sublime Text の SublimeLinter で Java の法チェックすると、内部で javac が実行されているので構文チェックが問題ないとそのままコンパイルが完了して .class ファイルが出力されるので Java Compiler API を使って .class を作成しないようにコンパイル処理を作成してみました。

tadaedo/memjc · GitHub

memjc Test.java

でクラスファイルが作成されないため、構文チェックだけが使用できます。

memjc -sourcepath /tmp /tmp/Test.java

一部 javac オプションも使えます。(そのまま Java Compiler API に渡してるだけなので)

memjc -memjc-out Test.java

でクラスファイルも出力されるので

javac Test.java

と同じになります。

java コマンドが jre を参照している場合 Java Compiler API は呼び出せないっぽいので jdk を参照させる必要があるようです。まあ jrejavaコンパイルできないのは当然ですね。

infinispanメモ

infinispan

簡単に言ってしまえば JBoss で開発されている KVS です。

infinispan で何ができるかは自分自身まだ理解できていないのでそれは他のサイトにお任せするとして、infinispan を軽く動かしてみた際のメモ。

本当に動かしただけなのでクラスタやクエリまではやれてないです。

組み込みで使ってみる

maven プロジェクトを作成して、 infinispan-core を pom.xml に追加します。今回は 7 系が Alpha 版だったので 6.0.2 を使用しました。

DefaultCacheManager manager = new DefaultCacheManager();

まずはマネージャの作成。DefaultCacheManagerをそのまま使うとメモリ上に値が保持されるため、アプリを再起動すると内容がすべて消えるため、値を保持しておきたい場合、以下のようにするとローカルにファイルが保存される。保存する方法も JPA や LevelDB 等が選べるらしい。

DefaultCacheManager manager = new DefaultCacheManager();
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.persistence()
    .addSingleFileStore()
    .location("M:\\cache"); // パスは適当に環境に合わせて

DefaultCacheManager manager = new DefaultCacheManager(builder.build());

これで cache というディレクトリが作成され、中にキャッシュファイルが保存される。

manager.start();

Cache<String, String> cache = manager.getCache();

manager を start させて cache を取得します。ただし、start は manager のコンストラクタ内で呼ばれるっぽいので、コンストラクタに明示的に start を実行しないフラグを渡さない限り省略してもいいかも。

Cache<String, String> cache = manager.getCache("cacheName");

キャッシュに名前を指定可能。

cache.put("key", "value");
System.out.println(cache.get("key"));

後は put と get で OK。~Async というメソッドがあるが、これは組み込みモードでは意味はないらしい(Asyncがついてないメソッドと同じ動きと理解してます)。

cache.put("key", "value", 30, TimeUnit.SECONDS);

TimeUnit で有効期間も指定可能。

manager.stop();

最後に stop。Webアプリに組み込んだ際、きちんと stop を呼ばないとアプリ再デプロイ時に Exception が発生しました(JVM の再起動があれば問題ないかも)。サーブレットなら ServletContextListener でアプリ内で使用した manager の stop を呼べば良い。

サーバを立てて使ってみる(サーバ側)

infinispan のサーバは以下のサイトからダウンロード可能 http://infinispan.org/download/

解凍して bin ディレクトリにある standalone.sh (or bat) が起動スクリプト

./bin/standalone.sh

localhost でサーバを立ち上げるのであればこれでいいのですが、別のサーバ上で実行する場合はバインドする IP を指定する必要があります。

./bin/standalone.sh -b xxx.xxx.xxx.xxx

自身のサーバの IP を指定します。ホスト名でも可能かどうかは未確認。

私の環境では Windows8 が原因なのか CPU 負荷 MAX になってしまうため、別途 Linux サーバを立てて起動したので以降 IP 指定で確認しています。

また、設定ファイルを指定しない場合、./standalone/configuration/standalone.xml の設定ファイルが使用されるようです。

(以下は暫定手順)

<cache-container name="local" default-cache="default" statistics="true">
  ~ 他の設定は省略 ~
 <local-cache name="testCache" start="EAGER"/> <!-- 追加行 -->
</cache-container>

デフォルトのキャッシュ(キャッシュ名指定しない)の場合、うまくデータが登録されてくれなかったため、設定ファイルにキャッシュ名を登録しておきます。

デフォルトの設定ファイルの設定値もきちんと理解しておかなきゃ、、、

サーバを立てて使ってみる(クライアント側)

maven に infinispan-client-hotrod を追加します。

ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServer().host("xxx.xxx.xxx.xxx").port(11222);

RemoteCacheManager manager = new RemoteCacheManager(builder.build());

ConfigurationBuilder クラスは、組み込みで使ってたクラスとは異なるため注意。

RemoteCache<String, String> cache = manager.getCache("testCache");

cache.put("key", "value");
System.out.println(cache.get("key"));

後は組み込み時と一緒。

Linux で論理ボリュームの容量の調整

LVM(Logical Volume Manager)

数年前から Linux をインストールするとデフォルトで LVM が使われていて、「容量を後から調整できるんだ。へー」くらいにしか思っていなかったけど、最近会社で使っているサーバで急きょ容量の調整をしなくてはいけなくなったため、その時の作業メモ

今回行ったのは home から root への容量の移動だったので、swapを調整したい場合はまた別途作業が必要かも。

検証OS

CentOS6.5

手順1 現状確認

今のサーバの状態を把握する

$ df -h

手順2 アンマウント

調整する論理ボリュームをアンマウントが必要なのでサーバを再起動して、LiveCDのテキストモードで起動する。

手順3 現状確認

物理ボリュームを表示

$ pvscan

ボリュームグループを表示

$ vgscan
Reading all physical volumes.  This may take a while...
Found volume group "VolGroup" using metadata type lvm2

論理ボリュームを表示

$ lvscan
inactive    '/dev/VolGroup/lv_root' [13.01 GiB] inherit
inactive    '/dev/VolGroup/lv_home' [30.59 GiB] inherit
inactive    '/dev/VolGroup/lv_swap' [1.50 GiB] inherit

手順4 テキストモード上でLVMを有効にする

論理ボリュームの場所は環境依存らしいですが、CentOS6.5のインストール直後では /dev/mapper 内にありました。

論理ボリュームが見当たらない場合、ボリュームグループを有効にします

vgchange -ay

手順5 容量を減らす

減らす際は、物理容量の調整が先でその後に論理容量の調整をする必要があるって外人さんがStackOverflowのどっかで回答してた

ファイルシステムをチェック

e2fsck -f /dev/mappler/VolGroup-lv_roog

物理容量を調整。リサイズ後のサイズを指定 ※環境によるかもしれませんがGB数の箇所に小数点を入れるとうまく動きませんでした  その際は端数を調整切り捨てました

resize2fs /dev/mapper/VolGroup-lv_home 15G

論理容量を調整。サイズの箇所は-5Gなどマイナス指定可能。

lvreduce -L 15G /dev/mapper/VolGroup-lv_home

最後にもう一度ファイルシステムをチェック

手順6 容量を増やす

増やす際は減らすとは逆で、論理容量の調整が先でその後に物理容量らしい。

ファイルシステムをチェック

e2fsck -f /dev/mappler/VolGroup-lv_roog

論理容量を調整。サイズの箇所は+5Gなどプラス指定可能。

lvextend -L 28G /dev/mapper/VolGroup-lv_root

物理容量を調整

resize2fs /dev/mapper/VolGroup-lv_root 28G

最後にもう一度ファイルシステムをチェック

これで完了です。

Sublime Text で Groovy の構文チェック

やりたいこと

Sublime Text を使って Groovy の構文エラーを表示させたい。

まずはビルドさせてみる

メニューから [Tools] > [Build System] > [New Build System] を選択すると新しい設定ファイルが開かれるので、Groovy の情報を書き込む。

{
    "cmd": ["C:\\groovy\\2.2.0\\bin\\groovy.bat",  "$file"]
}

これを名前の先頭に Groovy とつけて保存(場所はたぶん変更しなくてOK)

再びメニューから [Tools] > [Build System] > [Groovy] を選択して、Groovy ファイルを開いた状態で Ctrl + b

f:id:erudoru:20131207171427p:plain

できた。

でも違う!

1、2年前 PHP の開発を行った時に Sublime Text を使い始めたのですが、その時は SublimeLinter を使ってファイル保存時に構文チェックをかけて、エラーの箇所にアラートを表示してたのですが、それを Groovy でもやりたい!

※ SublimeLinter のインストールは省略します

SublimeLinter を Groovy に対応させる

Sublime Text のプラグインは Python で作成されているため、Groovy 用の Python コードを書きます。

メニューから [Preferences] > [Brose package] でパッケージディレクトリが開かれます。 SublimeLinter > sublimelinter > modules の中に各種言語用の処理が記載されています。今のバージョンでは Groovy は無いっぽいので自作してしまいます。

groovy.py

import re

from base_linter import BaseLinter, INPUT_METHOD_FILE

CONFIG = {
    'language': 'Groovy',
    'executable': 'groovy',
    'lint_args': ['-e', 'new GroovyShell().parse(new FileReader(args[0]), args[0])', '{filename}'],
    'input_method': INPUT_METHOD_FILE
}

class Linter(BaseLinter):
    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        for line in errors.splitlines():
            match = re.match(r'^(?P<file>.*\.groovy)[\d: ]+(?P<error>.*)\s+@\s+line\s+(?P<line>\d+)\s*,\s*column\s+(?P<column>\d+)\s*.', line)
            if match:
                break

        if match:
            error, line = match.group('error'), match.group('line')
            self.add_message(int(line), lines, error, errorMessages)

Groovy には PHP の -l オプションの様な構文チェックオプションがありません。そのため GroovyShell クラスを使用して構文チェックを行います。 内容は既存のコードを参考にしています(主に php.py)

チェック用の python コードができたら今度は SublimeLinter の設定。

[Preferences] > [Package Settings] > [SublimeLinter] > [Settings - User] で、Groovy のパスを通します。

{
    "sublimelinter_executable_map":
    {
        "groovy": "C:\\groovy\\2.2.0\\bin\\groovy.bat"
    }
}

さて、これで Groovy ファイルを編集&保存を行うと

f:id:erudoru:20131207173147p:plain

でた!

行番号の箇所にエラーが表示されました。 (表示のされ方が以前とちょっと違う気がしますがプラグインのバージョンの違いでしょうか)

groovyserv を使ってもっと早く!

チェックはできたのですが、反応が一瞬遅れて表示されます。sublimelinter_executable_map の設定に groovyclient のパスを指定すれば速度が向上します。

Windows 環境の場合、Cygwin 上で GVM を使っている場合でも、別途 groovyserv の公式サイトから Windows 用の groovyclient.exe を落としてくる必要があると思います。 かつ自前の環境では、事前に groovyserv を起動していないと Sublime Text が固まってしまいました。

Flyway で DB マイグレーション

JavaでDBのマイグレーションについて、Flyway か Liquibase で迷って色々なサイトをささっと斜め読みした感じ 「Java」 という点に誘われて Flyway に決定。 Flyway のサイトにある比較では Liquibase は Groovy で書けるらしい。

maven plugin としてでなく Spring の方に組み込んで WAR をデプロイしたタイミングでマイグレーションが行われるようにしました。

事前準備

・データベースが作成されている(テーブルは無い状態)

・テーブル作成済の場合は、フルバックアップしたあと、全テーブル削除。マイグレーション後にデータのみ手動で戻す

pom.xml

<dependency>
    <groupId>com.googlecode.flyway</groupId>
    <artifactId>flyway-core</artifactId>
    <version>2.2.1</version>
</dependency>

applicationContext.xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    ・・・
</bean>

<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate" depends-on="dataSource">
    <property name="dataSource" ref="dataSource" />
    <property name="locations" value="jp/co/xxxx/yyyy/migration" />
    <property name="initOnMigrate" value="true" />
</bean>

<bean id="transactionManager" depends-on="flyway"
class="org.springframework.orm.jpa.JpaTransactionManager">
    ・・・
</bean>

initOnMigrate=true で管理用テーブルが作成される。 locations は デフォルトだと db/migration らしいので、プロジェクトのパッケージに合わせる。

上記の例の場合SQL

src/main/resources/jp/co/xxxx/yyyy/migration

に作る。Java

src/main/java/jp/co/xxxx/yyyy/migration

に作る。 Spring プロジェクトでは SpringJdbcMigration を実装すればいいらしい。

V1_0__update_table.java

public class V1_0__update_table implements SpringJdbcMigration {
    @Override
    public void migrate(JdbcTemplate jt) throws Exception {
        jt.update("更新内容");
    }
}

JavaSQL の同居が可能だけど、バージョン番号はずらす必要あり。 V1__xxxx と V1_0__yyyy は同じバージョン扱い。これで実行すると「同じバージョンがあるぞ!」って怒られる。

Caused by: com.googlecode.flyway.core.api.FlywayException: Found more than one migration with version '1.0'

同一バージョン不可なので実行順番はバージョン順。

Spring 上で使ってみての不満点

・エラーがわかりづらい 何が原因でエラったのかわからない。スタックトレースから読み取れない(ログの出し方が悪い?)

GradleでWebアプリ実行

Gradleをさわってみる。

ドキュメント通りにディレクトリを作って http://gradle.monochromeroad.com/docs/userguide/java_plugin.html#N13140

コマンド一発で実行

gradle jettyRun

build.gradle

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.mortbay.jetty:servlet-api:3.0.20100224"
    compile "org.mortbay.jetty:jsp-api-2.1:6.1.14"
    compile "jstl:jstl:1.2"
}

jettyRun {
    webAppSourceDirectory = new File("src/main/java")
    webXml = new File("src/webapp/WEB-INF/web.xml")
}

Servlet.java

package myapp;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Servlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest req,
                      HttpServletResponse res) 
                      throws ServletException, IOException {
        res.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = res.getWriter();
        writer.print("<html><body>test</body></html>");
    }
}

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">

    <servlet>
        <servlet-name>webApplication</servlet-name>
        <servlet-class>myapp.Servlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>webApplication</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>