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月21日月曜日

Windows Defenderの自動定義更新

Windows Defenderの定義更新はWindows Updateに統合されていて
自動更新させようとするとWindows Updateの自動更新も起動してしまいます。
そうすると勝手に再起動されて困ります。


そんなときに試したいのが
Windows Defenderの定義のみを更新するコマンドです

これ、

"C:\Program Files\Windows Defender\MpCmdRun.exe" -SignatureUpdate


私は、プロンプトの表示を避けたいので
下のようなvbsファイルを作成してTaskスケジューラに登録しています


WindowsDefenderUpdate.vbs :

Set ws = CreateObject("Wscript.Shell")

ws.run "cmd /c ""C:\Program Files\Windows Defender\MpCmdRun.exe"" -SignatureUpdate", vbhide


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を使ったほうが楽だし早い

2013年9月21日土曜日

J2EE (jetty) アプリケーション初期化処理

J2EE のリスナーの話です

基本のリスナーの使い方は覚えないといけないなー
と思いながら全然すすまない

まずは初期化処理から
初期化時にはDBからマスタの読み込みとかします


package com.gauuud.StartUp;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;


public class InitDB implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //初期化処理       
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //終了時処理

    }

}


web.xml :

<listener>     
        <listener-class>com.gauuud.StartUp.InitDB</listener-class>
</listener>


余談になりますが
マスタは起動時にメモリ上に全部読み込むようにするべきですよね
最近のサーバはメモリ多いし(マスタとか10Mもいかないだろうし
クラスタ構成が多いので更新時に再起動でサービスが止まることもないし

2013年9月20日金曜日

Java Tripletクラス 3つの組みあわせ

2つの値を一つの組にまとめて比較やMapのキーにしたり
することがある場合は、

commons-langのTupleを使えばいいのですが
3つ必要 だという場合には自分で定義する必要があります。たぶん



Triplet.java:


import java.io.Serializable;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;

public class Triplet<X, Y, Z> implements Comparable<Triplet<X, Y, Z>>,Serializable {

    private static final long serialVersionUID = -7986546850125833527L;

    public X x;

    public Y y;

    public Z z;

    public static <X, Y, Z> Triplet<X, Y, Z> of(X x, Y y, Z z) {
        return new Triplet<X, Y, Z>(x, y, z);
    }

    public Triplet(X x, Y y, Z z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public X getX() {
        return x;
    }

    public void setX(X x) {
        this.x = x;
    }

    public Y getY() {
        return y;
    }

    public void setY(Y y) {
        this.y = y;
    }

    public Z getZ() {
        return z;
    }

    public void setZ(Z z) {
        this.z = z;
    }

    public int compareTo(Triplet<X, Y, Z> other) {
        return new CompareToBuilder().append(getX(), other.getX())
                .append(getY(), other.getY()).append(getZ(), other.getZ())
                .toComparison();
    }

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof Triplet<?, ?, ?>) {
            Triplet<?, ?, ?> other = (Triplet<?, ?, ?>) obj;
            return ObjectUtils.equals(getX(), other.getX())
                    && ObjectUtils.equals(getY(), other.getY())
                    && ObjectUtils.equals(getZ(), other.getZ());
        }
        return false;
    }
   
    public int hashCode() {
        // see Map.Entry API specification
        return (getX() == null ? 0 : getX().hashCode())
                ^ (getY() == null ? 0 : getY().hashCode())
                ^ (getZ() == null ? 0 : getZ().hashCode());
    }

    @Override
    public String toString() {
        return new StringBuilder().append('(').append(getX()).append(',')
                .append(getY()).append(',').append(getZ()).append(')').toString();
    }
}




commons-lang3が必要です。
Tupleを真似してみただけですが割りと使います^^;

3つのPKをもつデータをMapに登録するとか

JavaでTimeSpanクラス

TimeSpanクラスとはなんぞやというと
Date と Dateを比較した差分を保持するという単純なクラスです#

Calendarクラス使えばいいと思われますが
static final な fieldの定義とか結構めんどくさい



TimeSpanクラスで7日の差分を定義すると

public static final TimeSpan TIMESPAN = TimeSpan.Days(7);


で現在と差分を足したDateを求めるには、

Date date = TIMESPAN.getDate(new Date());

とすればいい。


何に利用するかというと主に
Memcachedに渡すExpireの 定義とかです。


Memcached.get(key, new Memgetable<String>() {
    public String get() {
        return value;
    }
}, TIMESPAN.getDate(new Date()));



