2008年10月22日水曜日

JREのバージョンが違うとこんなエラー

JDK6でコンパイルしたWARファイルをJDK5の環境で実行させたらこんなエラー。
JDKを更新したの忘れてた。

JDK5でコンパイルしなおすか、JDK6のコンパイルオプションでJDK5互換で動作するようにコンパイルしなおすこと。


java.lang.UnsupportedClassVersionError: Bad version number in .class file
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1853)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:875)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1330)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1209)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:207)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:302)
at org.apache.catalina.core.ApplicationFilterConfig.(ApplicationFilterConfig.java:78)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:3635)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4222)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:760)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:740)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:544)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:626)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:553)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:120)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1022)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:736)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:448)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
at org.apache.catalina.startup.Catalina.start(Catalina.java:552)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)


以上

2008年10月21日火曜日

PDFをHTMLに変換

HTMLをPDFに変換するプロダクトは数多くあるけれど
PDFをHTMLに変換するプロダクトは本当にすくない。
なにかよい手はないかとお悩みのあなたに以下がお勧め

poppler
http://poppler.freedesktop.org/

かなりいい精度で変換してくれる。
linux、solaris環境では動作確認しました。
solarisはコンパイルオプションつけないとだめ。

htmlのtitle属性と索引ページが日本語はいっていると文字化けするのでその辺は注意してください。
他のページは日本語でもOKです。テーブルもきれいに変換してくれます。

がんがん更新されてるので期待してます。

以上

2008年10月20日月曜日

VBSでUNICDEの全角文字数を判断する関数


' UNICODEでも全角半角の文字数を判断するメソッド
Function LenByte(ByVal s)

Dim c, i, k

c = 0

For i = 0 To Len(s) - 1

k = Mid(s, i + 1, 1)

If (Asc(k) And &HFF00) = 0 Then

c = c + 1

Else

c = c + 2

End If

Next

LenByte = c

End Function

Hpricotでテキスト表示させるとスペースが「?」になる

require 'hpricot'
require 'hpricot/xchar'

Hpricot::XChar::PREDEFINED_U.merge!({" " => 32})
または
Hpricot::XChar::PREDEFINED_U.update({" " => 32})

doc = Hpricot('

before after

')
puts doc.inner_text


スペースへのエンコード対応表がないので原因。

2008年10月17日金曜日

ワイルドカードでファイル名を抜き出す

ほんとすぐ忘れる。

# ワイルドカード指定
Dir.glob("D:/temp/test/*.xml") {|f|
p f
}

# ワイルドカード指定してなくともよい
Dir.glob("D:/temp/test/test.xml") {|f|
p f
}

REXMLを使ってXMLファイル作成

すぐに忘れるので備忘録。

doc = REXML::Document.new
doc << REXML::XMLDecl.new('1.0', 'UTF-8')
tag_1 = doc.add_element("tag_1")
tag_1_1 = tag_1.add_element("tag_1_1").add_text("ああああ")
tag_1_2 = tag_1.add_element("tag_1_2").add_text("いいいい")

tag_2 = doc.add_element("tag_2")
tag_2_1 = tag_2.add_element("tag_2_1").add_text("かかかか")

# 標準出力へ
doc.write STDOUT

# ファイル出力
File.open("sample.xml", "w") {|fp|
fp.write(doc)
}

以上

2008年10月16日木曜日

HtmlStringBufferを使いやすくする

Click FrameworkのHtmlStringBufferを使うと行数が増えてつかいづらかったので以下のようにしてみました。
以下のCustomHtmlStringBufferを利用するとコードの記述量がへります。

改善前
CustomHtmlStringBuffer customHtmlStringBuffer = new CustomHtmlStringBuffer();
customHtmlStringBuffer.elementStart("div");
customHtmlStringBuffer.appendAttribute("id", "test_id");
customHtmlStringBuffer.closeTag();
customHtmlStringBuffer.append("これはテストです。");
customHtmlStringBuffer.elementEnd("div");

改善後
CustomHtmlStringBuffer customHtmlStringBuffer = new CustomHtmlStringBuffer();
customHtmlStringBuffer.elementStart("div").appendAttribute("id", "test_id").closeTag().append("これはテストです。").elementEnd("div");

ね、記述しやすくなったでしょ。




import java.util.Map;

import net.sf.click.util.HtmlStringBuffer;

public class CustomHtmlStringBuffer {

protected HtmlStringBuffer htmlStringBuffer;

public CustomHtmlStringBuffer() {

htmlStringBuffer = new HtmlStringBuffer();

}

public CustomHtmlStringBuffer append(char value) {
htmlStringBuffer.append(value);
return this;
}

public CustomHtmlStringBuffer append(int value) {
htmlStringBuffer.append(value);
return this;
}

public CustomHtmlStringBuffer append(long value) {
htmlStringBuffer.append(value);
return this;
}

public CustomHtmlStringBuffer append(Object value) {
htmlStringBuffer.append(value);
return this;
}

public CustomHtmlStringBuffer append(String value) {
htmlStringBuffer.append(value);
return this;
}

public CustomHtmlStringBuffer appendAttribute(String name, int value) {
htmlStringBuffer.appendAttribute(name, value);
return this;
}

public CustomHtmlStringBuffer appendAttribute(String name, Object value) {
htmlStringBuffer.appendAttribute(name, value);
return this;
}

public CustomHtmlStringBuffer appendAttributeDisabled() {
htmlStringBuffer.appendAttributeDisabled();
return this;
}

public CustomHtmlStringBuffer appendAttributeReadonly() {
htmlStringBuffer.appendAttributeReadonly();
return this;
}

public CustomHtmlStringBuffer appendAttributes(Map attributes) {
htmlStringBuffer.appendAttributes(attributes);
return this;
}

public CustomHtmlStringBuffer appendEscaped(Object value) {
htmlStringBuffer.appendEscaped(value);
return this;
}

public CustomHtmlStringBuffer appendStyleAttributes(Map attributes) {
htmlStringBuffer.appendStyleAttributes(attributes);
return this;
}

public CustomHtmlStringBuffer closeTag() {
htmlStringBuffer.closeTag();
return this;
}

public CustomHtmlStringBuffer elementEnd() {
htmlStringBuffer.elementEnd();
return this;
}

public CustomHtmlStringBuffer elementEnd(String name) {
htmlStringBuffer.elementEnd(name);
return this;
}

public CustomHtmlStringBuffer elementStart(String name) {
htmlStringBuffer.elementStart(name);
return this;
}

public boolean equals(Object obj) {
return htmlStringBuffer.equals(obj);
}

public int hashCode() {
return htmlStringBuffer.hashCode();
}

public boolean isJavaScriptAttribute(String name) {
return htmlStringBuffer.isJavaScriptAttribute(name);
}

public int length() {
return htmlStringBuffer.length();
}

public String toString() {
return htmlStringBuffer.toString();
}

}

2008年10月15日水曜日

ludiaの検索書式で括弧を普通の文字列同様に扱いたい

ludiaでは括弧は検索書式(正確にはsenna)の一種なので以下のような検索文字列だと思ったとおりにヒットしない。

例:(aaaaa)

ただし、2重引用符で囲めばできるけどそれだと以下のような場合に不便

例:product_name (aaaaaa) 

上記をワンフレーズとして検索するなら2重引用符で囲めばいいけど
「product_name」「 (aaaaaa) 」という文字列をそれぞれ含む場合は思ったとおりにヒットしない。
なんかやり方ありますかなー?

2008年10月14日火曜日

SelectQueryでSQLの独自演算子を使う方法

SelectQueryで独自演算子を利用したいが基本的にはCayenne自身のSQLパーサーにひっかかってエラーになってしまう。なにかよい方法はないだろうか?
Postgresみたいな独自演算子を利用するときなんてとくに不便だ。
なんとかならかなー。ludiaとかも使いたいし。

2008年10月10日金曜日

Cayenneでキャッシュする

■DataContext(ObjectContext)のインスタンス毎にキャッシュする。

SelectQuery query = new SelectQuery(Tag.class);
query.setName("sql1");
query.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);//…①
// 初回はSQL実行
List res = dataContext.performQuery(query);


// 2回目以降はキャッシュから取得
res = dataContext().performQuery(query);


// 再度SQLを実行
query.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);//…②
res = dataContext().performQuery(query);


■VMのインスタンス毎にキャッシュする。
①②を以下のようにすればよい。
①:QueryCacheStrategy.SHARED_CACHE
②:QueryCacheStrategy.SHARED_CACHE_REFRESH

■異なるVMのインスタンス毎にもキャッシュできる
あとで記載。

以上

2008年10月9日木曜日

SQLTemplateのバグ?

CayenneのSQLTemplateでページサイズを指定してかつ
独自の型(例えばList)をその取得カラム指定して実行すると
2ページ目以降がエラーになるなんで?

2008年10月7日火曜日

Cayenneでデフォルトトランザクションを実行させない方法(つづき)

前回のブログではCayenneにデフォルトトランザクションを実行させない方法を記載した。
がしかしこれではちょっとつかいずらいのではと思ったので以下のようなクラスを作成してみました。

import org.apache.cayenne.access.Transaction;

public abstract class NoTransactionSqlExecutioner {

protected Object[] prm;

public NoTransactionSqlExecutioner() {

}

public NoTransactionSqlExecutioner(Object... prm) {
this.prm = prm;
}

public Object getPrm(int index) {

if (prm == null)
throw new RuntimeException("paramter not setting");

if (prm.length < index)
throw new RuntimeException("paramter out bound ");

return prm[index];

}

public Object exec(){

Transaction tx = new NoTransaction();
try {

Transaction.bindThreadTransaction(tx);
return execDetail();

}
finally {

Transaction.bindThreadTransaction(null);
try {
tx.commit();
} catch (Exception e) {
}

}

}

public abstract Object execDetail();

}


これを利用して以下のようします。
ちょっとはすっきりしたかな。JAVAでインナークラス内でローカル変数つかえればもっと楽になるんだけど。finalで宣言するのも面倒だし...。

public List sampleMethod(String parameter1) {

List list = (List)new NoTransactionSqlExecutioner(parameter1) {

public Object execDetail() {

Expression expression = ExpressionFactory.matchExp(SampleTable.COL1_TYPE_PROPERTY, getPrm(0));
SelectQuery query = new SelectQuery(Tag.class, expression);
return (List)getDataContext().performQuery(query);

}

}.exec();

return list;

}

以上

Cayenneでデフォルトトランザクションを実行させない方法

CayenneではすべてのSQLについてデフォルトで自動トランザクションが開始される。
しかし、この自動トランザクションのおかげでpgpool等で参照系のSQL(つまりSELECT)を複数DBサーバーの分散処理をさせたい場合、トランザクションが開始されると分散したDBへすべて同じSQLが実行されてしまう。これではDBを分散した意味がない(更新系ならいいけど…)。

そこでCayenneを自動でトランザクションしないようにする。

まずはCayenneのTransactionクラスを継承した以下のクラスを作成する。



import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;

import org.apache.cayenne.CayenneException;
import org.apache.cayenne.access.Transaction;

public class NoTransaction extends Transaction {

public void begin() {
}

public void commit() throws IllegalStateException, SQLException, CayenneException {
close();
}

public void rollback() throws IllegalStateException, SQLException, CayenneException {
close();
}

protected void close() {

if (connections == null || connections.isEmpty()) {
return;
}

Iterator it = connections.values().iterator();
while (it.hasNext()) {
try {

((Connection) it.next()).close();
}
catch (Throwable th) {
}
}

}

}



これを以下のように利用することでトランザクションは開始されない。

Transaction tx = new NoTransaction();
try {
Transaction.bindThreadTransaction(tx);

Expression expression = ExpressionFactory.matchExp(SampleTable);
SelectQuery query = new SelectQuery(SampleTable.class, expression);
List list = getDataContext().performQuery(query);
/* あとは個別処理 を記載 */

}
finally {

Transaction.bindThreadTransaction(null);
try {
tx.commit();
} catch (Exception e) {
}

}


以上

cayenneのキャッシュをクリア

DataContext dataontext = new DataContext.createDataContext();
List dataRows = dataontext.performQuery(selectQuery);
context.getObjectStore().getDataRowCache().clear();
DataRowレベルのキャッシュ時にクリアする方法。

以上

2008年10月6日月曜日

Click FrameworkのCheckBoxコントロールをグループ化するコントロールを作成する

デフォルトではRadioGroupコントロールをグループ化するコントロールとしてRadioGroupコントロールが存在しますがCheckBoxについてはありません。ですのでRadioGroupコントロールを参考に作成しましょう。
面倒なのでJavaScriptによるバリデーションはなにも実装しません。
実装時に利用者が個々に実装すればよいでしょう(「2つチェックボックス選択が必須」等)。

追加機能としてチェックボックスのラベル表示位置を設定するメソッドとそのGetter,Setterを追加した。
もともとのRadioGroupではラベルの表示位置設定がないのでこまったので。
/*
*
*/

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sf.click.control.Checkbox;
import net.sf.click.control.Field;
import net.sf.click.control.Form;
import net.sf.click.util.HtmlStringBuffer;
import net.sf.click.util.PropertyUtils;

public class CheckBoxGroup extends Field {

private static final long serialVersionUID = 1L;

protected List checkBoxList;

protected boolean isVerticalLayout = true;

protected int labelPosition = LABEL_POSITION_RIGHT;

protected static final int LABEL_POSITION_NONE = 0;
protected static final int LABEL_POSITION_LEFT = 1;
protected static final int LABEL_POSITION_RIGHT = 2;

public CheckBoxGroup(String name) {
super(name);
}

public CheckBoxGroup(String name, boolean required) {
super(name);
setRequired(required);
}

public CheckBoxGroup(String name, String label) {
super(name, label);
}

public CheckBoxGroup(String name, String label, boolean required) {
super(name, label);
setRequired(required);
}

public CheckBoxGroup() {
super();
}

public void add(Checkbox checkBox) {
if (checkBox == null) {
throw new IllegalArgumentException("Null checkbox parameter");
}

checkBox.setParent(this);
getCheckBoxList().add(checkBox);
if (getForm() != null) {
checkBox.setForm(getForm());
}
}

public void addAll(Collection options) {
if (options == null) {
String msg = "options parameter cannot be null";
throw new IllegalArgumentException(msg);
}
for (Iterator i = options.iterator(); i.hasNext();) {
Checkbox checkBox = (Checkbox) i.next();
add(checkBox);
}
}

public void addAll(Map options) {
if (options == null) {
String msg = "options parameter cannot be null";
throw new IllegalArgumentException(msg);
}
for (Iterator i = options.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
Checkbox checkBox = new Checkbox(entry.getKey().toString(),
entry.getValue().toString());
add(checkBox);
}
}

public void addAll(Collection objects, String value, String label) {
if (objects == null) {
String msg = "objects parameter cannot be null";
throw new IllegalArgumentException(msg);
}
if (value == null) {
String msg = "value parameter cannot be null";
throw new IllegalArgumentException(msg);
}
if (label == null) {
String msg = "label parameter cannot be null";
throw new IllegalArgumentException(msg);
}

if (objects.isEmpty()) {
return;
}

Map cache = new HashMap();

for (Iterator i = objects.iterator(); i.hasNext();) {
Object object = i.next();

try {
Object valueResult = PropertyUtils.getValue(object, value, cache);
Object labelResult = PropertyUtils.getValue(object, label, cache);

Checkbox checkBox = null;

if (labelResult != null) {
checkBox = new Checkbox(valueResult.toString(), labelResult.toString());

} else {
checkBox = new Checkbox(valueResult.toString());
}

add(checkBox);

} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

public void setForm(Form form) {
super.setForm(form);
if (hasCheckBoxes()) {
for (int i = 0, size = getCheckBoxList().size(); i < checkbox =" (Checkbox)" isverticallayout =" vertical;" checkboxlist ="="" checkboxlist =" new" i =" 0," size =" getCheckBoxList().size();" checkbox =" (Checkbox)" continueprocessing =" true;" i =" 0," size =" getCheckBoxList().size();" checkbox =" (Checkbox)" continueprocessing =" checkBox.onProcess();" i =" 0," size =" getCheckBoxList().size();" checkbox =" (Checkbox)" size =" getCheckBoxList().size();" buffer =" new" value =" getValue();" i =" 0;" checkbox =" (Checkbox)"> 0) {
if (checkBox.getValue().equals(value)) {
checkBox.setChecked(true);
} else {
checkBox.setChecked(false);
}
}

if (labelPosition != LABEL_POSITION_NONE && labelPosition == LABEL_POSITION_LEFT) {

buffer.elementStart("label");
buffer.appendAttribute("for", checkBox.getId());
buffer.closeTag();
buffer.appendEscaped(checkBox.getLabel());
buffer.elementEnd("label");

}

buffer.append(checkBox.toString());

if (labelPosition != LABEL_POSITION_NONE && labelPosition == LABEL_POSITION_RIGHT) {

buffer.elementStart("label");
buffer.appendAttribute("for", checkBox.getId());
buffer.closeTag();
buffer.appendEscaped(checkBox.getLabel());
buffer.elementEnd("label");

}

if (isVerticalLayout() && (i < size - 1)) {                 buffer.append("
");
}

}

return buffer.toString();
}

public void validate() {
setError(null);

if (isRequired() && getValue().length() == 0) {
setErrorMessage("select-error");
}
}

public int getLabelPosition() {
return labelPosition;
}

public void setLabelPosition(int labelPosition) {
this.labelPosition = labelPosition;
}



}
以上

2008年10月3日金曜日

Cayenneで全エンティティオブジェクトに共通のメソッドを持たせる

以下のようにして全エンティティ共通のメソッドを追加する。
getObjEntity().getName()でオブジェクト名(テーブル名)を渡しているところがポイント。

public class CustomCayenneDataObject extends CayenneDataObject {

/*
* 主キーで検索するメソッド
*/
public Object selectById(Object id) {

ObjectIdQuery query = new ObjectIdQuery(new ObjectId(getObjEntity().getName(), ID_PK_COLUMN, id));
return DataObjectUtils.objectForQuery(getObjectContext(), query);

}

/*
* 複数の主キーを指定して検索するメソッド
*/
public List selectByIdList(List idList) {

checkObjectContext();

if (CollectionUtils.isEmpty(idList))
return null;

Expression expression = null;
expression = ExpressionFactory.inDbExp(ID_PK_COLUMN, idList);
// expression = ExpressionFactory.inExp(ID_PK_COLUMN, idList); // これだとだめ。変だろ!
SelectQuery query = new SelectQuery(getObjEntity().getName(), expression);
List objectList = getObjectContext().performQuery(query);
if (CollectionUtils.isEmpty(objectList))
return null;

return objectList;

}


}

cayenneでIN句に主キーを利用する

CayenneでIN句に主キーを利用しようとした場合
inExpメソッドではなく、 inDbExpを利用しないとだめ。なんで?

List idList = new ArrayList();
idList.add("pk00001");
idList.add("pk00002");
idList.add("pk00003");
idList.add("pk00004");

Expression expression = null;

// ①:これは正常に動作
expression = ExpressionFactory.inDbExp(TestTable.ID_PK_COLUMN, idList);

// ②:これは異常終了。 これつかんのは変だよなー
// expression = ExpressionFactory.inExp(TestTable.ID_PK_COLUMN, idList);

SelectQuery query = new SelectQuery(TestTable.class, expression);

List testTableList = getObjectContext().performQuery(query);

以上

JRubyでjava.util.Dateクラスを利用する

JRubyでは「java.util.Date」クラスはうまく相互変換してくれない場合がある。

例えば
現在の日時を取得したい場合に
today = Time.nowでrubyのDateクラスを取得する。

そして

public void sample_method(Date prm)

みたいなjavaの「java.util.Date」クラスを引数とするメソッドに
todayを引き渡す場合はうまくいくが

todayをハッシュマップ等でラップして引数として渡した場合はうまく変換してくれない。

public void sample_method(Map prm2)

param = {}
param["today_1"] = today

これはJRuby側で「java.util.Date」をみつけられないらしくJRubyオブジェクトのままで
わたしてしまうのが原因ぽい。

なのでtodayを作るときにrubyのオブジェクトとしてではなく以下のように最初から「java.util.Date」として作成してあげればうまくいく。

today = java.util.Date

以上