ラベル json の投稿を表示しています。 すべての投稿を表示
ラベル json の投稿を表示しています。 すべての投稿を表示

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の文字列" }

2014年2月22日土曜日

Jersey2.6(JAX-RS2.2) with Jetty9 セットアップ


JAX-RS2.*を使いたいと思って、Jersey2.6をJetty9に組み込もうとしたのですが、
四苦八苦したのでメモ

いろいろdependancyに入れすぎてたのが問題だったようです。
jsr311-apiをdependancyから取り除くとうまくいきました。

JAX-RS2.*では、Pojomappingを指定するのではなく
JAX-RS JSON Providerに対応するライブラリを追加することでJSONに対応します。
下の例では、jackson-jaxrs-json-providerを追加しています。

JavaEE3以上は、jersey-container-servlet-coreを使うよう言われますが
自分は以前のweb.xmlでの指定方法が好みなので
jersey-container-servletを使っています。


pom.xml :

 <略>

<properties>
    <java.version>1.7</java.version>
    <jettyVersion>9.1.2.v20140210</jettyVersion>
    <jerseyVersion>2.6</jerseyVersion>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
   
<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>${jettyVersion}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>${jettyVersion}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jerseyVersion}</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>${jerseyVersion}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
            <version>2.2.3</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jettyVersion}</version>
                <configuration>
                    <webAppSourceDirectory>${basedir}/webapp</webAppSourceDirectory>
                    <webXmlFile>${basedir}/webapp/WEB-INF/web.xml</webXmlFile>
                    <jettyEnvXml>${basedir}/webapp/WEB-INF/jetty-env.xml</jettyEnvXml>
                    <contextPath>/</contextPath>
                </configuration>
            </plugin>
        </plugins>
    </build>




WEB-INF/web.xml:


<?xml version="1.0" encoding="UTF-8"?>
<web-app>

    <servlet>
        <servlet-name>jersey</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer
        </servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>パッケージ</param-value>
        </init-param>
        <load-on-startup>10</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jersey</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>




WEB-INF/jetty-env.xml:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">

  <Call name="setAttribute">
      <Arg>org.eclipse.jetty.webapp.configuration</Arg>
      <Arg>
          <Array type="java.lang.String">
              <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
              <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
              <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
              <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
              <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
              <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
              <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
              <Item>org.eclipse.jetty.webapp.TagLibConfiguration</Item>
          </Array>
      </Arg>
    </Call>
 

  <Set name="war"><SystemProperty name="jetty.home" default="."/>/</Set>
 
</Configure>

2013年11月15日金曜日

Jersey ApacheHttpClientをつかう

Javaでhttpから何かを取得する場合に、
一番簡単に扱えてかつ拡張性が高いのは
Jersey ApacheHttpClient だとおもいますという話。


通常の使い方


ApacheHttpClientの特徴は、オブジェクトマッピングをしてくれるという点です

String型で取得する場合は次のようにすればよい


String res = ApacheHttpClient.create().resource(urlString).get(String.class);


一行ですんじゃいます。

またJSONやXMLを取得する場合にJAXBによる変換を行うことができます。
取得するXMLがつぎのような場合、

<?xml version="1.0"?>
<date>2012/10/11</date>
<hash>hogehogegeee</hash>
<rank>1</rank>
<value>2</value>
<addTime>2012/10/11 12:00</addTime>

マッピングをするクラスを次のように定義して、

Ranking.java :

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement
public class Ranking {
    @XmlElement
    public Date date;

    @XmlElement
    public String hash;

    @XmlElement
    public int rank;

    @XmlElement
    public long value;

    @XmlElement
    public Date addTime;


    public Ranking(){}

}


で、次のようにすれば指定したクラスにマッピングして取得することができます。

Ranking ranking = restClient.resource(urlString).get(Ranking.class);

 

 

 

よくある設定


タイムアウトの設定
接続タイムアウト

ApacheHttpClient client = ApacheHttpClient.create();
client.setConnectTimeout(360000);

読み込みタイムアウト

client.setReadTimeout(900000);



フィルターの定義


フィルターを定義することによって、通信の前後の挙動を
ファンクションクラス?として定義して使いまわすことができます。


Cookieの管理  

クッキーは検証してないのですが、コンフィグで渡せば使えそうです。
クッキーを管理してセッション等を保持する場合は下のような
フィルターを登録すればよいようです。


ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true);