こんな風に記述ができます。




TimeSpan.java : stackoverflowを参照したような…参照元は失念


import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;

public class TimeSpan {
    private int millis;

    public TimeSpan(int millis) {
        this.millis = millis;
    }

    public TimeSpan(Date source, Date dest){
        millis = (int)(dest.getTime() - source.getTime());
    }
   
    private TimeSpan() {
    }

    public int getDays() {
        return this.getTotalHours() / 24;
    }

    public int getTotalHours() {
        return this.getTotalMinutes() / 60;
    }

    public int getTotalMinutes() {
        return this.getTotalSeconds() / 60;
    }

    public int getTotalSeconds() {
        return (int) (this.millis / 1000);
    }

    public long getTotalMillis() {
        return this.millis;
    }

    public TimeSpan addDays(int days) {
        return addHours(days * 24);
    }

    public TimeSpan addHours(int hours) {
        return addMinutes(hours * 60);
    }

    public TimeSpan addMinutes(int minutes) {
        return addSeconds(minutes * 60);
    }

    public TimeSpan addSeconds(int seconds) {
        return addMillis(seconds * 1000);
    }

    public TimeSpan addMillis(int millis) {
        this.millis += millis;
        return this;
    }
   
    public Date getDate() {
        return new Date(this.millis);
    }
   
    public Date getDate(Date currentDate) {
        return DateUtils.addMilliseconds(currentDate, millis);
    }

    public static TimeSpan Days(int days) {
        return new TimeSpan().addDays(days);
    }

    public static TimeSpan Hours(int hours) {
        return new TimeSpan().addHours(hours);
    }

    public static TimeSpan Minutes(int minutes) {
        return new TimeSpan().addMinutes(minutes);
    }

    public static TimeSpan Seconds(int seconds) {
        return new TimeSpan().addSeconds(seconds);
    }   
   
    public static TimeSpan Millis(int millis){
        return new TimeSpan(millis);
    }

    public static TimeSpan Span(Date source, Date dest){
        return new TimeSpan((int)(dest.getTime() - source.getTime()));
    }
}



TODO:TimeSpan同士の比較

追記
2014/01/23 Java 8でのDurationによる代替方法→ここ
 

jquery cdn 止まった時には

https://ajax.aspnetcdn.com/ajax/最近たまに止まりますね
jquery.tmpl.jsがあるから使ってるのに…

そういう時の対策として
jquery.tmpl.js読み込めてない場合に自前のjsを読み込むようにする


jqueryの場合は、htmlに下記を追加

<script>
window.jQuery || document.write(unescape("%3Cscript src='js/jquery-1.6.2.min.js'%3E%3C/script%3E"));
</script>




jquery.tmplの場合は、htmlに下記を追加

<script>
$.tmpl || document.write(unescape("%3Cscript src='js/jquery.tmpl.min.js'%3E%3C/script%3E"));
</script>


環境によってpathとかversionは変わります
読み込み順序には気をつけましょう

2013年9月16日月曜日

JNDIのカスタムPropertyクラス2

jetty 8.1以降では

JNDI に Array を指定することが可能になっています
次のように指定可能です


web.xml



    <resource-ref>
        <description>Maintenance Property</description>
        <res-ref-name>maintenance</res-ref-name>
        <res-type>java.lang.reflect.Array</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>



jetty-env.xml



    <New id="maintenance" class="org.eclipse.jetty.plus.jndi.Resource">
        <Arg></Arg>
        <Arg>maintenance</Arg>
        <Arg>
              <Array type="com.gauuud.Property.MaintenanceProp">
                <item><New class="com.gauuud.Property.MaintenanceProp">
                    <Set name="start">2012/9/11 1:25</Set>
                    <Set name="end">2012/9/11 1:30</Set>
                    <Set name="type">10</Set>
                    <Set name="desc">test</Set>
                </New></item>
                <item><New class="com.gauuud.Property.MaintenanceProp">
                    <Set name="start">2012/9/12 1:25</Set>
                    <Set name="end">2012/9/13 1:30</Set>
                    <Set name="type">10</Set>
                    <Set name="desc">test2</Set>
                </New></item>
            </Array>
        </Arg>
    </New>




MaintenanceProp.java :



package com.gauuud.Property;

import java.util.Date;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;




public class MaintenanceProp {

    private int type;

    public int getType() {
        return this.type;
    }

    public void setType(int type) {
        this.type = type;
    }

    private Date start;

    public Date getStart() {
        return this.start;
    }

    public void setStart(Date start) {
        this.start = start;
    }

    private Date end;

    public Date getEnd() {
        return this.end;
    }

    public void setEnd(Date end) {
        this.end = end;
    }

    private String desc;

    public String getDesc() {
        return this.desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public MaintenanceProp() {}

}



MaintenancePropArray.java:



package com.gauuud.Property;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;



public class MaintenancePropArray {
    private static final MaintenanceProp[] array;
   
    public static MaintenanceProp[] get(){
        return array;
    }
   
    static {
        MaintenanceProp[] props = null;
        try {
            InitialContext ic = new InitialContext();
            Context envCtx = (Context) ic.lookup("java:comp/env");
            props = (MaintenanceProp[]) envCtx.lookup("maintenance");
        } catch (NamingException e) {
            e.printStackTrace();
        }
        array = props;
    }
}


小ネタ mavenを使ってjava -cp を実行しよう

java -cp たまに使うけどライブラリを探したりするの面倒
ライブラリとか全部mavenに任せようぜという人向け

下のようなコマンドを実行しろとかたまにある
java -cp lib/jetty-util-xxx.jar org.eclipse.jetty.util.security.Password password
 
 
こういうのはpom.xmlで下みたいに指定して
つぎのコマンドを実行すればOK

mvn exec:java -Dexec.mainClass="org.eclipse.jetty.util.security.Password" -Dexec.args="password" --quiet  



pom.xml :
 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.gauuud.passgen</groupId>
 <artifactId>passgen</artifactId>
 <version>0.0.1</version>
 <dependencies>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-util</artifactId>
   <version>8.1.12.v20130726</version>
  </dependency>
 </dependencies>
</project>
 
 
または、mainClassをpomで指定する
mvn exec:java -Dexec.args="password" --quiet  

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.gauuud.passgen</groupId>
 <artifactId>passgen</artifactId>
 <version>0.0.1</version>
 <dependencies>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-util</artifactId>
   <version>8.1.12.v20130726</version>
  </dependency>
 </dependencies>
  <build>
    <plugins>
      <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>exec-maven-plugin</artifactId>
          <version>1.2.1</version>              
        <configuration>
          <mainClass>org.eclipse.jetty.util.security.Password</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
 </project>
 
 
または、mainClassを指定してjar化する 
mvn packageしてtarget以下のjar-with-dependenciesを実行する
 
java -jar passgen-0.0.1-jar-with-dependencies.jar password 


pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.gauuud.passgen</groupId>
 <artifactId>passgen</artifactId>
 <version>0.0.1</version>
 <dependencies>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-util</artifactId>
   <version>8.1.12.v20130726</version>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
     <archive>
      <manifest>
       <mainClass>org.eclipse.jetty.util.security.Password</mainClass>
       <packageName>passgen</packageName>
       <addClasspath>true</addClasspath>
       <addExtensions>true</addExtensions>
       <classpathPrefix>dependency</classpathPrefix>
      </manifest>
     </archive>
     <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
     </descriptorRefs>
    </configuration>
   </plugin>
   <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
     <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
     </descriptorRefs>
     <archive>
      <manifest>
       <mainClass>org.eclipse.jetty.util.security.Password</mainClass>
      </manifest>
     </archive>

    </configuration>
    <executions>
     <execution>
      <id>make-assembly</id> <!-- this is used for inheritance merges -->
      <phase>package</phase> <!-- append to the packaging phase. -->
      <goals>
       <goal>single</goal> <!-- goals == mojos -->
      </goals>
     </execution>
    </executions>
   </plugin>
  </plugins>
 </build>
</project>





 
 
あまり効率的とは言えない?
プライベートリポジトリに登録して使い回せばいいじゃないか
nexusよいですよ 

Jetty SSL設定

Jetty 8.*での情報です
9.*でも利用可能かもしれません


#証明書作成用キーの作成

openssl genrsa -des3 -out jetty.key
openssl req -new -x509 -key jetty.key -out jetty.crt


#サーバへ送る証明書の作成

openssl req -new -key jetty.key -out jetty.csr


#2048bit以上が必要な場合
#openssl req -nodes -newkey rsa:2048 -keyout jetty.key -out jetty.csr


#keytoolがない場合${JAVA_HOME}/jre/bin/keytool



#証明書の登録
#jetty.csrの内容を証明サーバに送る
#jetty.crtに帰ってきた証明書を保存する
#keystoreへ登録

keytool -keystore keystore -import -alias jetty -file jetty.crt -trustcacerts



#pkcs12形式を登録
openssl pkcs12 -export -inkey jetty.key -in jetty.crt -certfile etc/SSL_CA.cer(中間証明書)  -out jetty.pkcs12
keytool -importkeystore -srckeystore jetty.pkcs12 -srcstoretype PKCS12 -destkeystore etc/keystore



#jetty-ssl.xml修正

<New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">

    <Set name="KeyStore"><Property name="jetty.home" default="." />/etc/keystore</Set>

    <Set name="KeyStorePassword">[keystore password]</Set>

    <Set name="KeyManagerPassword">[key password]</Set>

    <Set name="TrustStore"><Property name="jetty.home" default="." />/etc/keystore</Set>

    <Set name="TrustStorePassword">[keystore password]</Set>

 </New>


#verify
openssl s_client -connect {対象サーバ}:443 -showcerts


参考
http://wiki.eclipse.org/Jetty/Howto/Configure_SSL

Ubuntu 12.* Jetty のマニュアルインストール

Jettyの手動インストール方法です
レポジトリのjettyは古いし動かないし手動でインストールするしかありません

基本は参考文献の多いTomcatのまねをすればいいみたいです

※権限周りは知識がないので要確認です



1.Jettyをダウンロード
http://download.eclipse.org/jetty/stable-9/dist/



2.解凍

tar -xvf jetty-distribution-8.1.11.v20130520.tar.gz






3.移動、リンクはる
sudo mv jetty-distribution-8.1.11.v20130520 /usr/local/

cd /usr/local/

sudo ln -s  jetty-distribution-8.1.11.v20130520 jetty



4.スクリプトに権限付与

sudo useradd jetty -U -s /bin/false

#こっちかな?useradd jetty jetty -d /usr/local/jetty/ -s /bin/false -p’*’ -r

sudo chown -R jetty webapps temp logs work conf

sudo chmod a+x /usr/local/jetty/bin/jetty.sh

nano /usr/local/jetty/bin/jetty.sh

#1行目を修正

!/bin/bash




5.init.d登録


sudo cp bin/jetty.sh /etc/init.d/jetty

sudo chmod 755 /etc/init.d/jetty

sudo nano /etc/default/jetty

下記の内容を書き込む

JAVA_HOME=/usr/lib/jvm/jdk1.7.0 # Path to Java
NO_START=0 # Start on boot

PATH=${JAVA_HOME}/bin:${PATH}
JAVA_OPTION=-Duser.timezone="Asia/Tokyo" #TimeZoneの指定が必要なら
JETTY_HOME=/usr/local/jetty
JETTY_USER=jetty # Run as this user

開始してみる
sudo service jetty start



6.自動起動に登録

sudo update-rc.d jetty defaults


参考
http://jensontaylor.blogspot.jp/2010/09/manually-installing-tomcat-7-on-ubuntu.html


その他の設定
#ポート80を開く場合
iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -I PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443 

Java UTF-8(Shift-JIS)の取り扱いTips

JavaでUTF-8を扱おうとするとよくぶつかるencoding問題を
ちょっとまとめてみる(Shift-JISも同様)

1.ファイル読み込み

ファイルを読み込む際にreadlineが途中で終わってしまう場合
一番簡単な解決法はcommons-ioを使う
Java標準のFileReaderはマルチバイト対応してないよう


import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;


        LineIterator it = null;

        try {
            it = FileUtils.lineIterator(file, "UTF-8");

            while (it.hasNext()) {
                String line = it.nextLine();
            }
         } catch (Exception e) {
            e.printStackTrace();
         } finally {
            LineIterator.closeQuietly(it);
         }
        



2.URLConnectionでUTF-8指定

 InputStreamReaderでUTF-8を指定する


URL url = new URL("your url");
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));

StringBuilder builder = new StringBuilder();
String str;

while (null != (str = reader .readLine())) {
    builder.append(str).append("\r\n");
}
reader.close();
str = builder.toString();




3.XMLパーサでのUTF-8指定

parserにXMLInputSourceでUTF-8指定
例では、URLConnectionからデータ取得


     DOMParser parser = new DOMParser();
            parser.setFeature("http://xml.org/sax/features/namespaces", false);
            parser.setFeature("http://cyberneko.org/html/features/scanner/notify-builtin-refs", true);
               
            BufferedReader reader = new BufferedReader(new InputStreamReader(       url.openStream(), "UTF-8"));
           
            XMLInputSource source = new XMLInputSource(null, "Element", null,
                    reader, "UTF-8");
           
            parser.parse(source);

            Document document = parser.getDocument();

