JerseyはレスポンスとしてXMLを前提にしていて
JSON扱おうとするとたまに問題が起こるのでその対応をメモ
1.Listに要素がひとつしかないとArrayではなくObject渡してくる
このあたり参考にして、サーブレットを登録しているパッケージ中に
@Providerを定義する
http://tugdualgrall.blogspot.jp/2011/09/jax-rs-jersey-and-single-element-arrays.html
2.…to be continued
もしくは、stackoverflowにJSONパーサをJacksonに変更する方法が乗っていたので
これを利用すると全て解決しそう(うまく動かなかった)
how-can-i-customize-serialization-of-a-list-of-jaxb-objects-to-json
追記:
JSONの扱いは上記の方法を取る必要はなく、POJOMappingFeatureを利用すればいいもよう
Web.xmlのサーブレット定義に追加します
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
2012年12月23日日曜日
wine on mac でつまづいたこと
wineでアプリをインストールするにはwinetricksを利用するのですが
winetricksの挙動がおかしく、アプリ選択してインストールしようとしても
一向に先に進まない現象が起こっていました。
解決方法というか回避策としてコンソールからインストールしました。
1.winetricksで対象のアプリの一覧を取得する
winetricks apps list | grep -i "hoge"
2. 取得したアプリ名を指定してインストール
winetricks hoge
#手動でX window 入れた後にwine入れて競合してるのが問題なのだろうか
winetricksの挙動がおかしく、アプリ選択してインストールしようとしても
一向に先に進まない現象が起こっていました。
解決方法というか回避策としてコンソールからインストールしました。
1.winetricksで対象のアプリの一覧を取得する
winetricks apps list | grep -i "hoge"
2. 取得したアプリ名を指定してインストール
winetricks hoge
#手動でX window 入れた後にwine入れて競合してるのが問題なのだろうか
2012年12月9日日曜日
Java 重み付けランダム抽選
車輪の再発明かもしれませんが、
重み付けをしたランダム抽選ロジック作ったので共有
package org.chikishe.Util;
import java.util.ArrayList;
import java.util.Collection;
public class RandomUtil {
@SuppressWarnings("unchecked")
public static <T> T weightedRandom(Collection<T> resource,
Weight<T> delegate) throws Exception {
ArrayList<WeightCompare> weightedResource = new ArrayList<WeightCompare> ();
int totalWeight = 0;
for (T temp : resource) {
int weight = delegate.getWeight(temp);
if (weight <= 0)
continue;
totalWeight += weight;
weightedResource.add(new WeightCompare(temp, totalWeight));
}
if (totalWeight <= 0)
throw new Exception("No Resource");
int random = randomNext(1, totalWeight + 1);
int lower = -1;
int upper = weightedResource.size();
while (upper - lower > 1) {
int index = (lower + upper) / 2;
if (weightedResource.get(index).weight >= random) {
upper = index;
} else {
lower = index;
}
}
return (T) weightedResource.get(upper).obj;
}
public static int randomNext(int from, int to) {
return (int) (Math.random() * (to - from) + from);
}
public interface Weight<T> {
int getWeight(T temp);
}
private static class WeightCompare {
private Object obj;
private int weight;
public WeightCompare(Object obj, int weight) {
this.obj = obj;
this.weight = weight;
}
}
}
呼び出しは、次のようになります。
public String getTest4() {
ArrayList<TestMember> members = new ArrayList<TestMember>();
members.add(new TestMember(1, 50));
members.add(new TestMember(2, 80));
members.add(new TestMember(3, 130));
try {
TestMember member = RandomUtil.weightedRandom(members,
new Weight<TestMember>() {
public int getWeight(TestMember m) {
return m.weight;
}
});
return "" + member.id;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new WebApplicationException(500);
}
}
private class TestMember {
public int id;
public int weight;
public TestMember(int id, int weight) {
this.id = id;
this.weight = weight;
}
}
RandomUtil.weightedRandomはジェネリック型を受け取るようにしているので
第1引数のリストに指定した型を返します。
第2引数には重み付けを与えるメソッドの定義を渡しています。
サンプルの場合は、リストのTestMemberクラスのweightを重みとして取得しています。
ジェネリックの使い方がようやくわかってきた。
追記
2014/01/* Java 8でのラムダ式を使った使用方法はこちら
重み付けをしたランダム抽選ロジック作ったので共有
package org.chikishe.Util;
import java.util.ArrayList;
import java.util.Collection;
public class RandomUtil {
@SuppressWarnings("unchecked")
public static <T> T weightedRandom(Collection<T> resource,
Weight<T> delegate) throws Exception {
ArrayList<WeightCompare> weightedResource = new ArrayList<WeightCompare> ();
int totalWeight = 0;
for (T temp : resource) {
int weight = delegate.getWeight(temp);
if (weight <= 0)
continue;
totalWeight += weight;
weightedResource.add(new WeightCompare(temp, totalWeight));
}
if (totalWeight <= 0)
throw new Exception("No Resource");
int random = randomNext(1, totalWeight + 1);
int lower = -1;
int upper = weightedResource.size();
while (upper - lower > 1) {
int index = (lower + upper) / 2;
if (weightedResource.get(index).weight >= random) {
upper = index;
} else {
lower = index;
}
}
return (T) weightedResource.get(upper).obj;
}
public static int randomNext(int from, int to) {
return (int) (Math.random() * (to - from) + from);
}
public interface Weight<T> {
int getWeight(T temp);
}
private static class WeightCompare {
private Object obj;
private int weight;
public WeightCompare(Object obj, int weight) {
this.obj = obj;
this.weight = weight;
}
}
}
呼び出しは、次のようになります。
public String getTest4() {
ArrayList<TestMember> members = new ArrayList<TestMember>();
members.add(new TestMember(1, 50));
members.add(new TestMember(2, 80));
members.add(new TestMember(3, 130));
try {
TestMember member = RandomUtil.weightedRandom(members,
new Weight<TestMember>() {
public int getWeight(TestMember m) {
return m.weight;
}
});
return "" + member.id;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new WebApplicationException(500);
}
}
private class TestMember {
public int id;
public int weight;
public TestMember(int id, int weight) {
this.id = id;
this.weight = weight;
}
}
RandomUtil.weightedRandomはジェネリック型を受け取るようにしているので
第1引数のリストに指定した型を返します。
第2引数には重み付けを与えるメソッドの定義を渡しています。
サンプルの場合は、リストのTestMemberクラスのweightを重みとして取得しています。
ジェネリックの使い方がようやくわかってきた。
追記
2014/01/* Java 8でのラムダ式を使った使用方法はこちら
java でのmemcached ラッパー
Javaでmemcachedを利用する際に、
1.キーからオブジェクトget
2.オブジェクトがnullならデータ取得
3.2で取得したデータをmemcachedにset
というのは割りとよくある一連の実装だと思います。
前職でC#を使っているときはdelegate(ラムダ式)を使って
1つのメソッドで一連の処理を実装していました。
Javaでも同じように実装してみます。
Delegateはこんなかんじでいいのかな…
Delegate interfaceを定義、そのgetメソッドから取得するように。
public class Memcached {
protected static MemCachedClient mcc = new MemCachedClient();
static {
SockIOPool pool = SockIOPool.getInstance();
//設定
pool.initialize();
}
public static <T> T get(String key, Memgetable<T> delegate, Date expire){
Object ret = null;
ret = mcc.get(key);
if (ret != null) return ret;
ret = (T) delegate.get();
if (ret == null) return null;
mcc.set(key, ret, expire);
return ret;
}
public interface Memgetable<T> {
public T get();
}
}
呼び出しは、
String key = "test_key";
final String value = "test_value";
Memcached.get(key, new Memgetable<String>() {
public String get() {
return value;
}
}, new Date());
こんなかんじで、一行で出来ます。
Delegate interfaceにgetメソッドを定義し値がなければメソッドから値を取得します。
ラムダ式周りはC#が素晴らしすぎてJavaの非力さが露呈してしまう。。
Java 8ではもっと楽に実装できるようになるのかしら。
1.キーからオブジェクトget
2.オブジェクトがnullならデータ取得
3.2で取得したデータをmemcachedにset
というのは割りとよくある一連の実装だと思います。
前職でC#を使っているときはdelegate(ラムダ式)を使って
1つのメソッドで一連の処理を実装していました。
Javaでも同じように実装してみます。
Delegateはこんなかんじでいいのかな…
Delegate interfaceを定義、そのgetメソッドから取得するように。
public class Memcached {
protected static MemCachedClient mcc = new MemCachedClient();
static {
SockIOPool pool = SockIOPool.getInstance();
//設定
pool.initialize();
}
public static <T> T get(String key, Memgetable<T> delegate, Date expire){
Object ret = null;
ret = mcc.get(key);
if (ret != null) return ret;
ret = (T) delegate.get();
if (ret == null) return null;
mcc.set(key, ret, expire);
return ret;
}
public interface Memgetable<T> {
public T get();
}
}
呼び出しは、
String key = "test_key";
final String value = "test_value";
Memcached.get(key, new Memgetable<String>() {
public String get() {
return value;
}
}, new Date());
こんなかんじで、一行で出来ます。
Delegate interfaceにgetメソッドを定義し値がなければメソッドから値を取得します。
ラムダ式周りはC#が素晴らしすぎてJavaの非力さが露呈してしまう。。
Java 8ではもっと楽に実装できるようになるのかしら。
2012年12月8日土曜日
memcached for Windows 8 x64
Windows 8 x64 で memcachedをインストールする方法。
開発環境でmemcachedが必要だったのでインストールしようとしましたが、
ここにあるようなバイナリではサービスの追加に失敗して動きませんでした。
>memcached -d install
のコマンドで失敗します。(もしかして、管理者で実行しなかったからか…
のでもう一つの方法、CouchbaseのMembaseをインストールして
memcached モードで利用します。
ダウンロードはここの下の方から
インストールする際に、Bucket Setting 画面で Bucket Type : Memcached を選択
すればインストールはOKです。
では確認、
>telnet localhost 11211
>stats
で反応があれば確認完了です。
Memcached-Java-Clientでは今のところ問題なく動作しています。
開発環境でmemcachedが必要だったのでインストールしようとしましたが、
ここにあるようなバイナリではサービスの追加に失敗して動きませんでした。
>memcached -d install
のコマンドで失敗します。(もしかして、管理者で実行しなかったからか…
のでもう一つの方法、CouchbaseのMembaseをインストールして
memcached モードで利用します。
ダウンロードはここの下の方から
インストールする際に、Bucket Setting 画面で Bucket Type : Memcached を選択
すればインストールはOKです。
では確認、
>telnet localhost 11211
>stats
で反応があれば確認完了です。
Memcached-Java-Clientでは今のところ問題なく動作しています。
2012年12月7日金曜日
JNDIのカスタムPropertyクラス
JNDIに設定を集約できないかと思い、カスタムPropertyクラス作ってみました。
Jettyを利用している場合、
xmlのNew要素に指定したclass属性のクラスをnewして、
その子要素のSetに指定した値をname属性と同じ名前のメンバに代入します。
何言ってるかわからんないですね。。つまり、
<New class="org.chikishe.Property.MemcachedProp">
<Set name="servers">127.0.0.1:8091</Set>
<Set name="weights" type="java.lang.String" >1</Set>
…
というxmlは、
MemcachedProp prop = new MemcachedProp();
prop.setServers("127.0.0.1:8091");
prop.setWeights("1"):
…
というふうに解釈されます。
Memcached-Java-Client用のPropertyクラスを作るとするとこうなります。
MemcahedProp.java:
package org.chikishe.Property;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class MemcachedProp {
private String[] servers;
private Integer[] weights;
private int initConn;
private int minConn;
private int maxConn;
private int maxIdle;
private int maintSleep;
private boolean nagle;
private int socketTO;
private int socketConnectTO;
public String[] getServers() {
return servers;
}
public void setServers(String servers) {
this.servers = servers.split(",");
}
public Integer[] getWeights() {
return weights;
}
public void setWeights(String weights) {
String[] strings = weights.split(",");
Integer[] ints = new Integer[strings.length];
for (int i = 0; i < strings.length; i++)
ints[i] = Integer.parseInt(strings[i]);
this.weights = ints;
}
public int getInitConn() {
return initConn;
}
public void setInitConn(int initConn) {
this.initConn = initConn;
}
public int getMinConn() {
return minConn;
}
public void setMinConn(int minConn) {
this.minConn = minConn;
}
public int getMaxConn() {
return maxConn;
}
public void setMaxConn(int maxConn) {
this.maxConn = maxConn;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaintSleep() {
return maintSleep;
}
public void setMaintSleep(int maintSleep) {
this.maintSleep = maintSleep;
}
public boolean isNagle() {
return nagle;
}
public void setNagle(boolean nagle) {
this.nagle = nagle;
}
public int getSocketTO() {
return socketTO;
}
public void setSocketTO(int socketTO) {
this.socketTO = socketTO;
}
public int getSocketConnectTO() {
return socketConnectTO;
}
public void setSocketConnectTO(int socketConnectTO) {
this.socketConnectTO = socketConnectTO;
}
public MemcachedProp() {
}
public static MemcachedProp getInstance() throws NamingException {
InitialContext ic = new InitialContext();
Context envCtx = (Context) ic.lookup("java:comp/env");
MemcachedProp prop = (MemcachedProp) envCtx.lookup("memcached");
return prop;
}
}
jetty-env.xml:
<New id="memcached" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg></Arg>
<Arg>memcached</Arg>
<Arg>
<New class="org.chikishe.Property.MemcachedProp">
<Set name="servers">127.0.0.1:8091</Set>
<Set name="weights" type="java.lang.String" >1</Set>
<Set name="initConn">5</Set>
<Set name="minConn">5</Set>
<Set name="maxConn">250</Set>
<Set name="maxIdle">21600000</Set>
<Set name="maintSleep">30</Set>
<Set name="nagle">false</Set>
<Set name="socketTO">3000</Set>
<Set name="socketConnectTO">0</Set>
</New>
</Arg>
</New>
web-inf/web.xml :
<resource-ref>
<description>Memcached Property</description>
<res-ref-name>memcached</res-ref-name>
<res-type>org.chikishe.Property.MemcachedProp</res-type>
<res-auth>Container</res-auth>
</resource-ref>
呼び出しはこんなふうになります。
MemcachedProp prop = MemcachedProp.getInstance();
SockIOPool pool = SockIOPool.getInstance();
// set the servers and the weights
pool.setServers(prop.getServers());
pool.setWeights(prop.getWeights());
とりあえず、JNDIに設定をまとめるには使えそうです。
本来はstatic finalにしたいところ。
あと、 Memcached-Java-ClientのJNDI対応まだないんでしょうか?
Jettyを利用している場合、
xmlのNew要素に指定したclass属性のクラスをnewして、
その子要素のSetに指定した値をname属性と同じ名前のメンバに代入します。
何言ってるかわからんないですね。。つまり、
<New class="org.chikishe.Property.MemcachedProp">
<Set name="servers">127.0.0.1:8091</Set>
<Set name="weights" type="java.lang.String" >1</Set>
…
というxmlは、
MemcachedProp prop = new MemcachedProp();
prop.setServers("127.0.0.1:8091");
prop.setWeights("1"):
…
というふうに解釈されます。
Memcached-Java-Client用のPropertyクラスを作るとするとこうなります。
MemcahedProp.java:
package org.chikishe.Property;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class MemcachedProp {
private String[] servers;
private Integer[] weights;
private int initConn;
private int minConn;
private int maxConn;
private int maxIdle;
private int maintSleep;
private boolean nagle;
private int socketTO;
private int socketConnectTO;
public String[] getServers() {
return servers;
}
public void setServers(String servers) {
this.servers = servers.split(",");
}
public Integer[] getWeights() {
return weights;
}
public void setWeights(String weights) {
String[] strings = weights.split(",");
Integer[] ints = new Integer[strings.length];
for (int i = 0; i < strings.length; i++)
ints[i] = Integer.parseInt(strings[i]);
this.weights = ints;
}
public int getInitConn() {
return initConn;
}
public void setInitConn(int initConn) {
this.initConn = initConn;
}
public int getMinConn() {
return minConn;
}
public void setMinConn(int minConn) {
this.minConn = minConn;
}
public int getMaxConn() {
return maxConn;
}
public void setMaxConn(int maxConn) {
this.maxConn = maxConn;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaintSleep() {
return maintSleep;
}
public void setMaintSleep(int maintSleep) {
this.maintSleep = maintSleep;
}
public boolean isNagle() {
return nagle;
}
public void setNagle(boolean nagle) {
this.nagle = nagle;
}
public int getSocketTO() {
return socketTO;
}
public void setSocketTO(int socketTO) {
this.socketTO = socketTO;
}
public int getSocketConnectTO() {
return socketConnectTO;
}
public void setSocketConnectTO(int socketConnectTO) {
this.socketConnectTO = socketConnectTO;
}
public MemcachedProp() {
}
public static MemcachedProp getInstance() throws NamingException {
InitialContext ic = new InitialContext();
Context envCtx = (Context) ic.lookup("java:comp/env");
MemcachedProp prop = (MemcachedProp) envCtx.lookup("memcached");
return prop;
}
}
jetty-env.xml:
<New id="memcached" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg></Arg>
<Arg>memcached</Arg>
<Arg>
<New class="org.chikishe.Property.MemcachedProp">
<Set name="servers">127.0.0.1:8091</Set>
<Set name="weights" type="java.lang.String" >1</Set>
<Set name="initConn">5</Set>
<Set name="minConn">5</Set>
<Set name="maxConn">250</Set>
<Set name="maxIdle">21600000</Set>
<Set name="maintSleep">30</Set>
<Set name="nagle">false</Set>
<Set name="socketTO">3000</Set>
<Set name="socketConnectTO">0</Set>
</New>
</Arg>
</New>
web-inf/web.xml :
<resource-ref>
<description>Memcached Property</description>
<res-ref-name>memcached</res-ref-name>
<res-type>org.chikishe.Property.MemcachedProp</res-type>
<res-auth>Container</res-auth>
</resource-ref>
呼び出しはこんなふうになります。
MemcachedProp prop = MemcachedProp.getInstance();
SockIOPool pool = SockIOPool.getInstance();
// set the servers and the weights
pool.setServers(prop.getServers());
pool.setWeights(prop.getWeights());
とりあえず、JNDIに設定をまとめるには使えそうです。
本来はstatic finalにしたいところ。
あと、 Memcached-Java-ClientのJNDI対応まだないんでしょうか?
2012年12月6日木曜日
Jersey の HttpServletRequest.getParameter仕様
Jerseyの開発でつまづいたので共有
JerseyでPost時のパラメータを取得するには、普段は@FormParamを利用します。
が、既存のライブラリを利用する際に、HttpServletRequest.getParameter, getParameterMap
を通して取得したい場合があります。
ただ、Jerseyの仕様でgetParameterはnullが返ってきます。
(内部のinputstreamがなくなっているからとか)
その場合には手段がいくつかあります。
1.filterを利用する
2.ServletContainerをextendsする
上記を利用して@FormParam生成前に内部的にparameterを保持しておく
という案がありますが、資料なさ過ぎて私にはむりです。
そこでとりあえず
3.@Consumesを使ってFormデータを直接受け取る
を利用して既存ライブラリのほう
Postは
JerseyでPost時のパラメータを取得するには、普段は@FormParamを利用します。
が、既存のライブラリを利用する際に、HttpServletRequest.getParameter, getParameterMap
を通して取得したい場合があります。
ただ、Jerseyの仕様でgetParameterはnullが返ってきます。
(内部のinputstreamがなくなっているからとか)
その場合には手段がいくつかあります。
1.filterを利用する
2.ServletContainerをextendsする
追記(2014/04/15):
jetty 9あたリから@Injectを利用した方法があるようです要調査
上記を利用して@FormParam生成前に内部的にparameterを保持しておく
という案がありますが、資料なさ過ぎて私にはむりです。
そこでとりあえず
3.@Consumesを使ってFormデータを直接受け取る
を利用して既存ライブラリのほう
を修正し
ました。Postは
"application/x-www-form-urlencoded"のcontent-typeでデータを渡してくるので
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void post(MultivaluedMap formParams) {
…
というふうにデータ取得ができます。
でこれをHttpServletRequestのgetPrameterに詰め込めればいいのですが
あきらめ。
既存ライブラリでgetParameterする際にデータを結合しました。
登録:
投稿 (Atom)