ApacheHttpClient client = ApacheHttpClient.create(config);
client.addFilter(new ClientFilter() {
    private ArrayList cookies = new ArrayList();

    @Override
    public ClientResponse handle(ClientRequest request)
            throws ClientHandlerException {
        if (cookies != null) {
            request.getHeaders().put("Cookie", cookies);
        }

        ClientResponse response = getNext().handle(request);
        if (response.getCookies() != null) {
            if (cookies == null) {
                cookies = new ArrayList();
            }
            cookies.addAll(response.getCookies());
        }
        return response;
    }
});



単にCookieを渡したいだけならWebResourceから指定もできます。


ApacheHttpClient client = ApacheHttpClient.create();

String res = client
        .resource(url)
        .header(HttpHeaders.COOKIE, cookieString)
        .get(String.class);

 

 

 

UserAgentの設定

UserAgentの設定は2つの方法があります。
Jersey Clientと同じようにFilterで登録するかWebResourceにヘッダーを追加する方法です。


Filterを追加する場合:

ApacheHttpClient client = ApacheHttpClient.create();

client.addFilter(new ClientFilter() {
    @Override
    public ClientResponse handle(ClientRequest request)
            throws ClientHandlerException {
        request.getHeaders().put("UserAgent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9b5) Gecko/2008032619 Firefox/3.0b5");
        return getNext().handle(request);
    }
});



WebResourceに追加する場合:

ApacheHttpClient client = ApacheHttpClient.create();

String res = client
        .resource(url)
        .header(HttpHeaders.USER_AGENT, userAgent)
        .get(String.class);



プロキシの設定

HTTP プロクシの設定にはApacheHttpClientConfigを
Client作成時に渡します。


ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(
                        ApacheHttpClientConfig.PROPERTY_PROXY_URI,
                        "proxyurl");

ApacheHttpClient client = ApacheHttpClient.create(config);

SocksプロクシとHTTPプロクシの優先順位

SocksプロクシにはApacheHttpClient自体は対応していないので、
Java自体の機能を利用します。


System.setProperty("socksProxyHost", "127.0.0.1");
System.setProperty("socksProxyPort", "9050");



SocksプロクシとApacheHttpClientのconfigでのプロクシの設定は
同時に利用可能です。両方設定した場合、Socksが優先されます。


2013年10月5日土曜日

JAXB アノテーションを利用したJSON マッピング

Jerseyを使っているとレスポンス用のJSONの定義クラスに
アノテーションの@XmlTransitで省略したり
@XmlElement(name="element")で名前変更したりして
ObjectをJAXB経由でJSONに自動でパースさせてたりしますが
その定義を他でも使いまわして各々の処理でJSON文字列にパースする場合には

JacksonJAXBAnnotations

を利用すればいいです。

これは利用すればJAXBアノテーションを利用したマッピングが可能です。


mavenでpom.xmlに追加

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-xc</artifactId>
            <version>1.9.13</version>
        </dependency>



Javaでは次のようにするればobjをJSONにパース可能
例ではやってないけどObjectMapperはthread safeなので使いまわしましょう

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;



        JaxbAnnotationIntrospector introspector  = new JaxbAnnotationIntrospector();
        ObjectWriter ow = new ObjectMapper().setAnnotationIntrospector(introspector).writer()
                .withDefaultPrettyPrinter();
       
        String json = ow.writeValueAsString(obj);

       


最初は、jerseyの内部ロジックを利用しようとしましたが
ひとつのオブジェクトしかないリストがオブジェクトでパースされる問題が発生したり
いちいちクラスタイプ指定しないといけないので面倒でやめました


ちなみに 最初はこんなかんじにしてました

        private Class[] types = {
            MyClass.class,  

            ArrayList.class,
        };


        String result = null;
        JSONConfiguration config = JSONConfiguration.natural().build();
        JSONJAXBContext ctx = new JSONJAXBContext(config, types);
       
        JSONMarshaller m = ctx.createJSONMarshaller();
        StringWriter sw = new StringWriter();
        m.marshallToJSON(xmlObj, sw);
        result = sw.toString();

        return result;



JAXBContextResolverと似たような感じですね
POJOMappingFeatureで問題はどうにかなるかと思ったんですが
JacksonJAXBAnnotationsを使ったほうが楽だし早い