Home > Tags > Java

Java

CompassをMavenから実行できるプラグインを作った

CSS を効率よく書こうみたいなフレームワーク(と言うのか不明ですが)が流行ってて、LESS とか Sass とか色々あります。

この中で自分が結構いいなぁと思ったものに、Compass (Sass の拡張)というものがあって、ちょろちょろと試したりしているんですが、こういう CSS フレームワークっていうのは、静的にコンパイルする仕組みになっていて、アプリケーション開発とかに組み込む場合は、やっぱりビルドプロセスにうまく組み込みたいなぁと思うのが普通です(手動でやったら絶対ミスりますしね)

ということで、Maven から Compass を実行できるプラグインを作ってみました。

maven-compass-plugin
https://github.com/sinsengumi/maven-compass-plugin

README にも書いてますが、実行には Compass へのパスが通っている必要があります。
こういう外部プログラムを起動するタイプのやつはどうするのがベストプラクティスなんですかね。そこら辺のお作法がよくわからなかったので、直接コマンドを叩いてますけど。
(ちなみに LESS の maven プラグインは JRuby 経由で実行する形になってたけど)

Mavenリポジトリで提供されていないサードパーティJarをどうするか

  • Posted by: sinsengumi
  • 2012年12月16日 11:52 AM
  • Java
  • |

Maven のセントラルリポジトリやリムーブリポジトリ(※)で提供されていないサードパーティ Jar や俺俺ライブラリを pom.xml 上でどう管理するかという話です。
管理の仕方によって、開発者や管理者(環境構築をするようなアーキテクトっぽい人)の仕事が変わってくると思います。

http://daipresents.com/2009/maven2_internal_repository_webdav/

方法としては、主に以下の3つがあると思います。

  1. ローカルリポジトリに Jar をインストールする
  2. インターナルリポジトリを立てる
  3. system スコープを使う

1. ローカルリポジトリに Jar をインストールする

Jar ファイルをローカルのリポジトリに手動でインストールする方法。
以下のコマンドを叩くことで、自分のローカルリポジトリに Jar ファイルがインストールされる。

mvn install:install-file -Dfile=jarまでの絶対パス -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=バージョン -Dpackaging=jar -DgeneratePom=true

メリット

  • 特になし?

デメリット

  • 開発者全員が同じコマンドを叩く必要がある
  • ライブラリのバージョンアップ時にも同じ作業が必要

2. インターナルリポジトリを立てる

開発用に新たなリポジトリサーバを立てる。いわゆる社内リポジトリ。
やり方は省略。

http://daipresents.com/2009/maven2_internal_repository_webdav/

メリット

  • 開発者自体は特別な操作を必要としない

デメリット

  • インターナルリポジトリの構築が手間
  • インターナルリポジトリがネットワーク経由でアクセス出来ないと環境構築できない(ソースだけあってもビルドできない)

3. system スコープを使う

ライブラリを system スコープのライブラリとして定義して、Jar ファイル自体はプロジェクト内で持つという方法です。
pom.xml には環境依存しないように systemPath を ${basedir} 等を使って定義する。。

<dependency>
	<groupId>net.sinsengumi</groupId>
	<artifactId>ThirdPartyJar</artifactId>
	<version>1.0</version>
	<scope>system</scope>
	<systemPath>${basedir}/lib/ThirdPartyJar.jar</systemPath>
</dependency>

メリット

  • 開発者は特別な操作を必要としない

デメリット

  • ライブラリをプロジェクト内で持つ必要がある(バージョン管理システムに含めなければならなかったりして、ファイルサイズが大きくなる)

※Webアプリケーションについて

Webアプリケーションの場合、WEB-INF/lib に Jar ファイルを置くことで自動的にクラスパスに含まれるので、ここに配置してしまうという手もありますが、pom.xml に依存性を書く必要がなくなるので、このライブラリだけ pom.xml に書かれてないみたいな気持ち悪さがあります。
そして、pom.xml に書かれてないと、mvn test 時にクラスパスが通らないという問題もあります。

あと、ライブラリをプロジェクト内で持つ必要がある、というデメリットも当然あります。

まとめ

方法としては、2. か 3. のどちらかかなぁと思います。
自分は管理の簡便性を求めて、3. でやっています。
プロジェクトの規模感で 2. でやる方法もありかなぁと思います。

HttpServletRequestWrapperを使って、HTTPリクエストを改変する

OpenAMは、認証済のユーザー情報をHTTPリクエストヘッダーに載せてくれるんですが、Servletでそれってどうやって実現してるのかなーと思って、調べたら HttpServletRequestWrapper クラスを使うようです。

使い方ですが、HttpServletRequestWrapper は、HttpServletRequestインタフェースを実装しているので、HttpServletRequestインタフェースのメソッドをオーバーライドして独自の実装を定義します。
http://mergedoc.sourceforge.jp/tomcat-servletapi-5-ja/javax/servlet/http/HttpServletRequestWrapper.html

今は、すべてのリクエストのヘッダーに情報を付与しようとしているので、getHeader辺りをオーバーライドします。

package org.sample;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class HeaderAddedHttpServletRequest extends HttpServletRequestWrapper {

    private Map<String, String> addHeaders;

    public HeaderAddedHttpServletRequest(HttpServletRequest request, Map<String, String> addHeaders) {
        super(request);

        if (addHeaders == null) {
            this.addHeaders = new HashMap<String, String>();
        } else {
            this.addHeaders = addHeaders;
        }
    }

    @Override
    public String getHeader(String name) {
        String header = super.getHeader(name);
        if (header != null) {
            return header;
        }

        header = addHeaders.get(name);
        if (header != null) {
            return header;
        }

        return null;
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        List<String> newHeaderNames = new ArrayList<String>();

        Enumeration<String> currentHeaderNames = super.getHeaderNames();
        while (currentHeaderNames.hasMoreElements()) {
            newHeaderNames.add(currentHeaderNames.nextElement());
        }

        for (Entry<String, String> e : addHeaders.entrySet()) {
            newHeaderNames.add(e.getKey());
        }

        return Collections.enumeration(newHeaderNames);
    }
}

で、すべてのリクエストがこのWrapperクラスを使うようにFilterを定義します。

package org.sample;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter("/*")
public class AddHeaderFilter implements Filter {

    @Override
    public void init(FilterConfig fConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        Map<String, String> addHeaders = new HashMap<String, String>();
        addHeaders.put("X-USER-ID", "HIJIKATA");
        addHeaders.put("X-USER-NAME", "土方歳三");

        chain.doFilter(new HeaderAddedHttpServletRequest((HttpServletRequest) request, addHeaders), response);
    }
}

これで、HTTPリクエストが飛んできたら自動でヘッダーが付与サれてます。

package org.sample;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/SampleServlet")
public class SampleServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);

            System.out.println(headerName + "=" + headerValue);
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
}
host=localhost:8080
connection=keep-alive
user-agent=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.152 Safari/535.19
accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
accept-encoding=gzip,deflate,sdch
accept-language=ja,en-US;q=0.8,en;q=0.6
accept-charset=Shift_JIS,utf-8;q=0.7,*;q=0.3
X-USER-ID=HIJIKATA
X-USER-NAME=土方歳三

同様の事はレスポンスでもできます。

シンプルな HTTP Server 書いた

以前の記事の続きということで、シンプルな HTTP Server を書いた。

classファイル一つで動くというお手軽サーバー。マルチスレッド?自分しか使わないからいいんですよ。
お手軽に使いたいのでデフォルトパッケージ。

java SimpleHttpServer "C:\htdocs" 81
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class SimpleHttpServer {

    private static String requestPath;

    private static Map<String, String> CONTENT_TYPES = new HashMap<String, String>();

    static {
        CONTENT_TYPES.put("html", "text/html");
        CONTENT_TYPES.put("htm", "text/html");
        CONTENT_TYPES.put("css", "text/css");
        CONTENT_TYPES.put("js", "text/javascript");
        CONTENT_TYPES.put("jpg", "image/jpeg");
        CONTENT_TYPES.put("jpeg", "image/jpeg");
        CONTENT_TYPES.put("png", "image/png");
        CONTENT_TYPES.put("gif", "image/gif");
        CONTENT_TYPES.put("pdf", "application/pdf");
        CONTENT_TYPES.put("txt", "text/plain");
        CONTENT_TYPES.put("xml", "text/xml");
        CONTENT_TYPES.put("zip", "application/zip");
        CONTENT_TYPES.put("exe", "application/octet-stream");
    }

    public static void main(String[] args) throws IOException {
        String documentRoot = args[0];
        int port = Integer.parseInt(args[1]);

        ServerSocket server = new ServerSocket(port);

        while (true) {
            Socket client = server.accept();

            // HTTPリクエストを出力
            outputRequest(client);

            // HTTPレスポンスを出力
            outputResponse(client, documentRoot);
        }
    }

    private static void outputRequest(Socket client) throws IOException {
        System.out.println("------------------------------------------");

        BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));

        // 1行目からリクエストパスを取得しておく(/でアクセスされた場合はindex.htmlを表示)
        String inline = br.readLine();
        requestPath = inline.split(" ")[1];
        if (requestPath.endsWith("/")) {
            requestPath += "index.html";
        }

        while (br.ready() && inline != null) {
            System.out.println(inline);
            inline = br.readLine();
        }

        System.out.println("------------------------------------------");
    }

    private static void outputResponse(Socket client, String documentRoot) throws IOException {
        PrintStream ps = new PrintStream(client.getOutputStream());

        String responseFile = documentRoot + requestPath;
        File file = new File(responseFile);

        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);

            // ヘッダー出力
            int bodyLength = (int) file.length();
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Length: " + bodyLength);
            ps.println("Content-Type: " + getContentType(file));
            ps.println("");

            // ボディ出力
            byte buf[] = new byte[bodyLength];

            fis.read(buf);
            ps.write(buf, 0, bodyLength);
            ps.flush();
        } catch (FileNotFoundException e) {
            // ヘッダー出力
            ps.println("HTTP/1.1 404 Not Found");
            ps.println("");
            ps.println("<h1>指定されたファイルは存在しません。</h1>");
        } finally {
            if (fis != null) {
                fis.close();
            }
        }

        ps.close();
    }

    private static String getContentType(File file) {
        String extension = getExtension(file);

        String contentType = CONTENT_TYPES.get(extension);

        if (contentType == null) {
            return "text/html";
        } else {
            return contentType;
        }
    }

    private static String getExtension(File file) {
        String path = file.getPath();
        int lastDotPosition = path.lastIndexOf(".");

        String extension = null;
        if (lastDotPosition == -1) {
            extension = "";
        } else {
            extension = path.substring(lastDotPosition + 1).toLowerCase();
        }

        return extension;
    }
}

コードはGistにもペタリ。
https://gist.github.com/2260004

Socket の勉強のためにエコーサーバー書いた

前々から、Jarひとつで動くHTTPサーバーを書いてみたいと思っていて(既にたくさんあると思うけど)、それには Socket でがちゃがちゃしないといけないとの事だったので、重い腰を上げて Socket の勉強をしてみた。

とりあえず、リクエストされた文字列をそのまま返すだけのエコーサーバーを書いてみた。
一応、マルチスレッド対応。

Server

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {

    public static void main(String[] args) throws IOException {

        ServerSocket server = new ServerSocket(5000);

        // クライアントからの接続を待ち受ける
        while (true) {
            Socket client = server.accept();

            new Thread(new EchoServer().new EchoThread(client)).start();
        }
    }

    class EchoThread implements Runnable {

        Socket client;

        public EchoThread(Socket client) {
            this.client = client;
        }

        @Override
        public void run() {
            echo(client);
        }

        private void echo(Socket client) {
            System.out.println("処理開始");

            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            }

            System.out.println("接続中 : " + client.getRemoteSocketAddress());

            try {
                InputStream in = client.getInputStream();
                OutputStream out = client.getOutputStream();

                int recvByte;
                while ((recvByte = in.read()) != -1) {
                    out.write(recvByte);
                }
            } catch (IOException e) {
                System.err.println(e);
            } finally {
                closeQuietly(client);
            }

            System.out.println("処理終了");
        }

        private void closeQuietly(Socket socket) {
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                // ignore
            }
        }
    }
}

Client

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class EchoClient {

    public static void main(String[] args) throws IOException {

        Socket socket = new Socket("localhost", 5000);
        System.out.println("サーバとの接続を確立 : " + socket.getLocalPort());

        InputStream in = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        byte[] data = args[0].getBytes();
        out.write(data);
        System.out.println("送信 : " + new String(data));

        int i = 0;
        byte[] output = new byte[data.length];
        int recvByte;
        while ((recvByte = in.read()) != -1) {
            output[i++] = (byte) recvByte;

            if (i >= data.length) {
                break;
            }
        }

        System.out.println("受信 : " + new String(output));

        socket.close();
    }
}

バイトの読み書きを1バイトずつやってるので、あまり効率的ではないんだろうな。

なぜ Java の配列は共変で、Generics は共変ではないのか

まず、Java の配列がタイプセーフではない話。

public class ExampleArray {

    public static void main(String[] args) {
        String[] strArray = {"test1", "test2"};
        Object[] objArray = strArray; // 配列は共変なので代入可能

        objArray[0] = new Integer(3); // java.lang.ArrayStoreException
    }
}

上記のように、Java の配列は共変という性質を持っているので、Object[] に String[] を代入することができます。つまり、String[] は Object[] のサブクラスである、ということです。
しかし、Generics の場合は、この性質が当てはまりません(Generics は共変ではない)

public class ExampleGenerics {

    public static void main(String[] args) {
        List<String> strList = new ArrayList<String>();
        strList.add("test1");
        strList.add("test2");

        List<Object> objList = new ArrayList<Object>();

        objList = strList; // コンパイルエラー
    }
}

なぜ、配列は共変で、Generics は共変ではないのか?(一緒の方が分かりやすいのに)

その理由の前に、
配列の例で見たように、型の混入現象は、強い型付け言語である Java としては避けたい現象です。
配列の場合は、不正な型が混入した場所で例外(java.lang.ArrayStoreException)を投げてくれます。
(これは、結構大事な所で、使用時(get)ではなく、設定時(set)にきちんと例外を投げてくれると、バグの混入場所が特定しやすいです)
なぜこんなことができるかというと、配列は自分が何の型であるかを自身で知っている(バイトコードに型情報が存在する)ので、違う型を入れたときに、自分と違うということが判定できるのです。

一方、Generics の場合はコンパイル時に型消去という操作が行われます。
型消去については、別エントリーでも書いてます(Generics(Java)の型消去について

Generics のコードは、コンパイル後にはその型情報を一切残していないので、java.lang.ArrayStoreException のような例外を投げることができません。
そのため、共変ではなくして、型の混入を防いでいるのではないかと思います。

Generics で共変っぽいことをしたい場合は、extends とかの境界条件をつければ可能です。

JPA Hibernate (H2 database) をやってみる

最近、Play framework が面白いなぁと思ってちょろちょろ遊んでたりするんですが、Play はモデル層に JPA を採用していて、実は JPA って一度も触った事が無かったのでこれを機にどういうものなのか試して見ました。

プロジェクトの準備

JPA は昔は Java EE(EJB) でしか使えなかったらしいですが、スタンドアロンでも使えるようになったらしいので、スタンドアロンで試してみます。

JPA の実装は Hibernate を使用します。
この Hibernate がやたら依存モジュールが多いので、簡単のため maven プロジェクトにします。

mvn archetype:create -DgroupId=com.example -DartifactId=JPASample

pom.xml に以下を書いておく。
pom.xml に追加する jar はここらへんを参考にしました。注意点としては、JPA を使用するには、hibernate-entitymanager というモジュールが必要なのでそれを必ず追加しておくこと。
あとは、永続化先に使用する DB の JDBC も忘れずに追加しておくこと(今回は H2 database を使用)

<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.example</groupId>
    <artifactId>JPASample2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>JPASample2</name>
    <url>http://maven.apache.org</url>

    <properties>
        <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.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>3.6.8.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.6.8.Final</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.4</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.162</version>
        </dependency>
    </dependencies>
</project>

できたプロジェクトを Eclipse にインポートさせるため、以下のコマンドを叩いて Eclipse プロジェクトにしておく。

cd JPASample
mvn eclipse:eclipse

できたら、Eclipse からインポートする。

データベース の準備

永続先として今回は H2 database を使用。Jar ファイル1つで完結するのでとても簡単。
ここから Jar を DL して Jar 実行。

java -jar h2-1.3.162.jar

Web コンソールが立ち上がると思うので、ログイン(ID、パスワードはそのままでOK)して、テーブルを作成しておく。

CREATE TABLE EMPLOYEE(ID INT PRIMARY KEY, NAME VARCHAR(255));

JPA の準備

まずテーブルにマッピングさせるエンティティクラスを作成する。

@Entity
public class Employee {

    @Id
    public int id;
    public String name;

    public Employee() {
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

次に、JPA が DB の設定などを行う persistence.xml を作成する。
配置場所はクラスパスが通っている場所に配置する(今回は src/main/resources/META-INF/persistence.xml に配置した)
Eclipse でちゃんとクラスパス設定を行うこと。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

    <persistence-unit name="sample" transaction-type="RESOURCE_LOCAL">
        <class>com.example.Employee</class>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.connection.driver_class" value="org.h2.Driver" />
            <property name="hibernate.connection.username" value="sa" />
            <property name="hibernate.connection.password" value="" />
            <property name="hibernate.connection.url" value="jdbc:h2:tcp://localhost:9092/demo" />
            <property name="hibernate.max_fetch_depth" value="3" />
        </properties>
    </persistence-unit>
</persistence>

実行

あとは、JPA を使って実行するのみ。

public class App {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("sample");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            em.persist(new Employee(1, "近藤 勇"));
            em.persist(new Employee(2, "土方 歳三"));

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}

H2 database にデータがきちんと登録されていれば成功。

Generics(Java)の型消去について

Java の Generics の実装方式の型消去についてちょっと調べました。

そもそも ジェネリックプログラミングというのは、Java だけにあるものではなく色んな言語に同様の機能があるようです。
ジェネリックプログラミング wikipedia

Java の場合、言語仕様的に、どうやって Generics を実装しているかというと「型消去(type erasure)」によって行われている。
型消去とは、「コンパイル後のバイトコードに型情報を残さない」ということ。

具体的なサンプルコードで見てみる。

public void method15() {
    List<String> list = new ArrayList<String>();
    list.add("string");
    String str = list.get(0);
}

public void method14() {
     List list = new ArrayList();
     list.add("string");
     String str = (String) list.get(0);
}

これを、jad したものが以下。

  public void method15();
     0  new java.util.ArrayList [15]
     3  dup
     4  invokespecial java.util.ArrayList() [17]
     7  astore_1 [list]
     8  aload_1 [list]
     9  ldc <String "string"> [18]
    11  invokeinterface java.util.List.add(java.lang.Object) : boolean [20] [nargs: 2]
    16  pop
    17  aload_1 [list]
    18  iconst_0
    19  invokeinterface java.util.List.get(int) : java.lang.Object [26] [nargs: 2]
    24  checkcast java.lang.String [30]
    27  astore_2 [str]
    28  return

  public void method14();
     0  new java.util.ArrayList [15]
     3  dup
     4  invokespecial java.util.ArrayList() [17]
     7  astore_1 [list]
     8  aload_1 [list]
     9  ldc <String "string"> [18]
    11  invokeinterface java.util.List.add(java.lang.Object) : boolean [20] [nargs: 2]
    16  pop
    17  aload_1 [list]
    18  iconst_0
    19  invokeinterface java.util.List.get(int) : java.lang.Object [26] [nargs: 2]
    24  checkcast java.lang.String [30]
    27  astore_2 [str]
    28  return

確かに、コンパイルされると型情報が消去されて、Java 1.4 時代のものと同じになっている。

なぜ、Java では型消去という実装方式を採用したか?

C++ の場合、同様の機能にテンプレートというものがあるらしく、そちらはコンパイル後も型情報をインライン展開して残すらしいです。
で、Java の場合、なぜ型消去という実装方式を採用したかというと、「後方互換性」のためらしい。

先の例で見たように、コンパイル後のコードが全く同じになるので、1.4 以前で書かれたコードと 1.5 以上で書かれてコードがコードが混在していても実行できる、ということ。

なるほど、よく出来てる。

Servlet 3.0 のアノテーションでは Filter の順番を指定できない?

Servlet 3.0 になってから HttpServlet クラスの @WebServlet 同様に、Filter クラスも @WebFilter というアノテーションが指定できるようになっていて、やたら便利です。

@WebFilter(dispatcherTypes = { DispatcherType.REQUEST }, urlPatterns = { "/*" },
        initParams = { @WebInitParam(name = "encoding", value = "UTF-8") })
public class EncodingFilter implements Filter {

    private String encoding = null;

    @Override
    public void init(FilterConfig fConfig) throws ServletException {
        encoding = fConfig.getInitParameter("encoding");
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        chain.doFilter(request, response);
    }
}

が、どうもアノテーションだけでは、Filter クラスの順番を定義することはできないようです。
なので、結局、web.xml に Filter を上から順番に書いていくしかないようです、残念。
(指定する方法があったら教えて欲しいです)

ちなみに @WebFilter に指定するアノテーション定義はこちら
http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebFilter.html

やはり、順番を指定する指定は無いっぽい。

LDRich という livedoor Reader のクライアントアプリを作りました

Android で何か作りたくて、勢いで Galaxy S を買ってしまったので、アプリを作りました。

LDRich という Android アプリで、livedoor Reader のクライアントです。
ネーミングに特に意味はなくて、思いついたものは全て使われていて、ゴロで決めました。


Android 使っている人で、livedoor Reader ユーザーは使っていただけたら嬉しいです。

Android Market
使い方の解説ページ

Home > Tags > Java

Search
Feeds
Meta

Return to page top