2009年10月30日金曜日

デプロイされてるプロジェクトのリビジョン番号を分かるようにする

開発時なんかで今デプロイされてるリビジョンは一体いくつなんだろう?というのを知りたい!と言った時のソリューション。これはbuildnumber-maven-pluginで簡単に実現することが出来る。

pomに以下を追加して、先ほどのエントリで紹介したmaven-resources-pluginのフィルタリング機能を使えばいい。これによって${buildNumber}という変数が使えるようになるので、表示用のHTMLなどで置換してあげればOK。
<scm>
  <connection>scm:svn:http://your-project-svn-repo.com/</connection>
  <developerConnection>scm:svn:http://your-project-svn-repo.com/</developerConnection>
  <url>http://your-project-svn-repo.com/</url>
</scm>
:
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>buildnumber-maven-plugin</artifactId>
  <version>1.0-beta-3</version>
  <executions>
    <execution>
      <phase>validate</phase>
      <goals>
        <goal>create</goal>
      </goals>
    </execution>
  </executions>
</plugin>

mavenでappengine-web.xmlを開発者別に生成する方法 修正版

先日書いたエントリではmaven-replacer-pluginを使って・・・なんて書いてましたが、標準のmaven-resources-pluginでより簡単に複数のトークン、ファイルに対して使えることに気づいたので、こちらの方法を取ると良いです。こちらの方法だと、POMファイル内で参照可能な変数は全て利用することが可能なので、色々なことが出来ます。 構成はこんな感じで。
project
|-src/main/java
| :
+-src/filtering-resources
| |-WEB-INF/appengine-web.xml
|-pom.xml
appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  <application>${appId}</application>
  <version>${appVersion}</version>

  <sessions-enabled>true</sessions-enabled>
  <ssl-enabled>true</ssl-enabled>

  <system-properties>
    <property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
    <property name="com.google.gdata.DisableCookieHandler" value="true" />
  </system-properties>
</appengine-web-app>
pom.xml
<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/maven-v4_0_0.xsd">
  :
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <appId>appId</appId>
    <appVersion>version</appVersion>
  </properties>
  :
  <build>
  :
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-appengine-web-xml</id>
            <phase>process-resources</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <outputDirectory>war</outputDirectory>
              <resources>
                <resource>
                  <directory>src/filtering-resources</directory>
                  <filtering>true</filtering>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>  
</build>
  :
</project>

「Google App Engine for Java[実践]クラウドシステム構築」正誤表

弊社執筆の「Google App Engine for Java[実践]クラウドシステム構築」について、誤りが幾つかございます。
読者の皆様にご迷惑をお掛けしたことを深くお詫びし、訂正させていただきます。
正誤表を各章ごとに記載致しますので、以下のリンクからご参照ください。


TaskQueueを使ってメールを送信する

GAEにはMail送信サービスがあり、これを使ってEメールを送信することができます。
しかし、メール送信は必ず成功するとは限らないので、通常はメールを送信する際、送信の成功/失敗を確認し、失敗時には再送信するよう処理すると思います。
GAEでもそういう処理を書くことはできますが、「レスポンスを30秒以内に返さなければならない」という制約があるため、ひとつの処理の中で何度も繰り返すかもしれない処理を行うと、途中でタイムアウトとなり強制的に処理が中断されてしまう可能性があります。

そこで、より確実にメール送信を行うことができるよう、同じくGAEで使えるTaskQueueサービスを使いバックグラウンドでメール送信をする仕組みを作りました。
例えばあるフォームからPOSTされたときにメールを送信しようとした場合、
1. ServletでPOSTを受け取り、送信するメールの情報を作成
2. TaskQueueにそのメール情報の登録だけ行いレスポンスを返し、実際にはメール送信はしない
3. 別のServletでTaskを受け取り、その情報を元にメールを送信する
- 送信が成功すればそのTaskは削除される
- 送信失敗した場合は200以外のステータスを返せば、Taskは削除されずに自動的にリトライし続ける
という処理の流れになります。

ただし、一度TaskQueueに処理を任せてしまうと、そこに登録した情報を確認することが難しくなってしまいます。何故かTaskが実行されない、という問題が起こった場合にデバッグ作業が難しくなってしまいます。上記の場合だと送信が失敗し続けるメールがどのような内容のメールなのか、というのが判別できなくなります。

ということで今回は送信するメールの情報をDatastoreのエンティティとして保存し、そのkeyだけをTaskに渡して処理させるようにしました。こうすることで送信すべきメールの情報を管理コンソールのDataViewerから確認できるようになります。

コードの例としては、以下のようになっています。
/**
* @param inputStream templateを読み込むためのInputStream
* @param context templateに当てはめるpropertyを保持するobject
* @param url TaskQueueのtaskがアクセスするURL
* @param encoding templateのエンコーディング
* @throws IOException
*/
public void spool(InputStream inputStream, Object context,
        String url, String encoding) throws IOException {
    MailTemplateEngine templateEngine = new MailTemplateEngine();
    MailMessage mailMessage = 
            templateEngine.merge(inputStream, context, encoding);

    // MailTask entityに保存
    Map<headerkind, string> headers = mailMessage.getHeaders();
    String from    = headers.get(HeaderKind.FROM);
    String to      = headers.get(HeaderKind.TO);
    String subject = headers.get(HeaderKind.SUBJECT);
    String body    = mailMessage.getBodyText();
    MailTask mailTask = new MailTask(from, to, subject, body);
    Key key = Datastore.put(mailTask);

    // TaskQueueへの登録
    String keyString = KeyFactory.keyToString(key);
    TaskOptions taskOptions =
            TaskOptions.Builder.param(KEY_NAME, keyString).url(url);

    Queue defaultQueue = QueueFactory.getDefaultQueue();
    defaultQueue.add(taskOptions);
}
MailTemplateEngineはメールの情報(TO,CC,BCC,SUBJECT,本文)を作成するためのクラスで、そこで作られた情報をMailMessageクラスに格納しています。これらの実装については省略します。
出来上がったMailMessageの情報をもとにMailTaskエンティティを作成し、Datastoreに保存しています。ここではslim3のDatastoreを利用しています。

これと別に、Taskを処理するためのServletを用意します。
@SuppressWarnings("serial")
public class MailSendServlet extends HttpServlet {

