Tracのテンプレートでログインユーザ名を取得する

最近Tracのチケットテンプレートファイルをカスタムするのがマイブームになっています。 
今日は、テンプレートファイルにて、ログインユーザ名を取得する方法がわかったので、メモします。

$authname
これで取得できます! 因みに環境は、以下のとおり

 Trac version : 0.11_04
 テンプレート:ticket.html

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

EnumでPropertyファイル読み込みクラスを作る

WebシステムでPropertyファイルを起動時に読み込んでメモリ上に展開することはよくあることだと思います。

で、それをEnumでやったらこうなる!というのを作ってみました。

PropertyHolder.java

package property;

import java.util.Properties;
import java.util.Set;

public enum PropertyHolder {

    Hoge("hoge.properties"), Foo("foo.properties"), Bar("bar.properties");

    private Properties properties;
    private String fileName;

    static {
        System.out.println("staticイニシャライザ開始");
        for (PropertyHolder p : values()) {
            System.out.println("static:" + p.name());
            p.init();
        }
    }

    private PropertyHolder(String fileName) {
        System.out.println("constructor:" + fileName);
        this.fileName = fileName;
    }

    private void init() {
        System.out.println("init:" + fileName);
        properties = new Properties();
        try {
            properties.load(getClass().getClassLoader().getResourceAsStream(
                    fileName));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String get(String key) {
        return properties.getProperty(key);
    }

    public String getAll() {
        System.out.println("getAll:" + fileName);
        Set<Object> keys = properties.keySet();
        StringBuilder sb = new StringBuilder();
        for (Object obj : keys) {
            String key = (String) obj;
            sb.append("[");
            sb.append(key);
            sb.append(":");
            sb.append(get((String) obj));
            sb.append("]");
        }
        return sb.toString();
    }
}


Main.java
package property;

public class Main {

    public static void main(String[] args) {
        System.out.println("Hoge.get(\"a\"):" + PropertyHolder.Hoge.get("a"));
        System.out.println("Hoge.getAll():" + PropertyHolder.Hoge.getAll());
        System.out.println("Foo.getAll():" + PropertyHolder.Foo.getAll());
        System.out.println("Bar.getAll():" + PropertyHolder.Bar.getAll());
    }
}
hoge.properties
a=A b=B c=C
foo.properties
aa=AA bb=BB cc=CC
bar.properties
aaa=AAA bbb=BBB ccc=CCC
実行結果
constructor:hoge.properties constructor:foo.properties constructor:bar.properties staticイニシャライザ開始 static:Hoge init:hoge.properties static:Foo init:foo.properties static:Bar init:bar.properties Hoge.get("a"):A getAll:hoge.properties Hoge.getAll():[b:B][a:A][c:C] getAll:foo.properties Foo.getAll():[aa:AA][bb:BB][cc:CC] getAll:bar.properties Bar.getAll():[bbb:BBB][aaa:AAA][ccc:CCC]
ポイントは、staticイニシャライザでEnumクラスの定数一つ一つがもっているinitメソッドを呼んでいるところですかね。

staticイニシャライザは1クラスに対して1回しか動かないので、Enumクラスの中に定数がいくつあっても、その定数ひとつに対して1回しかstaticイニシャライザが呼ばれないことは実行結果をみるとわかりますね。

staticイニシャライザを使うと例外がRuntimeExceptionしか出せないのがきついですね。 システム共通ランタイムExceptionクラスがある場合、そちらを使ってラップしてからスローするといいですね。

あとは、いちいちinitメソッドを作らずにコンストラクタでファイルを読んでもいいと思います。 場合によると思うので、使い分けてみてください。

※サンプルのgetAllメソッドは実際に使う時は不要だと思うので、削除して使ってください。

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

EnumでDate変換Utilクラスを作る

いろいろな使い方があるEnumですが、Utilクラスを作るのにもつかえます!
ためしに日付を変換するUtilを作ってみました。

日付<>文字列を変換するときは、いろいろなフォーマットの日付を表す文字列に対応しなければならないので、フォーマットの定義(yyyyMMddみたいなの)をたくさん定義して、定義した分だけ同じよう名メソッドを作らなければなりません。。

ですが、Enumを使えばすっきりかけます~。

以下コードです。

DateUtil.java

package util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

// TODO: 自動生成された Javadoc
/**
* The Enum DateUtil.
*/
public enum DateUtil {

// ------------------------------
// 変換したいフォーマットを追加(start)
// ------------------------------
/** The YYYYMMDD. */
YYYYMMDD("yyyy/MM/dd"),
/** The YYYYMMDDHHMM. */
YYYYMMDDHHMM("yyyy/MM/dd hh:mm"),
/** The YYYYMMDDHHMMSS. */
YYYYMMDDHHMMSS("yyyy/MM/dd HH:mm.ss"),
/** The YYYYMMDDHHMMSSSSS. */
YYYYMMDDHHMMSSSSS("yyyy/MM/dd HH:mm.ss.SSS"),
/** The Jp_ yyyymmdd. */
JP_YYYYMMDD("yyyy年MM月dd日"),
/** The Jp_ yyyymmddhhmm. */
JP_YYYYMMDDHHMM("yyyy年MM月dd日HH時mm分"),
/** The Jp_ yyyymmddhhmmss. */
JP_YYYYMMDDHHMMSS("yyyy年MM月dd日HH時mm分ss秒");
// ------------------------------
// 変換したいフォーマットを追加(end)
// ------------------------------
/** The format. */
private String format;

/**
* Instantiates a new date util.
*
* @param format
* the format
*/
private DateUtil(String format) {
this.format = format;
}

/**
* To date.
*
* @param dateString
* the date string
* @return the date
*/
public Date toDate(String dateString) {
try {
return new SimpleDateFormat(format).parse(dateString);
} catch (ParseException e) {
// RuntimeExceptionでラップしてしまっていますが、システム共通Exceptionを投げるようにする方がいいと思います。
// 場合によって使い分けてください。
throw new RuntimeException("文字列→Dateへの変換に失敗しました(format:" + format
+ ",string:" + dateString + ")", e);
}
}

/**
* To string.
*
* @param date
* the date
* @return the string
*/
public String toString(Date date) {
return new SimpleDateFormat(format).format(date);
}

}

Main.java


package util;

import java.util.Date;

public class Main {

public static void main(String[] args) {
System.out.println("YYYYMMDD.toDate:"
+ DateUtil.YYYYMMDD.toDate("2012/1/29"));
System.out.println("YYYYMMDDHHMM.toDate:"
+ DateUtil.YYYYMMDDHHMM.toDate("2012/1/29 1:5"));
System.out.println("YYYYMMDDHHMMSS.toDate:"
+ DateUtil.YYYYMMDDHHMMSS.toDate("2012/1/29 1:5.7"));
System.out.println("YYYYMMDDHHMMSSSSS.toDate:"
+ DateUtil.YYYYMMDDHHMMSSSSS.toDate("2012/1/29 1:5.7.100"));
System.out.println("JP_YYYYMMDD.toDate:"
+ DateUtil.JP_YYYYMMDD.toDate("2012年1月29日"));
System.out.println("JP_YYYYMMDDHHMM.toDate:"
+ DateUtil.JP_YYYYMMDDHHMM.toDate("2012年1月29日1時5分"));
System.out.println("JP_YYYYMMDDHHMMSS.toDate:"
+ DateUtil.JP_YYYYMMDDHHMMSS.toDate("2012年1月29日1時5分7秒"));

System.out.println("------------------------------");
System.out.println(DateUtil.YYYYMMDD.toString(new Date()));
System.out.println(DateUtil.YYYYMMDDHHMM.toString(new Date()));
System.out.println(DateUtil.YYYYMMDDHHMMSS.toString(new Date()));
System.out.println(DateUtil.YYYYMMDDHHMMSSSSS.toString(new Date()));
System.out.println(DateUtil.JP_YYYYMMDD.toString(new Date()));
System.out.println(DateUtil.JP_YYYYMMDDHHMM.toString(new Date()));
System.out.println(DateUtil.JP_YYYYMMDDHHMMSS.toString(new Date()));

}
}



実行結果

YYYYMMDD.toDate:Sun Jan 29 00:00:00 JST 2012
YYYYMMDDHHMM.toDate:Sun Jan 29 01:05:00 JST 2012
YYYYMMDDHHMMSS.toDate:Sun Jan 29 01:05:07 JST 2012
YYYYMMDDHHMMSSSSS.toDate:Sun Jan 29 01:05:07 JST 2012
JP_YYYYMMDD.toDate:Sun Jan 29 00:00:00 JST 2012
JP_YYYYMMDDHHMM.toDate:Sun Jan 29 01:05:00 JST 2012
JP_YYYYMMDDHHMMSS.toDate:Sun Jan 29 01:05:07 JST 2012
------------------------------
2012/01/29
2012/01/29 06:27
2012/01/29 18:27.51
2012/01/29 18:27.51.073
2012年01月29日
2012年01月29日18時27分
2012年01月29日18時27分51秒


まぁこんな感じです。
DateUtilクラスでは、フォーマットを定義をして、Date<>Stringのメソッドを共通的に実装してます!
フォーマットごとにDate<>String変換メソッドを実装しなくてすむので、楽です~


※サンプルでは、toDateメソッドにて、ParseExceptionをRuntimeExceptionにラップしてしまっているので、使うときは、システムで使用しているExceptionにラップして使うようにしてください!

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

tilesが動かない!springを使う場合は、tiles用のRequestプロセッサを指定してね。

以下の環境で、strutsのtilesをしようしようとしたら503エラーが。。

struts 1.3.10
spring 2.5

なんでかって。。それは、struts-configで宣言しているcontrollerタグで指定するクラスが間違ってたからでした。

↓の用に設定していましたが、DelegatingRequestProcessorではなDelegatingTilesRequestProcessorでした。。

ダメ

<controller>
<set-property property="processorClass"
value="org.springframework.web.struts.DelegatingRequestProcessor"/>
</controller>


正解

<controller>
<set-property property="processorClass"
value="org.springframework.web.struts.DelegatingTilesRequestProcessor"/>
</controller>

これに数時間はまってましたorz..

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

JSPの上のほうに書く@page contentTypeやカスタムタグの宣言を省略する

きっかけ
JSPを作成する際に上のほうに↓のような宣言をよくかきますよね。。



<%@ page contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>


これをどのすべてのJSPファイルに書くのは面倒だし重複コードになる気がしたので、どうにか省略できる方法はないかと探したら見つけたので、メモしておきます。

<%@ page contentType="text/html;charset=UTF-8"%>の省略
これは、以下のページを見るとわかります。

http://docs.oracle.com/cd/E18355_01/web.1013/B31860-01/jspnls.htm

pageディレクティブでのコンテンツ・タイプの設定

あたり


呼んでいくと
contentTypeエンコーディングを指定しない
かつ
pageEncodingを指定
した場合、contentTypeエンコーディングは、pageEncodingを指定 に従うとあるので、pageEncodingをweb.xmlのjsp-configにて、設定してあげれば、省略することが出来ます。

例えば、①の@pageタグを省略したい場合は、jsp-configには②のように書きます。


<%@ page contentType="text/html;charset=UTF-8"%>



<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
</jsp-property-group>
</jsp-config>

これで、あえて、@pageタグを記載しなければ、すべてのJSPページのcontextTypeは
"text/html;charset=UTF-8"
となります。(JSPファイルの場合、text/html;はデフォルトで付与されるそうです)

カスタムタグの宣言を省略
以下のページに書いてありました。
http://www.atmarkit.co.jp/fjava/javatips/119jspservlet39.html

jsp-configの
include-preludタグを使えばすべてのJSPの上部に任意のJSPを設定できるようです。

で、include-preludタグにカスタムタグの宣言だけ書いてあるJSPを指定すれば、すべてのJSPの上部にカスタムタグの宣言が挿入されるってことです!

ecilpseでjspを作成する際に、このタグを設定してから保管機能(Ctrl+SPとか)を使うと宣言したカスタムタグが保管されるので、便利です。


これでJSPの上のほうがすっきり!

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

tiles:getAsString で取得した値をbean:message のkey属性に値を設定する

tiles.xmlでputタグに設定したvalueの値を、JSP側のbean:messageタグのkey属性に設定しようとしたらはまったので、メモします。

前提

tiles.xml
 ↓の用に定義しています。

<definition name="baseLayout" path="/WEB-INF/jsp/shared/baseLayout.jsp">
<put name="screenId" value="A001"/>
</definition>

bean:messageで取得するリソースファイル
リソースファイルの内容を↓の用に設定して、struts-configにリソースファイルとして定義しておきます。

    A001=A画面

baseLayout.jspでは、tiles.xmlで指定したscreenIdを取得し、リソースファイルに設定した"A画面"という文字をbean:messageタグを使って取得しようとしたとします。

問題

↓の様に書いてもだめでした。。。まぁ当然ですよね。

<bean:message key="<tiles:getAsString name="screenId"/>" />

対策 
bean:defineを使って一旦変数にとり、その後EL式を使って取得するようにしました。

<bean:define id="screenId"><tiles:getAsString name="screenId"/></bean:define>
<bean:message key="${screenId}" />
これで何とかリソースファイルから値を取得することが出来ました。

欠点 

この方法だとtiles.xmlでscreenIdがputタグで定義されていない場合、bean:defineにnullを設定しようとするため、エラーとなってしまいます。。。orz..

欠点を克服できる方法がありましたら、教えていただけるとありがたいです。

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

antのreplaceregrexタスクを使って複数行置換!

前回に引き続きantでreplace文字列のreplaceです~
今回は、ある複数行からある複数行に置換する方法です。

例えばあるファイル(test.xml)内にある①のような複数行の文字列を②に置換したいとします。


<hoge name="a">
aaaa
</hoge>


<piyo name="b">
bbbb
</piyo>

その場合以下のようにreplaceregrexタスクで記載します。

その場合、replace

<replaceregexp file="./test.xml" encoding="UTF-8" flags="gs" replace="&lt;piyo name=&quot;b&quot;&gt;&#10;bbbb&#10;&lt;/piyo&gt;">
<regexp pattern="&lt;hoge name=&quot;a&quot;&gt;.*?aaaa.*?/hoge&gt;" />
</replaceregexp>

のようになります。

ポイントは、flagsに設定している"gs"と文字のエスケープです。


【flagsに設定している"gs"】

g:繰り返し
regexpタスクで指定したpatternに指定した1度該当しても置換を継続して行う。
s:シングルラインモード
通常"."は\nを除く任意の一文字を示すが、シングルラインモードでは、
"."は\nを含む一文字を示します。
シングルラインモードに設定することで、複数行の内容のファイルを1行として扱うことが出来るので、複数行にまたがる正規表現が比較的簡単にかけます。
【文字をエスケープ】
replaceregexpタスクのreplace属性に置換後の複数行の内容を記載するときや
regexpタスクのpattern属性を記載するときは、
antはxml形式で記載するので、xmlの記載方法にのっとって記載しなければなりません。
xmlでは、改行コード、ダブルクオート、<,>をエスケープしないとうまく文字を認識してくれないので、以下のようにエスケープします。

< &lt;
> &gt;
LF &#10;
CR &#13;

例では、改行をLFのみで示しています。
インデントの部分を広報参照で書ければ汎用性が高くなおよいと思いますが、
例では、おこなっていません。

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS