バッチ実行にSpring Batchを利用しようかと検討しています。
jBatchがJavaEEに組み込まれたこともあり、
それに準拠した形でバッチを作ったほうがなにかと恩恵があるのかなと思ったというだけですが。
で、Spring BatchにはSpring Batch AdminというWebUIがあるので
使ってみます。あくまで、実行、停止、実行履歴が見れる程度のものです。
標準のdatabaseがhsqldbとなっているのでMySQLに変更します。
Adminのテンプレートでは
META-INF/batch-hsql.properties
というファイルがあるので次のファイルに変更します。
META-INF/batch-mysql.properties:
batch.jdbc.driver=com.mysql.jdbc.Driver
batch.jdbc.url=jdbc:mysql://localhost/test
batch.jdbc.user=user
batch.jdbc.password=
batch.jdbc.testWhileIdle=true
batch.jdbc.validationQuery=SELECT 1
batch.jdbc.maxActive=70
batch.jdbc.initialSize=20
batch.schema.script=classpath:/org/springframework/batch/core/schema-mysql.sql
batch.drop.script=classpath*:/org/springframework/batch/core/schema-drop-mysql.sql
batch.business.schema.script=classpath:/business-schema-mysql.sql
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer
# Non-platform dependent settings that you might like to change
batch.data.source.init=false
batch.data.source.init=falseはdatabase初期化するかという設定です。
一番最初に起動するときのみtrueにする必要があります。
このままだと、遅いので dataSourceをoverrideします。
次のファイルを作成します。
META-INF/spring/batch/override/war-content.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id=”dataSource” class=”org.apache.commons.dbcp.BasicDataSource”>
<property name=”driverClassName” value=”${batch.jdbc.driver}” />
<property name=”url” value=”${batch.jdbc.url}” />
<property name=”username” value=”${batch.jdbc.user}” />
<property name=”password” value=”${batch.jdbc.password}” />
<property name=”initialSize” value=”${batch.jdbc.initialSize}”/>
<property name=”maxActive” value=”${batch.jdbc.maxActive}”/>
</bean>
</beans>
実行時に引数を渡す必要があります。
-DENVIRONMENT=mysql
なのでmaven でデバッグするときは 次のようになる
jetty:run -Dmaven.surefire.debug test -DENVIRONMENT=mysql
せっかくなのでスケジュールに機能もつけてみました→次
2014年5月15日木曜日
2014年5月13日火曜日
Jetty 9.1.* の設定について
jetty 9.1.*系以降jettyの一部がモジュール化され
設定項目体系が変わっています
設定方法についてをすこしまとめておきます。
1.httpsを有効化
ダウンロードしたアーカイブのdemo-base以下にあるstart.d/ssl.ini、https.iniが参考になります
demo-baseのstart.d/ssl.ini、https.iniを start.dにコピペして
環境に合わせて修正する
2. jetty-envを有効化
そのままでOKただし、pom.xmlにjetty関連のdependancyがある場合
loaderエラーになる可能性があるのでjetty pluginでのみ必要な物は
jetty plugin 以下のdependancyに移す
3. jspを有効化
start.iniの--module=jspのコメントを外す
4. servletを有効化
defaultでservletsモジュールは読み込まれないので
jetty-servlets を使う場合は、
start.iniに--module=servletsを追加
5. リクエストログの有効化
start.iniに--module=requestlogを追加
等々
9.1.4はログ出力のリンク回りがバグっているのかjetty.shが動かない場合があるので注意
多分9.1.5で修正ずか?
設定項目体系が変わっています
設定方法についてをすこしまとめておきます。
1.httpsを有効化
ダウンロードしたアーカイブのdemo-base以下にあるstart.d/ssl.ini、https.iniが参考になります
demo-baseのstart.d/ssl.ini、https.iniを start.dにコピペして
環境に合わせて修正する
2. jetty-envを有効化
そのままでOKただし、pom.xmlにjetty関連のdependancyがある場合
loaderエラーになる可能性があるのでjetty pluginでのみ必要な物は
jetty plugin 以下のdependancyに移す
3. jspを有効化
start.iniの--module=jspのコメントを外す
4. servletを有効化
defaultでservletsモジュールは読み込まれないので
jetty-servlets を使う場合は、
start.iniに--module=servletsを追加
5. リクエストログの有効化
start.iniに--module=requestlogを追加
等々
9.1.4はログ出力のリンク回りがバグっているのかjetty.shが動かない場合があるので注意
多分9.1.5で修正ずか?
2014年4月17日木曜日
UUID <-> Base64 変換する Jackson用wrapperクラス
データベースでUUIDを何かと使うのですが、
JSONで出力すると'afb055a0-c18f-11e3-8b27-b1398a321474'みないな
文字列になってちょっと長いですし、
UUIDだというのがバレバレでそこはかとなく心配になります。
なので、JSONマッパーに渡すUUIDをwrapしてBase64としてマップするクラスを作ったので共有
表題にある通りJackson向けです
@JsonValueアノテーションをつけることで
このクラスをシリアライズする際にtoString()が呼ばれBase64化された値を返します
デシリアライズ 時には、
UUIDBase64(String uuidBase64)で初期化されUUIDBase64クラスとしてマップされます。
UUIDBase64.java :
import java.nio.ByteBuffer;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.codehaus.jackson.annotate.JsonValue;
import org.codehaus.jackson.annotate.JsonIgnore;
public class UUIDBase64 {
@JsonIgnore
private final UUID uuid;
public UUIDBase64(UUID uuid) {
this.uuid = uuid;
}
public UUIDBase64(String uuidBase64) {
byte[] bytes = Base64.decodeBase64(uuidBase64);
ByteBuffer bb = ByteBuffer.wrap(bytes);
this.uuid = new UUID(bb.getLong(), bb.getLong());
}
@JsonValue
public String toString(){
if (uuid == null)
return null;
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return Base64.encodeBase64URLSafeString(bb.array());
}
public UUID getUUID() {
return this.uuid;
}
public static UUIDBase64 fromUUID(UUID uuid) {
return new UUIDBase64(uuid);
}
public static UUIDBase64 fromBase64(String str) {
return new UUIDBase64(str);
}
}
次のように変換されます
class User {
UUIDBase64 id;
}
<-> { "id" : "base64の文字列" }->
JSONで出力すると'afb055a0-c18f-11e3-8b27-b1398a321474'みないな
文字列になってちょっと長いですし、
UUIDだというのがバレバレでそこはかとなく心配になります。
なので、JSONマッパーに渡すUUIDをwrapしてBase64としてマップするクラスを作ったので共有
表題にある通りJackson向けです
@JsonValueアノテーションをつけることで
このクラスをシリアライズする際にtoString()が呼ばれBase64化された値を返します
デシリアライズ 時には、
UUIDBase64(String uuidBase64)で初期化されUUIDBase64クラスとしてマップされます。
UUIDBase64.java :
import java.nio.ByteBuffer;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.codehaus.jackson.annotate.JsonValue;
import org.codehaus.jackson.annotate.JsonIgnore;
public class UUIDBase64 {
@JsonIgnore
private final UUID uuid;
public UUIDBase64(UUID uuid) {
this.uuid = uuid;
}
public UUIDBase64(String uuidBase64) {
byte[] bytes = Base64.decodeBase64(uuidBase64);
ByteBuffer bb = ByteBuffer.wrap(bytes);
this.uuid = new UUID(bb.getLong(), bb.getLong());
}
@JsonValue
public String toString(){
if (uuid == null)
return null;
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return Base64.encodeBase64URLSafeString(bb.array());
}
public UUID getUUID() {
return this.uuid;
}
public static UUIDBase64 fromUUID(UUID uuid) {
return new UUIDBase64(uuid);
}
public static UUIDBase64 fromBase64(String str) {
return new UUIDBase64(str);
}
}
次のように変換されます
class User {
UUIDBase64 id;
}
<-> { "id" : "base64の文字列" }->
2014年4月11日金曜日
HttpServletRequestのthread内共有 Spring編
HttpServletRequestを参照しようとした時に
引数で渡さずに、static fieldのthread localに保存して
スレッド内から参照できるようにする方法を昔紹介していましたが、
Spring使用時には標準の機能としてすでにあります。
次のようにRequestContextHolderからHttpServletRequestを取得します。
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
HttpServletRequest request =
(HttpServletRequest)attrs.resolveReference(RequestAttributes.REFERENCE_REQUEST);
web.xmlにlistenerを登録する必要があります。
web.xml:
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
Spring 2.5以上が必要です
使い道としては、エラー出力にリクエスト情報を出すとか
エラーハンドルのためにいちいち、
リクエストを引数には渡せないのでthreadに保持しているものを参照します。
ThreadLocalに自分で保存する記事よりは
既存のものを使うべきでしょう
引数で渡さずに、static fieldのthread localに保存して
スレッド内から参照できるようにする方法を昔紹介していましたが、
Spring使用時には標準の機能としてすでにあります。
次のようにRequestContextHolderからHttpServletRequestを取得します。
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
HttpServletRequest request =
(HttpServletRequest)attrs.resolveReference(RequestAttributes.REFERENCE_REQUEST);
web.xmlにlistenerを登録する必要があります。
web.xml:
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
Spring 2.5以上が必要です
使い道としては、エラー出力にリクエスト情報を出すとか
エラーハンドルのためにいちいち、
リクエストを引数には渡せないのでthreadに保持しているものを参照します。
ThreadLocalに自分で保存する記事よりは
既存のものを使うべきでしょう
2014年3月28日金曜日
Spring Security Auto Login
Spring Security を利用してログインセッション管理を
している方も多いと思います。
Spring Securityデフォルトのままで使うと
アカウント作成後に一度ユーザにログインしてもらわないといけないとか
イケてないので、そういう時に自動でログインセッションを作成する方法です
アカウント作成後に次のようなメソッドを呼びます
AuthenticationProvideのBeanを指定します(自作クラスでも動きます)
@Autowired
AuthenticationProvider authenticationProvider;
HttpServletRequestを予め取得しておいて、username, passwordから
tokenを発行し、authenticationProviderに登録します
private void doAutoLogin(String username, String password,
HttpServletRequest request) {
try {
// Must be called from request filtered by Spring Security,
// otherwise SecurityContextHolder is not updated
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
username, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticationProvider
.authenticate(token);
// logger.debug("Logging in with [{}]",
// authentication.getPrincipal());
SecurityContextHolder.getContext()
.setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
logger.error("Failure in autoLogin", e);
}
}
AutowiredしなくてもSecurityContextHolderからいけるかも
しれないですね。そのうちやってみよ。
している方も多いと思います。
Spring Securityデフォルトのままで使うと
アカウント作成後に一度ユーザにログインしてもらわないといけないとか
イケてないので、そういう時に自動でログインセッションを作成する方法です
アカウント作成後に次のようなメソッドを呼びます
AuthenticationProvideのBeanを指定します(自作クラスでも動きます)
@Autowired
AuthenticationProvider authenticationProvider;
HttpServletRequestを予め取得しておいて、username, passwordから
tokenを発行し、authenticationProviderに登録します
private void doAutoLogin(String username, String password,
HttpServletRequest request) {
try {
// Must be called from request filtered by Spring Security,
// otherwise SecurityContextHolder is not updated
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
username, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticationProvider
.authenticate(token);
// logger.debug("Logging in with [{}]",
// authentication.getPrincipal());
SecurityContextHolder.getContext()
.setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
logger.error("Failure in autoLogin", e);
}
}
AutowiredしなくてもSecurityContextHolderからいけるかも
しれないですね。そのうちやってみよ。
2014年3月25日火曜日
Session Counter Listener
セッション数をカウントするServlet コンテナ用のListenerを自作したので共有
サービスのセッション数 を監視してーという人はどうぞ
thread safeなはず。 Java1.5以上必要です
SessionCounterListener.java:
public class SessionCounterListener implements HttpSessionListener {
private static AtomicInteger totalActiveSessions = new AtomicInteger();
public static int getTotalActiveSession() {
return totalActiveSessions.get();
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
totalActiveSessions.incrementAndGet();
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
totalActiveSessions.decrementAndGet();
}
}
web.xmlにはListenerとして登録
web.xml :
<listener>
<listener-class>my.package.SessionCounterListener</listener-class>
</listener>
サービスのセッション数 を監視してーという人はどうぞ
thread safeなはず。 Java1.5以上必要です
SessionCounterListener.java:
public class SessionCounterListener implements HttpSessionListener {
private static AtomicInteger totalActiveSessions = new AtomicInteger();
public static int getTotalActiveSession() {
return totalActiveSessions.get();
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
totalActiveSessions.incrementAndGet();
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
totalActiveSessions.decrementAndGet();
}
}
web.xmlにはListenerとして登録
web.xml :
<listener>
<listener-class>my.package.SessionCounterListener</listener-class>
</listener>
Cassandra3.x CQLでBlobを扱う
CassandraでのBlobの扱いに半日ぐらい使ったので覚書
Cassandra3.0、datastax driver 2.0
でCQLを利用してBlobを扱う場合、
bindにByteBufferをかまさないといけません。
それと、row.getBytes()で取得したByteBufferをそのままarray()しても
ゴミの入ったbyte[]になってしまいます。
なので、次のようにする
bind時
byte[] byteArray;
prepare(cql).bind(ByteBuffer.wrap(byteArray));
取得時
import com.datastax.driver.core.utils.Bytes;
byte[] byteArray = Bytes.getArray(row.getBytes("bytes"));
Cassandra3.0、datastax driver 2.0
でCQLを利用してBlobを扱う場合、
bindにByteBufferをかまさないといけません。
それと、row.getBytes()で取得したByteBufferをそのままarray()しても
ゴミの入ったbyte[]になってしまいます。
なので、次のようにする
bind時
byte[] byteArray;
prepare(cql).bind(ByteBuffer.wrap(byteArray));
取得時
import com.datastax.driver.core.utils.Bytes;
byte[] byteArray = Bytes.getArray(row.getBytes("bytes"));
登録:
投稿 (Atom)