4.MySQLで文字化ける


内部エンコーディング、入力、DB全部UTF-8で統一していても文字化けする場合
database urlにcharacterEncodingを指定する


jdbc:mysql://localhost/schema?useUnicode=true&characterEncoding=UTF8

jetty jndiではuseUnicode=trueが指定できないので
characterEncodingのみ指定



2013年1月5日土曜日

HttpServletRequestのthread内共有

ASP.NETを利用していると
static HttpContext HttpContext.Current からリクエスト情報を取得して
IPADDRESS等の情報をどこででも取得することができます

似たような意識でJavaで実装していると
どうしてもHttpServletRequestの取得でつまずいてしまいます。

そこで、スレッド毎に値を保持するThreadLocalを利用して
HttpServletRequest をスレッド内で参照できるようにします


private final static ThreadLocal<HttpServletRequest> servletRequests = new ThreadLocal<HttpServletRequest>();

public static void setServletRequest(HttpServletRequest request) {
    servletRequests.set(request);
}

public static HttpServletRequest getServletRequest() {
    return servletRequests.get();
}


もちろん非同期な実装では参照できなくなる(かな?)
リクエスト初期化時にセットする必要があります。
リクエスト終了後はガベージコレクション対象になります。

2013年1月4日金曜日

Logbackメモ


Logback を利用してログ出力しているのですが、
これまでサンプルのままだったのを少し見直しました。


次のような要件を満たすように変更、
1.レベルがエラーの場合にメールで送信する
2.ファイル出力、日毎、100M毎 にローリング


main/resource/logback.xml :

<?xml version="1.0"?>
 <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<configuration debug="true" scan="true" scanPeriod="30 seconds">

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>

    <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <smtpHost>smtp.gmail.com</smtpHost>
        <smtpPort>587</smtpPort>
        <STARTTLS>true</STARTTLS>
        <username>[YOUR_NAME]@gmail.com</username>
        <password>[YOUR_PASSWORD]</password>
        <to>[TO]</to> <!-- additional destinations are possible -->
        <from>[FROM]</from>
        <subject>TESTING: %logger{20} - %m</subject>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <charset>UTF-8</charset>
            <Pattern>[%-5level][%d{yyyy-MM-dd HH:mm:ss.SSS}] %class - %msg%n</Pattern>
        </layout>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- keep 30 days' worth of history -->
            <maxHistory>30</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- or whenever the file size reaches 100MB -->
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <charset>UTF-8</charset>
            <Pattern>[%-5level][%d{yyyy-MM-dd HH:mm:ss.SSS}] %class - %msg%n</Pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
        <appender-ref ref="EMAIL" />
    </root>
</configuration>



SMTPAppenderを利用してメールを送信します。
サンプルではGMAILで送信する設定にしています。
メールで送信する場合はLevelFilterを利用してERRORの場合のみ送信するようにしています。
File出力に関しては、ここのサンプルのままです。


File出力がネックになりそうな場合は、下のAsyncAppenderをFileAppenderの
前に挟んでAsync処理にする。
SMTPはAsyncで送信されているようなので気にしない 。

  <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
  </appender>



アクセスログを出力したい場合は、ここを参照する

2013年1月3日木曜日

jetty maven gzipフィルター適用

maven上のjetty plugin でgzip を適用する方法。
このあたりの内容です。

http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin#Using_GZip_Compression_and_Other_Jetty_Extensions


pom.xmlのプラグイン設定にdependancyを追加

<plugin>
       
<groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>                                        <version>8.1.8.v20121106</version>
         <configuration>
           [...]
       
</configuration>
         <dependencies> 
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-servlets</artifactId>
             <version>8.1.8.v20121106</version>
           </dependency>
         </dependencies>


web.xmlにgzipフィルターを定義

  <filter>
    <filter-name>GzipFilter</filter-name>
    <filter-class>org.eclipse.jetty.servlets.GzipFilter</filter-class>
    <init-param>
      <param-name>mimeTypes</param-name>
      <param-value>text/html,text/plain,text/xml,application/xhtml+xml,text/css,application/javascript,image/svg+xml</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>



pom.xmlにdependancyを追加していないと
ClassNotFoundExceptionで怒られます