    /**
     * requestパラメータからkeyを取り出し、該当するMailTaskの内容でメールを送信する。
     * メールの送信まで成功した場合のみ該当MailTaskのentityを削除する。
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // パラメータのkeyからMailTaskを取得
        String keyString = req.getParameter(MailSpooler.KEY_NAME);

        Key key = KeyFactory.stringToKey(keyString);
        MailTask mailTask = Datastore.get(new MailTaskMeta(), key);

        // MailTaskの情報をもとにメールを送信
        MailService mailService = MailServiceFactory.getMailService();
        Message message = new Message(
                mailTask.getFrom(),
                mailTask.getTo(),
                mailTask.getSubject(),
                mailTask.getBody().getValue());
        mailService.send(message);
        // 最後まで例外なく処理を終えることができたときのみ該当MailTaskを消去
        Datastore.delete(mailTask.getId());
    }
}
Taskに登録されているkeyを元に送信すべきメールの情報をDatastoreから取り出し、送信します。送信が成功すれば最後にそのエンティティを削除します。
途中でメールの送信に失敗し例外がthrowされた場合はTaskは処理を完了できなかったということで残り続け、エンティティも削除されず、TaskQueueサービスによって自動的にリトライされるようになります。

追記
bluerabbitさんからコメントにてご指摘をいただきました。ありがとうございます。
Taskを処理するServletで、メール送信をした後のエンティティ削除の部分で例外が発生すると、エンティティもタスクもそのまま残り、メールが再送信されてしまいます。
2重送信を防ぐためにも、最後のエンティティ削除は例外を無視するように変更した方が良いですね。
        // MailTaskの情報をもとにメールを送信
        MailService mailService = MailServiceFactory.getMailService();
        Message message = new Message(
                mailTask.getFrom(),
                mailTask.getTo(),
                mailTask.getSubject(),
                mailTask.getBody().getValue());
        mailService.send(message);

        // 最後まで例外なく処理を終えることができたときのみ該当MailTaskを消去
        try {
            Datastore.delete(mailTask.getId());
        } catch (Exception e) {
            // このときの例外は無視する
        }

「Google App Engine for Java[実践]クラウドシステム構築」正誤表 第7章

■p.245 リスト7.2.1内17行目
× if (req.getUserPrincipal() != null) { →②
○ if (req.getUserPrincipal() == null) { →②

■p.254 下から3行目
× URL http://code.google.com/p/gdata-java-client/downloads/list/
○ URL http://code.google.com/p/gdata-java-client/downloads/list

■p.261 図7.3.6
×

「Google App Engine for Java[実践]クラウドシステム構築」正誤表 第5章

■p.170 リスト5.1.1内7行目
× private final int ONE_HOUR_SEC = 60 * 60;
○ private static final int ONE_HOUR_SEC = 60 * 60;

「Google App Engine for Java[実践]クラウドシステム構築」正誤表 第4章

■p.148 リスト4.5.1内4行目
× .createEntityManagerFactory("transactions-optional";
○ .createEntityManagerFactory("transactions-optional");

■p.159 リスト4.6.3内47行目
× (List)query.execute(name) 
○ (List)query.execute(name); 


■p.162 リスト4.6.6内3行目~4行目 
× Query query = pm.newQuery(Entry.class) .setFilter("categories == :category"); 
○ Query query = pm.newQuery(Entry.class);  query.setFilter("categories == :category"); 

■p.162 リスト4.6.6内5行目 
× List results = (List)query.execute(category); 
○ List results = (List)query.execute(category); 

■p.164 リスト4.6.9内3行目 
× "select key from " + EntryIndex.class.SimpleName() + 
○ "select key from " + EntryIndex.class.getSimpleName() + 

■p.164 リスト4.6.9内4行目 
× " where categories == target"); 
○ " where categories == :target");

[Goose] まとめ

Gooseは自由な解答が入力できるオンラインJava教育システムとして開発されており、様々な解析技術を組み合わせて解答されたプログラムを自動的に添削することができます。また、全てブラウザ上から利用できるため、e-learningの問題集コンテンツとして利用したり、セミナーのハンズオンで利用したりすることを考えています。

また、グルージェントが提供するサイト「Song of Cloud」のクラウドに関するコンテンツ内でも、このGooseを利用したオンラインコースウェアやクイズを公開する予定です。

Gooseについてご興味がある方は、 songofcloud _at_ gluegent _dot_ com までお問い合わせください。

[Goose] プログラム解析技術 - 動的解析

前回のエントリでは、Gooseが解答を添削する際に利用していた技術として、プログラムの静的解析技術を紹介しました。

しかし、Javaなどの手続型プログラミング言語において、プログラムの静的解析だけでその挙動を完全に把握するのは非常に困難です。このため、Gooseでは静的解析が完了した後に、プログラム全体を実際に実行して動作を確認します。

空欄を含むプログラムと、空欄の内容については静的解析の段階で合成されますので、その結果をもとにオンラインでプログラムをコンパイルし、標準的なJavaの単体テストフレームワーク (JUnitなど) を利用したテストを実行できます。特殊な静的解析が必要でない場合、出題者は通常のプログラムと単体テストプログラムを用意し、通常のプログラムのいくつかに空欄を作成するだけで、新しい問題を作成できます。

なお、Gooseはクラウド上でも稼働することを前提に作成されています。このような場合、受講者が不正なプログラムを作成した際に、それを実行するとサーバに損害を与えることも可能となってしまいます。このような損害を回避するため、受講者の作成したプログラムに対して、実行時にいくつかの制限をかけられるようになっています。

サンドボックス実行環境

サンドボックス実行環境は、解答されたプログラムをGooseが実行(動的解析)する際に利用する環境です。この環境ではプログラムが特定の機能を利用しようとした際に、その利用を制限したり別の処理を実行させたりすることができます。受講者が環境に損害を与えるプログラムを作成した際に、プログラムの実行を停止 したり、無害な処理に書き換えて実行したりすることができます。

サンドボックス実行環境のイメージ。解答されたプログラムから利用可能な機能を制限している。

また、このサンドボックスは問題ごとに個別の設定を行えます。このため、一部のプログラムで特定のネットワークへの接続を許したり、コンソールへの出力を記録してそれをテストの対象としたりする、などといった使い方も可能です。

無限ループ解析

Gooseは解答されたプログラムを実際に実行しますが、そのプログラムの中に無限ループや非常に時間を要する処理が含まれていると、サービスを正常に稼働させられなくなってしまいます。このような処理は静的解析で検出するのが非常に困難で、Gooseでは動的解析の一環として無限ループの検出を行っています。

解答されたプログラムがコンパイルされる際、Gooseはクラスファイルに対して「チェックポイント処理」というものをプログラムの様々な個所に挿入します。このプログラムを実行すると、チェックポイント処理が何度も実行されることになり、Gooseはこの処理が行われた回数や最初のチェックポイント処理からの経過時間をもとに、サービスに損害を与えそうなプログラムを検出します。

サービスに損害を与えそうだと判断されたプログラムは、その場で実行が強制的に停止されます。そしてその解答は「時間内に動作が完了しなかった」という理由で不正解という扱いになります。Gooseでは、このような方法でサービスの可用性を向上しています。

[Goose] プログラム解析技術 - 静的解析

解答されたプログラムをGooseが添削する際には、プログラムコードを分析する静的解析と、プログラムを実際に起動して動作を分析する動的解析の2種類の技術を組み合わせて利用します。今回は、実際にGooseが行っている静的解析技術について紹介します。

プログラム解析の流れ。解答されたプログラムに対して、静的解析と動的解析の2種類の解析を行っている。

静的解析技術はグルージェントがかねてより興味を持って取り組んできた分野で、Javaプログラムの静的解析を行う開発環境Irenkaや、静的解析技術を取り込んだ開発環境のYukara, Pirka'rなどの支援を行っています。

このうち、GooseにはIrenkaの技術が組み込まれており、入力された解答に対して、変数やクラスなどへの参照についての分析や、型情報の分析などの高度な静的解析が行えます。

解答の合成

静的解析では、まず空欄を含むプログラムと、それらの空欄に入力された解答を合成します。このとき、Gooseでは空欄を含むプログラムと、空欄に入力されたプログラムを別々に解析し、Javaの構文レベルでの合成を行っています。

これは次項の「プログラム構造の解析」を単純化するための措置で、たとえば次のようなJavaのブロック構造を無視した解答を検出することができます。

ブロック構造を無視した解答の例。クラスブロックの中にメソッドを書いてほしかったが、クラスブロックを強制的に閉じて、新しいクラスの宣言を開始している。

例では空欄に「クラスの内容」を記述することを期待していますが、解答にはクラスの終端と、新しいクラスの開始が記載されています。これは、テキスト全体としての構文は正しいものの、空欄の中だけで見ると不正な構文です。

このような解答は題意から外れていることが多く、また個別の解析を複雑にする要因となります。Gooseでは個々の空欄を個別に分析するため、このような解答を検出して不正解とすることもできます。

空欄に入力されたプログラム構造の解析

解答の合成が終わったら、静的解析の機能を持つIrenkaを利用してプログラム全体の意味を解析します。このとき、プログラムが外部ライブラリを利用する場合には併せて解析を行い、プログラム中に出現する型の情報や、変数などの宣言と参照に関する情報など、コンパイルに必要なあらゆる情報を生成します。

Gooseでは、ここで生成された情報をIrenkaから取得して、それぞれの空欄に入力されたプログラムの構造を検証します。この情報はツリー状のモデルで表現されており、Javaのプログラムから自由に参照できるようになっています。このため、個々の問題に合わせた静的解析のルールを自由に追加し、複雑な静的解析を行えます。

Irenkaの処理の流れ。ソースプログラムと静的解析のルールをIrenkaに渡すと、様々な情報を解析して取得できる。

たとえば、「このAPIを利用せずに解答せよ」という問題や、「このAPIを使って解答せよ」などの出題も容易に行えますし、より複雑には「プロジェクトのコーディング規約に従って解答せよ」などの出題も可能です。

[Goose] Gooseの特徴

Gooseの最大の特徴は、Javaプログラムコードの穴埋め問題を適切に出題できる点です。実際のプログラムコードの一部を穴埋めのための空欄とし、受講者は空欄に対して任意のコード片を入力できます。また、この空欄の粒度は出題者が自由に設定することができ、たとえば1文字のみ空欄とすることも、ファイル全体を大きな空欄とすることも可能です。これにより、受講者の理解度に合わせた出題が可能です。

Goose出題画面 (開発中)

穴埋めされたプログラム片は、空欄の外側のプログラムコード全体と合成され、実際のJavaプログラムとして解析が実施されます。この解析には大きく分けて静的解析と動的解析の2種類があります。前者の静的解析はプログラムの「書き方」や「内容」などを精査する仕組みで、後者の動的解析はプログラムを実際に動作させて挙動を分析する仕組みです。これらについて詳しくは次のエントリで紹介する予定です。

また、これら一連のシステムはWebブラウザから利用できます。受講者のブラウザ上に空欄付きの問題が表示され、空欄を埋めて解答をクラウド上の添削サービスに送信すると、添削結果が受講者(または教育システム)に通知される仕組みになっています。

Goose添削結果画面 (開発中)

[Goose] オンラインJava教育システム

Gluegent Labsで開発中のGooseについて。

世の中には数多くのオンラインJava教育システムが存在しますが、その多くは下記の2つを組み合わせたコンテンツで成り立っています。

  1. Javaプログラミングやフレームワークなどの解説ページ
  2. 解説した内容を確認する選択式の問題集

まず、受講者は解説ページを読んで受講内容を理解し、その後に選択式の問題で理解度を確認する、という形態です。

ここで注目したのが、ほとんどの問題集が選択式の出題形式をとっているという点です。選択式の問題は添削や採点が楽ではありますが、解答が選択肢から推定できてしまったり、選択肢以外の表現を制限してしまったりするなどの問題があります。

とくに教育の目標を「アプリケーション開発能力の会得」とする場合、これらは非常に重大な問題となります。アプリケーションを開発する際にはプログラムコードを書く作業が少なからず含まれますが、選択式の問題では実際にプログラムコードを書くことがありません。そのため、いくらそのような問題集をこなしたところで、アプリケーション開発能力が成長しない可能性があります。

アプリケーション開発能力のうち、特にプログラムコードを書く能力を成長させるには、多くのプログラムを受講者自らが書くことが重要です。このため、Gluegent Labsで開発が進められているGooseでは「自由な大きさのJavaプログラムを穴埋めで入力できる」という出題形式を採用しています。

このような出題形式は新しいものではなく、従来では人間が目で見て添削を行っていました。Gooseではここにプログラムの解析技術を適用しており、自由度の高い解答表現に対しても機械的に正誤を判定し、問題があれば適切なアドバイスを解答者に提示できます。

BloggerAPIの軽い落とし穴

GWTでBloggerAPIを使用してフィードの取得を試みてみました。やり方は簡単。
BloggerServiceと言う名前のまんまのサービスが存在するので使用するだけ。
これに取得条件などを設定したQueryクラスを引数にして実行すれば簡単にフィードを取得できる…


…はずが実行時ClassDefFoundError!


ビルドエラーも起こっていないのになぜ…
どうやらgoogle-collect、gdata-mediaと言うライブラリも必要だそうです。罠すぎます。
これで今度こそと思いフィードを取得できる…


…はずが実行時ClassDefFoundError!


 今度は何でしょうか…
com.google.gdata.client.Queryが見つからないとお怒りのご様子。
gdata.clientライブラリは入れているのに…
軽く調べてみると、このcom.google.gdata.client.Queryクラス、


gdata-coreライブラリに入ってました


clientパッケージなのに…
ビルドエラーも怒らずGWTのコンパイル時も怒られず、実際に動かした場合にのみエラーが発生。分かってしまえば二度と落とし穴に落ちることはないですが。
他にも見えないエラーが多数潜むので油断できません…

Goose

Gooseはより多くの方々にJavaによるアプリケーション開発を行えるようになってもらえることを目指す、新しいオンラインJava教育システムです。このシステムはグルージェントがかねてより研究を行っていた、Javaプログラムの解析技術などを応用して作成されています。これにより、Javaプログラムの出題に対して自由度の高い解答が入力でき、さらにそれらを機械的に添削して親切なアドバイスを生成できるようになっています。

なお、Gooseは2009年8月よりGluegent Labs内の研究開発プロジェクトとして開発が進められています。Gluegent Labsでは、この他にも静的解析、動態検知、未来予測などの先進的な技術を使ったプロダクトを研究開発しています。

もくじ

  1. オンラインJava教育システム
  2. Gooseの特徴
  3. プログラム解析技術 - 静的解析
  4. プログラム解析技術 - 動的解析
  5. まとめ

2009年10月28日水曜日

Hategle(仮)を下書きで投稿できるように

Bloggerがどうにも書きづらいので、何かないかと探していたらHategle(仮)というのを発見。はてな記法で書けるので使ってみることに。ただ、下書きで投稿したいのでちょこっと修正してみた。

Perlはさっぱり分からないのだけれど、GDataAPIを生で使ってるようなので下書き投稿の方法をGoogle公式で調べて対応。

一つ注意があってGoogle公式にかかれてるxmlnsは誤りのようで、"http://purl.org/atom/app#"としてあげるのが正しい様子。 Hategleの56行目付近を以下のように修正すれば、下書きで投稿できるようになる。

my $post_content=<<EOM;
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entry xmlns='http://www.w3.org/2005/Atom'
     xmlns:app='http://purl.org/atom/app#'>
<app:control>
  <app:draft>yes</app:draft>
</app:control>
<title type='text'><!-- subject --></title>
<!-- timestamp -->
<content type='xhtml'>
<!-- post -->
</content>
<!-- category -->
<author>
  <name>$name</name>
  <email>$email</email>
</author>
</entry>
EOM

mavenでappengine-web.xmlを開発者別に生成する方法

共同で開発してると、appengine-web.xmlがAppIdとVersionの書き換えをする度に、コンフリクトしたり、デプロイ先を間違えたりと全くいいことがない。かといって、大事な設定項目もあるんで、各開発者でデプロイ先情報以外は統一しておきたい。そんな訳で、maven-replacer-pluginを使ってプロファイルを元に自動生成するようにしてみた。

構成はこんな感じで。
project
|-src/main/java
| :
+-template
| |-appengine-web-template.xml
|-pom.xml


appengine-web-template.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<!-- will be replaced by maven-replacer-plugin. see the pom file. -->
$DEPLOY_SETTING$

<sessions-enabled>true</sessions-enabled>
<ssl-enabled>true</ssl-enabled>

<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
</system-properties>
</appengine-web-app>


pom.xml
<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/maven-v4_0_0.xsd">
:
:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<appId>development</appId>
<appVersion>default</appVersion>
</properties>
:
<!-- 開発者別などにあわせて用意 -->
<profiles>
<profile>
<id>product</id>
<properties>
<appId>production</appId>
<appVersion>default</appVersion>
</properties>
</profile>
<profile>
<id>preview</id>
<properties>
<appId>development</appId>
<appVersion>preview</appVersion>
</properties>
</profile>
<profile>
<id>someone</id>
<properties>
<appId>development</appId>
<appVersion>someone</appVersion>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>bakersoftware</groupId>
<artifactId>maven-replacer-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<ignoreMissingFile>true</ignoreMissingFile>
<file>${basedir}/template/appengine-web-template.xml</file>
<outputFile>${basedir}/war/WEB-INF/appengine-web.xml</outputFile>
<regex>false</regex>
<token>$DEPLOY_SETTING$</token>
<value><![CDATA[<application>${appId}</application>
<version>${appVersion}</version>
]]></value>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
:
:
</project>


あとは、mvn process-resources -Psomeoneとプロファイルを指定して実行すれば、間違いのないファイルが生成される。

それにしてもblogger使いにくいな@@

2009年10月27日火曜日

「Song of Cloud」スタートしました

グルージェントでは、最近、Google App Engine for Java(GAE/J)とその周辺の技術に特化しています。私が執行役員を兼任する親会社のサイオステクノロジーがGoogle Appsのライセンスを再販しているので、グループ内の相乗効果を狙っての施策でもありますが、そもそも単純にGAE/Jが猛烈に面白いというのがその理由です。
10年来のグルージェントの伝統として、新しい技術はまず喰ってみて、それが良いものであれば先進的なお客様には努めて提案していきます。機会を得れば技術ナレッジや事例紹介を専門誌に寄稿しています。2009年4月7日にGAE/Jがリリースされてすぐ4月13日には理事を務めるNPO法人Seasarファウンデーションの小サービスを立ち上げ寄贈しました。また、5月16日には横浜YMCA様のシステムを構築しています。サイオステクノロジーのGoogle Appsオンライン販売サイトもGAE/JとGWTで開発し、このコーポレートサイトもそうです(コンテンツ部分はGoogle Bloggerを併用して工夫しています)。そして9月10日には技術評論社様より「Google App Engine for Java[実践]クラウドシステム構築」を上梓したところ、世界初のGAE/J専門書として好評いただいております。
一方、10年来のグルージェントの悪しき風習としては、マーケティングにはまったく手をかけず、毎度クレイジーに新しい技術に取り組んでいても社外へまったくアピールできていませんでした。今回あらためてコーポレートサイトを整備し、GAE/J関連の取り組みの全てを公表していきたいと考えてます。サイトタイトルもクラウドをストレートに意識して「Song of Cloud [叢雲の歌]」としました。コンテンツはGAE/J書籍サポート・GAE/Jおよびその関連技術の情報提供とともに、グルージェントのGAE/Jを中心としたソリューションの紹介です。
書籍を読んでいただいた全ての方々、GAE/Jにご興味を持つ全ての方々、そして少なからずいらっしゃるグルージェントファンのみなさまと、クラウド黎明期たる「今」を共有できましたら幸いです。
株式会社グルージェント 代表取締役社長 栗原 傑享