home ホーム search 検索 -  login ログイン  | help ヘルプ

find 検索

111 - 120 / 1320    [|<]  [|<]  [<]  11  12  13  14  15  16  17  18  19  20   [>]  [>|][>|]
タイトル/名前 更新者 更新日
Java/ポストJSP時代のモダンViewコンポーネント : mustache.java msakamoto-sf 2015-02-21 19:41:17
日記/2015/02/18/Mustacheでレイアウト(layout)風味のテンプレート→ギブアップ msakamoto-sf 2015-02-18 00:27:37
Java/CDI(JSR299,JSR346)とDI(JSR300)のメモ msakamoto-sf 2015-02-15 22:06:41
技術/HTTP/REST設計思想の "Stateless" との付き合い方 msakamoto-sf 2015-02-15 20:03:11
Java/JAX-RS/Jerseyで、FilterからResource Classに値を渡せるか msakamoto-sf 2015-02-12 01:19:28
日記/2015/02/08/EC2のインスタンス内から自分にattachしてるEBSをsnapshotを取る msakamoto-sf 2015-02-08 01:14:51
Java/JRebelの下調べメモ msakamoto-sf 2015-02-08 01:11:51
Java/JNDIのDNSサービスプロバイダによる名前解決 msakamoto-sf 2015-02-08 00:58:37
Java/Socket, InetAddressにおけるDNS名前解決の仕組みと networkaddress.cache.ttl msakamoto-sf 2015-02-08 00:16:40
Java/文字コードメモ3 msakamoto-sf 2015-02-08 00:12:03
ソート項目 / ソート順     1ページ 件ずつ表示

Java/ポストJSP時代のモダンViewコンポーネント : mustache.java  

所有者: msakamoto-sf    作成日: 2015-02-21 19:36:15
カテゴリ: Groovy Java 
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-21 19:41:17
md5:bea81518eb21e741726155dcb7d4edc0
sha1:56d09b374c0debe495530a373351a163f5c5d664

日記/2015/02/18/Mustacheでレイアウト(layout)風味のテンプレート→ギブアップ  

所有者: msakamoto-sf    作成日: 2015-02-18 00:14:22
カテゴリ: Java 

おんなじようなこと考えてる人は沢山いるっぽい。

ただ、今のところ、どれも最終的に取り込まれたのかどうか、状況がはっきりしない、というか各IssueやPull Requestを追いきれてない。

少なくとも公式のmustache(5)のページにはそれらしき機能がまだ解説されてないので、駄目なのかも。

強いて似たようなことが出来るとすれば、

{{#layout}}
body
{{/layout}}
|

にして、layout()メソッドで枠をレンダリングということになるんだろうけど、layout()の中でもまたmustacheのれんプレートを処理するので・・・ああ、結局、やっぱり、mustacheのコンパイルは二回起こるのか。しかも、body側のmustache処理のスタックの中でさらにmustacheが走るので、ちょっと怖いかも。

ソレくらいなら、bodyをcompileしてrenderして、それが終わってからlayoutをcompileしてrenderして、という風に分割した方が分かりやすいし危なく無さそう。

{{>header}}

...

{{>footer}}

みたいなpartial形式にしても良いのだけれど、partialについてのcompile & renderがどうなるか分からんし、割りとこの形での分割は融通効かないのでやめときたい。

実際の、compile & renderにかかる時間は計測するしか無いが・・・最悪、compileしたのをキャッシュしておきたいところだが、スレッドセーフにrenderしてくれないと、タイミングによっては別リクエストのレスポンス内容が混ざるとか困った状況になりかねない。その部分の確証がもてるまでは、都度compile & renderでも困らないといえば困らない。


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-18 00:27:37
md5:fe3858efb17702561eeb42bfe7b3b4c8
sha1:abbf410e4387261340797d7d5b8f84f6d749b877

Java/CDI(JSR299,JSR346)とDI(JSR300)のメモ  

所有者: msakamoto-sf    作成日: 2015-02-15 22:06:00
カテゴリ: Java 

勉強用の、参考URLのリンク集、メモ書きです。

JavaEEのバージョン:

2015-02現在、Java8も踏まえた2.0を策定中らしい。JavaEEに組み込まれるとしたら、JavaEE8からかな?

個人的には以下の記事にある通り、testabilityの向上と依存関係の排除という効果に期待が大きい。

同時に、オブジェクトのライフサイクルや注入されるタイミングなど、CDIの仕様とのすり合わせ・調査などで、CDIのレイヤーに関わる検証やトラブルが増えることも予想される。
また、DIコンテナは便利「すぎる」部分もあると思う。本来の想定範囲を踏まえた上での意図的な逸脱ならまだしも、技術的に可能というだけでイレギュラーな設定や使い方を当たり前のものとしてしまうと、後々大きなトラブルに繋がりそうなので、気にしておきたい。

他参考

近接仕様としてのJSR300 : Dependency Injection for Java

実装の代表

参考:



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-15 22:06:41
md5:2f633947a1cbe49fb99ae402cbe65ea8
sha1:2f271be177c1d5f57c99aa21253980aed7d5a415

技術/HTTP/REST設計思想の "Stateless" との付き合い方  

所有者: msakamoto-sf    作成日: 2015-02-11 21:34:52
カテゴリ: HTTP プログラミング 

RESTfulなAPIやWebアプリケーションを開発する際に、一つの疑問が生じる。
RESTでは「ステートレス」を重視して、サーバサイドでのセッション管理ではなく、クライアント側で認証情報や状態を保持して、サーバに都度送る方式を唱えている。これはHTTPで実装するなら、Cookieを使ったセッション管理ではなく、BasicやDigest認証など、HTTP認証を使うことになる。
しかし現実問題として、モダンなWebサイトでBasic/Digest認証を扱うことはなく、サーバサイドのCookieを使ったセッション管理を使うことになる。

RESTにおいて、ステートレスという特性と、現実のセッション管理をどうバランシングすれば良いのか?

うまく整理しきれていないが、結論を三行に濃縮してみる:

  1. RESTは2000年前後のWebに対するカウンターパンチとして提唱されたため、恐らく意図的に極端な主張となっている。
  2. RESTは設計思想であり、現場で吟味した上での「おいしいとこどり」でも構わない。
  3. RESTの下に自分たちが開発するWebアプリがあるのではなく、Webアプリ開発を支える設計思想の一つに、RESTの一部を取り込めればそれで良い。

以下、自分なりにRESTについて調べ、上記観点についてまとめた内容となる。

(全て表示する)
プレーンテキスト形式でダウンロード
現在のバージョン : 2
更新者: msakamoto-sf
更新日: 2015-02-15 20:03:11
md5:cb5ec4ae6db370f004f134ddfa6d907a
sha1:3509e86e85b44388ec11a7921808546f89a844a6

Java/JAX-RS/Jerseyで、FilterからResource Classに値を渡せるか  

所有者: msakamoto-sf    作成日: 2015-02-12 01:17:24
カテゴリ: Java 

Java/JAX-RS/Jerseyで、FilterからResource Classに値を渡せるか

イメージ的には、認証系のFilterでHttpSessionからドメインクラスのインスタンスを取り出して、Resource Classに"@Context"か"@Inject"で注入できたらなぁ。

→結論:ストレートにFilterからResource Classに渡すことは多分できない。ただし、FilterからHttpServletRequestにインスタンスを設定することは出来るため、RequestScopeでCDIのProviderを用意して、Resource Classへの注入はFactory側で HttpServletRequest を参照、Filter側でドメインクラスのインスタンスを取り出して HttpServletRequest に設定、みたいなアプローチは多分取れそう(未検証)。

そもそも、FilterとResource ClassではLifeCycleが違う。Resource ClassはRequestごとにインスタンスが生成され、Filterはアプリケーション単位で生成される。
Resource Classに "@Inject" できるということは、それは RequestScope になる。
しかし、FilterはRequestごとにインスタンスが生成されないため、Filterに "@Context" で HttpServletRequest を注入することはできない。
Filterの filter(ContainerRequestContext requestContext) で、ContainerRequestContextに getProperty()/setProperty() がある。
これを使って、Filterの方で HttpServletRequest -> HttpSession -> 認証情報や現在のステートを保持するドメインクラスの取り出し -> ContainerRequestContext#setProperty() (= HttpServletRequestにセット)

  • > Resource Classへの注入時に、RequestScope な Factory で "@Context" で HttpServletRequestを注入し、そこから Filter がセットしたドメインクラスのインスタンスを取り出し、Resource Classに最終的に注入。

・・・できるかな・・・?

なお、"@Inject" の使用自体は、JavaEE6以降のCDIの採用で、GlassFish側でもCDIとJerseyの相互連携が取れるようになったので、仕様上問題なく、思想的にもJavaEEの枠組みに収まっていると思う(こんなこと考える事自体がもう厭になってくる)。

よって、"@Inject" を効果的に使い、HttpSesssion にまつわる煩雑かつコピペの多いコーディングを減らすのは、合理的な判断であると思う。

また、"stateless"を重視してサーバサイドセッションを使わない方針のRESTにおいて、HttpSession=Servletにおけるサーバサイドセッションを使うことの是非については、個人的には 技術/HTTP/REST設計思想の "Stateless" との付き合い方 にて現実優先の結論を出しており、無理にRESTに従う必要はなく、枯れたサーバサイドのセッション管理機構を採用するという判断は十分に合理的であると考える。

Resource Class と Filter のLife Cycle 参考:

Resource Class : リクエストごと。

3.1.1 Lifecycle and Environment
By default a new resource class instance is created for each request to that resource. First the constructor (see
Section 3.1.2) is called, then any requested dependencies are injected (see Section 3.2), then the appropriate
method (see Section 3.3) is invoked and finally the object is made available for garbage collection.
An implementation MAY offer other resource class lifecycles, mechanisms for specifying these are outside
the scope of this specification. E.g. an implementation based on an inversion-of-control framework may
support all of the lifecycle options provided by that framework.

Filter : アプリケーションで一個。

6.4 Lifecycle
By default, just like all the other providers, a single instance of each filter or entity interceptor is instantiated
for each JAX-RS application. First the constructor is called, then any requested dependencies are injected,
then the appropriate methods are called (simultaneously) as needed. Implementations MAY offer alternative
lifecycle options beyond the default one. See Section 4.1 for additional information.

出典 : JAX-RS: Java(TM) API for RESTful Web Services (Version 2.0 Final Release, May 22, 2013)



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-12 01:19:28
md5:4f2b799e11feef31dfa3aac7919a3ffb
sha1:7fe56a9115f2abe46d2067fe09acb795ac32dc0d

日記/2015/02/08/EC2のインスタンス内から自分にattachしてるEBSをsnapshotを取る  

所有者: msakamoto-sf    作成日: 2015-02-08 01:12:11
カテゴリ: AWS 
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-08 01:14:51
md5:501402cfd60ee59ad4b5b8d60f91c513
sha1:3e0ad73a09353635e8b0fc79c7d05717112fad05

Java/JRebelの下調べメモ  

所有者: msakamoto-sf    作成日: 2015-02-08 01:09:40
カテゴリ: Java 

EclipseやNetBeansからServletアプリをTomcat上でローカルデバッグするとき、Javaソース編集してclass更新された後のHotSwapが時々失敗する。
JRebelという有償製品が、HotSwapを安定して動かせるようになるらしい。
購入する前の、調査メモです。

ユーザの声:

技術的に関連してそうなもの:

クラスファイルの更新監視ということであれば、ファイル更新の監視関連トピック:

こんなのもある:

JavaのClassLoaderの闇は深いので、有償製品で楽できるなら、それに頼るのもありか。



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-08 01:11:51
md5:36ba38eeb0ace00ad936553cbea6c5d0
sha1:8825c0ee7ca46d874e330fe6e5167095035d8614

Java/JNDIのDNSサービスプロバイダによる名前解決  

所有者: msakamoto-sf    作成日: 2015-02-08 00:55:49
カテゴリ: DNS Java ネットワーク 

Java/Socket, InetAddressにおけるDNS名前解決の仕組みと networkaddress.cache.ttl ではSocket, InetAddressによるDNS名前解決を追ったが、最終的に取得できるのはIPアドレスのみだった。

DNSのレコード単位で問い合わせを行ったり、DNSサーバを指定したい場合は、JNDIのDNSサービスプロバイダを利用できる。

この記事では、JNDIのDNSサービスプロバイダを使ったDNSサーバ問い合わせ処理のサンプルコードを紹介する。


サンプルコードの実行環境 : Win7 64bit + Oracle JDK 1.8.0.31

JNDIで使えるDNSサービスプロバイダの実装(Javaソース):

参考資料:


サンプルコード

JndiDnsTest1.java : OSのDNSサーバ設定を使用する。

JndiDnsTest1.java: "google.com" を取得

import java.util.Properties;
 
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
 
public class JndiDnsTest1 {
 
    public static void main(String[] args) throws Exception {
        // see: http://www.ibm.com/developerworks/jp/websphere/library/was/was_jndi/1.html
        Properties env = new Properties();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.dns.DnsContextFactory");
        DirContext ictx = new InitialDirContext(env);
        Attributes attrs = ictx.getAttributes("google.com");
        NamingEnumeration<?> allAttr = attrs.getAll();
        while (allAttr.hasMore()) {
            Attribute attr = (Attribute) allAttr.next();
            System.out.println("Attribute: " + attr.getID());
            NamingEnumeration<?> values = attr.getAll();
            while (values.hasMore())
                System.out.println("Value: " + values.next());
        }
    }
}

実行結果:

Attribute: A
Value: 216.58.220.174
Attribute: NS
Value: ns3.google.com.
Value: ns2.google.com.
Value: ns4.google.com.
Value: ns1.google.com.
Attribute: MX
Value: 30 alt2.aspmx.l.google.com.
Value: 40 alt3.aspmx.l.google.com.
Value: 20 alt1.aspmx.l.google.com.
Value: 10 aspmx.l.google.com.
Value: 50 alt4.aspmx.l.google.com.

→ getAttributes()の引数を "www.google.com" にしてみる:

Attributes attrs = ictx.getAttributes("www.google.com");

実行結果:

Attribute: A
Value: 173.194.38.208
Value: 173.194.38.210
Value: 173.194.38.211
Value: 173.194.38.212
Value: 173.194.38.209

→ AレコードとMXレコードだけに絞ってみる。

Attributes attrs = ictx.getAttributes("google.com", new String[] { "MX", "A" });

※やってみたが、取れたり取れなかったりしたので、スキップ。

JndiDnsTest2.java : DNSサーバを指定

JndiDnsTest2.java: DNSサーバのアドレスは、ご利用のプロバイダのアドレスなどに書き換えて試して下さい。

package jdk8scratch;
 
import java.util.Properties;
 
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
 
public class JndiDnsTest2 {
 
    public static void main(String[] args) throws Exception {
        // see: http://www.ibm.com/developerworks/jp/websphere/library/was/was_jndi/1.html
        Properties env = new Properties();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.dns.DnsContextFactory");
        env.put(Context.PROVIDER_URL, "dns://xxx.yyy.zzz.www");
        DirContext ictx = new InitialDirContext(env);
        Attributes attrs = ictx.getAttributes("google.com");
        NamingEnumeration<?> allAttr = attrs.getAll();
        while (allAttr.hasMore()) {
            Attribute attr = (Attribute) allAttr.next();
            System.out.println("Attribute: " + attr.getID());
            NamingEnumeration<?> values = attr.getAll();
            while (values.hasMore())
                System.out.println("Value: " + values.next());
        }
    }
}

実行結果:

Attribute: AAAA
Value: 2404:6800:4004:807::1002
Attribute: A
Value: 173.194.126.165
Value: 173.194.126.164
Value: 173.194.126.169
Value: 173.194.126.168
Value: 173.194.126.162
Value: 173.194.126.160
Value: 173.194.126.163
Value: 173.194.126.166
Value: 173.194.126.161
Value: 173.194.126.167
Value: 173.194.126.174
Attribute: NS
Value: ns3.google.com.
Value: ns1.google.com.
Value: ns2.google.com.
Value: ns4.google.com.
Attribute: MX
Value: 30 alt2.aspmx.l.google.com.
Value: 50 alt4.aspmx.l.google.com.
Value: 40 alt3.aspmx.l.google.com.
Value: 20 alt1.aspmx.l.google.com.
Value: 10 aspmx.l.google.com.

→ NSとAAAAレコードだけに絞ってみる。

Attributes attrs = ictx.getAttributes("google.com", new String[] { "NS", "AAAA" });

実行結果:

Attribute: AAAA
Value: 2404:6800:4004:812::200e
Attribute: NS
Value: ns4.google.com.
Value: ns1.google.com.
Value: ns3.google.com.
Value: ns2.google.com.

Context.INITIAL_CONTEXT_FACTORY に指定する値は "com.sun.jndi.dns.DnsContextFactory" と "com.example.jndi.dns.DnsContextFactory" のどちらが正?

2015-02-07時点:
JDK7,8の日本語/英語ドキュメント:

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.example.jndi.dns.DnsContextFactory");
                                          ^^^^^^^^^^^

JDK6の日本語/英語ドキュメント:

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
                                          ^^^^^^^

正解:"com.sun.jndi.dns.DnsContextFactory"の方。JDK8で "com.example"にしたら以下の例外発生:

Caused by: java.lang.ClassNotFoundException: com.example.jndi.dns.DnsContextFactory


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-08 00:58:37
md5:4ca5c091ea242d5d240da14fe288c427
sha1:ffecd9fa3a864c61a39de50266ed0168ca770d74

Java/Socket, InetAddressにおけるDNS名前解決の仕組みと networkaddress.cache.ttl  

所有者: msakamoto-sf    作成日: 2015-02-08 00:12:49
カテゴリ: DNS Java ネットワーク 

JavaでのSocketおよびInetAddressを使ったプログラミングで、DNSの名前解決はどのように行われているのか、ソースなどを追いかけてみた。



Socket(String host, int port)での名前解決の仕組み

三行で結論:

  1. java.net.Socketのコンストラクタ "Socket(String host, int port)"
  2. →内部的には "java.net.InetAddress#getByName(String host)" を呼んでる。
  3. →最終的にはnative(=JNI)に辿り着き、今回はそこでギブアップ。

まず "Socket(String host, int port)" コンストラクタでの名前解決の使い方を見てみる。

DnsTest1.java:

import java.net.InetAddress;
import java.net.Socket;
 
public class DnsTest1 {
    public static void main(String[] args) {
        try (Socket socket = new Socket("www.google.com", 80)) {
            InetAddress ia = socket.getInetAddress();
            System.out.println(ia.getHostAddress());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

実行結果:

216.58.220.228

名前解決がどうなっているのか?JDKのソースから、このコンストラクタのソースを確認してみる。

public Socket(String host, int port)
        throws UnknownHostException, IOException
    {
        this(host != null ? new InetSocketAddress(host, port) :
             new InetSocketAddress(InetAddress.getByName(null), port),
             (SocketAddress) null, true);
    }

"host"がnullでなければ、"new InetSocketAddress(host, port)" を呼び出している。

public InetSocketAddress(String hostname, int port) {
        checkHost(hostname);
        InetAddress addr = null;
        String host = null;
        try {
            addr = InetAddress.getByName(hostname);
        } catch(UnknownHostException e) {
            host = hostname;
        }
        holder = new InetSocketAddressHolder(host, addr, checkPort(port));
    }

アドレスを"InetAddress.getByName(hostname)"でInetAddressを取得している。

InetAddress.getByName(String host):

public static InetAddress getByName(String host)
        throws UnknownHostException {
        return InetAddress.getAllByName(host)[0];
    }

試しに、InetAddress.getAllByName() を使ってみる。
DnsTest2.java:

import java.net.InetAddress;
 
public class DnsTest2 {
    public static void main(String[] args) throws Exception {
        InetAddress[] ias = InetAddress.getAllByName("www.google.com");
        for (InetAddress ia : ias) {
            System.out.println(ia.getHostAddress());
        }
    }
}

実行結果:

173.194.38.211
173.194.38.212
173.194.38.209
173.194.38.210
173.194.38.208

"InetAddress.getAllByName(host)"からの流れだが、途中はスキップし、"getAddressesFromNameService(String host, InetAddress reqAddr)"の呼び出しまで進めてみる。

private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr)
        throws UnknownHostException
    {
        /* ... */
            try {
                for (NameService nameService : nameServices) {
                    try {
                        addresses = nameService.lookupAllHostAddr(host);
                        success = true;
                        break;
                    } catch (UnknownHostException uhe) {
                    /* ... */

"nameService" は "sun.net.spi.nameservice.NameService" のListになっており、NameServiceはinterfaceとなっている。

InetAddress.java中の、"nameService"の初期化処理周りのコードを見てみる:

/* Used to store the name service provider */
    private static List<NameService> nameServices = null;
 
    static {
        // create the impl
        impl = InetAddressImplFactory.create();
 
        // get name service if provided and requested
        String provider = null;;
        String propPrefix = "sun.net.spi.nameservice.provider.";
        int n = 1;
        nameServices = new ArrayList<NameService>();
        provider = AccessController.doPrivileged(
                new GetPropertyAction(propPrefix + n));
        while (provider != null) {
            NameService ns = createNSProvider(provider);
            if (ns != null)
                nameServices.add(ns);
 
            n++;
            provider = AccessController.doPrivileged(
                    new GetPropertyAction(propPrefix + n));
        }
 
        // if not designate any name services provider,
        // create a default one
        if (nameServices.size() == 0) {
            NameService ns = createNSProvider("default");
            nameServices.add(ns);
        }
    }

簡単にまとめると、以下のような処理になっている。

  1. "impl" staticメンバに、InetAddressImplのインスタンスを "InetAddressImplFactory.create()" メソッドで生成してセット。
  2. Javaのプロパティで "sun.net.spi.nameservice.provider.1", "sun.net.spi.nameservice.provider.2", ... の順で探していき、指定されているプロバイダ名があれば、InetAddress.createNSProvider()でインスタンスを生成して、nameServicesに追加する。
  3. "createNSProvider("default")" で取得したデフォルトのNameServiceインスタンス(provider)をnameServicesに追加する。

JDK付属のJRE内を "sun.net.spi.nameservice.provider" でgrepしてみたが、特に設定は見つからなかった。
よって、デフォルトの環境では "createNSProvider("default")" で取得したデフォルトのNameServiceインスタンスを使っていると考えられる。

そこで、InetAddress.createNSProvider()の実装を見てみて、"default"を引数に渡した時の挙動を確認する:

private static NameService createNSProvider(String provider) {
        if (provider == null)
            return null;
 
        NameService nameService = null;
        if (provider.equals("default")) {
            // initialize the default name service
            nameService = new NameService() {
                public InetAddress[] lookupAllHostAddr(String host)
                    throws UnknownHostException {
                    return impl.lookupAllHostAddr(host);
                }
                public String getHostByAddr(byte[] addr)
                    throws UnknownHostException {
                    return impl.getHostByAddr(addr);
                }
            };
        } else {
            /* ... "default" 以外のNameServiceプロバイダの生成 ... */
        }
 
        return nameService;
    }

"default" providerの場合、InetAddressの "impl" staticメンバに呼び出しを委譲した NameService インスタンスを生成している。
"impl" は InetAddressImpl の実装であり、 "lookupAllHostAddr()" と "getHostByAddr()" に委譲している。これらの実装を追うため、まず、"InetAddressImplFactory#create()" のソースを確認する。
InetAddressImplFactoryはInetAddress.java内で定義されていた:

/*
 * Simple factory to create the impl
 */
class InetAddressImplFactory {
 
    static InetAddressImpl create() {
        return InetAddress.loadImpl(isIPv6Supported() ?
                                    "Inet6AddressImpl" : "Inet4AddressImpl");
    }
 
    static native boolean isIPv6Supported();
}

native(=JNI)の isIPv6Supported() に応じて、Inet4AddressImplかInet6AddressImplかに、クラスの実体を切り替えている。
今回はInet4AddressImplのソースを確認してみる。

java/net/Inet4AddressImpl.java:

class Inet4AddressImpl implements InetAddressImpl {
    public native String getLocalHostName() throws UnknownHostException;
    public native InetAddress[]
        lookupAllHostAddr(String hostname) throws UnknownHostException;
    public native String getHostByAddr(byte[] addr) throws UnknownHostException;
    private native boolean isReachable0(byte[] addr, int timeout, byte[] ifaddr, int ttl) throws IOException;
    /* ... */

肝心の "lookupAllHostAddr()" などが軒並み native(=JNI) になっている。今回はJNIのソースまでは追い切れないため、Socket -> InetAddress 経由での名前解決処理についてはここで打ち切る。

InetAddressでの名前解決処理をカスタマイズするには

InetAddressでの名前解決処理をカスタマイズするには、これまで見てきた内容を整理すると以下のようになる。

  1. sun.net.spi.nameservice.NameService を実装する。
    1. https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/net/spi/nameservice/NameService.java
    2. また、以下のクラスも実装しないと駄目っぽい。
    3. https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/net/spi/nameservice/NameServiceDescriptor.java
  2. "sun.net.spi.nameservice.provider.N" のJavaプロパティに、実装したprovider名を指定する。
    1. これについても、正確にどういう名前を指定すればよいのかについてはInetAddressのソースなど要確認。SPIに沿ってるぽいので、そのへんも要確認。

また実装サンプルと思わしきコードがあるので、これも参考になる。

"networkaddress.cache.ttl" はどこで使われているのか?

JavaのDNSキャッシュとして "%JAVA_HOME%\lib\security\java.security" に設定する"networkaddress.cache.ttl"という値がある。InetAddressのJavaDocに説明がある。

  • https://docs.oracle.com/javase/jp/8/api/java/net/InetAddress.html
    • デフォルトは「実装に固有の期間キャッシュ」する。
    • -1 だと「ずっとキャッシュする」(キャッシュポイゾニング対策になる)
    • 1以上ならキャッシュ秒数
    • 0ならキャッシュ期間0秒なので毎回問い合わせになりそう。

これまで見てきたように、Javaにおける名前解決は以下の2種類の実装が提供されている。

  1. (Socketコンストラクタ ->) "InetAddress#getByName()" -> native(JNI)
  2. JNDI DNS プロバイダ

では、"networkaddress.cache.ttl" はどちらで使われているか?

結論から言うと、そもそもこのプロパティ値、InetAddressのJavaDocに記載されているのだから、InetAddressでの名前解決で使われる。
JNDI DNS プロバイダについては、sunパッケージ/com.sunパッケージのjavaソースも含めてgrepした範囲では使われている様子は確認できなかった。

"networkaddress.cache.ttl" についてsunパッケージ/com.sunパッケージも含めてJavaソースをgrepしてみると、以下のJavaファイルで参照していた。
sun/net/InetAddressCachePolicy.java: (openjdk7 : https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/net/InetAddressCachePolicy.java )

package sun.net;
 
import java.security.PrivilegedAction;
import java.security.Security;
 
public final class InetAddressCachePolicy {
 
    // Controls the cache policy for successful lookups only
    private static final String cachePolicyProp = "networkaddress.cache.ttl";
    private static final String cachePolicyPropFallback =
        "sun.net.inetaddr.ttl";
 
    // Controls the cache policy for negative lookups only
    private static final String negativeCachePolicyProp =
        "networkaddress.cache.negative.ttl";
    private static final String negativeCachePolicyPropFallback =
        "sun.net.inetaddr.negative.ttl";
 
    public static final int FOREVER = -1;
    public static final int NEVER = 0;
 
    /* default value for positive lookups */
    public static final int DEFAULT_POSITIVE = 30;
 
    /* ... */
    static {
        Integer tmp = null;
 
        try {
            tmp = new Integer(
              java.security.AccessController.doPrivileged (
                new PrivilegedAction<String>() {
                  public String run() {
                      return Security.getProperty(cachePolicyProp);
                  }
              }));
        } catch (NumberFormatException e) {
            // ignore
        }
        if (tmp != null) {
            cachePolicy = tmp.intValue();
            if (cachePolicy < 0) {
                cachePolicy = FOREVER;
            }
            propertySet = true;
        } else {
            /* ... */
            } else {
                /* No properties defined for positive caching. If there is no
                 * security manager then use the default positive cache value.
                 */
                if (System.getSecurityManager() == null) {
                    cachePolicy = DEFAULT_POSITIVE;
                }
            }
        /* ... */

ポイントとしては、このソースコードだと "networkaddress.cache.ttl" or "sun.net.inetaddr.ttl" が指定されていればその値を使うが、どこにも指定が無ければ、DEFAULT_POSITIVE, つまり30秒が名前解決のキャッシュTTLとなる。

現在の実行環境では実際にどの値なのか確認するため、以下のようなGroovyスクリプトを作成し、OracleJDK8のJRE環境で実行してみた。
https://gist.github.com/msakamoto-sf/733aa7a2d0b461766b77

実行結果:

groovy t_networkaddress_cache_ttl.groovy
30       # positive cache は30秒
10       # negative cache は10秒
false    # positive cache 設定は未設定だった。
true     # negative cache は設定されてた。

Javaでは以下のようになる。設定の未設定・設定有りについては省略。
DnsTest3.java:

import sun.net.InetAddressCachePolicy;
 
public class DnsTest3 {
    public static void main(String[] args) throws Exception {
        System.out.println(InetAddressCachePolicy.get());
        System.out.println(InetAddressCachePolicy.getNegative());
    }
}

※Eclipse上で "Access restriction on class due to restriction on required library rt.jar" というエラーが発生した場合は、 Windows -> Preferences -> Java -> Compiler -> Errors/Warnings で "Forbidden reference (access rules)" を Warning または Ignore に設定する。

実際に実行環境の jre/lib/security/java.security を見てみると、以下のように "networkaddress.cache.ttl" はコメントアウトされていて未設定、 "networkaddress.cache.negative.ttl" は10秒に設定されていた。

...

#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
# default value is forever (FOREVER). For security reasons, this
# caching is made forever when a security manager is set. When a security
# manager is not set, the default behavior in this implementation
# is to cache for 30 seconds.
#
# NOTE: setting this to anything other than the default value can have
#       serious security implications. Do not set it unless
#       you are sure you are not exposed to DNS spoofing attack.
#
#networkaddress.cache.ttl=-1

# The Java-level namelookup cache policy for failed lookups:
#
# any negative value: cache forever
# any positive value: the number of seconds to cache negative lookup results
# zero: do not cache
#
# In some Microsoft Windows networking environments that employ
# the WINS name service in addition to DNS, name service lookups
# that fail may take a noticeably long time to return (approx. 5 seconds).
# For this reason the default caching policy is to maintain these
# results for 10 seconds.
#
#
networkaddress.cache.negative.ttl=10

...

InetAddressCachePolicy クラス名で sun/com.sun パッケージ含めたJDKのJavaソースをgrepしたところ、 "java/net/InetAddress.java" でのみ使われていた。
InetAddressの中に Cache クラスというのがネストされて定義されており、その中で参照されている。

/**
     * A cache that manages entries based on a policy specified
     * at creation time.
     */
    static final class Cache {
        private LinkedHashMap<String, CacheEntry> cache;
        private Type type;
 
        enum Type {Positive, Negative};
 
        /**
         * Create cache
         */
        public Cache(Type type) {
            this.type = type;
            cache = new LinkedHashMap<String, CacheEntry>();
        }
 
        private int getPolicy() {
            if (type == Type.Positive) {
                return InetAddressCachePolicy.get();
            } else {
                return InetAddressCachePolicy.getNegative();
            }
        }
        /* ... */

"networkaddress.cache.ttl" のデフォルト値は?

"networkaddress.cache.ttl" はDNSサーバが返すTTLとどう連携するか?

これはあくまでも「Javaライブラリのレイヤーで名前解決したのをキャッシュする期間」である。そして、InetAddress経由の名前解決は、デフォルトではnative(=JNI)の実装になってしまい、その内部までは調べきれなかった。
そのため、以下のようなケースではどうなるか、確認していない。

  • DNSのNSレコードのTTLは数秒になっているのに、SecurityManager付き + "networkaddress.cache.ttl=-1" でJavaを起動した場合。
    • → 最初に名前解決したのがずっとキャッシュされてしまい、DNS側のTTLの設定値とは無関係な動きになってしまうのか?

この点を確認するのであれば、自前で sun.net.spi.nameservice.NameService 及び NameServiceDescriptor を実装して名前解決の要求があったらlogするようにして、それを "sun.net.spi.nameservice.provider.N" あたりに設定すれば良いだろう。
個人的な想像だが、 NameService の lookupAllHostAddr() や getHostByAddr() はDNSサーバからのTTL情報をやりとりできるメソッドシグネチャになってないため、DNSのTTLは InetAddress のキャッシュ処理とは連携出来ないのではないか、と予想される。つまりまず "networkaddress.cache.ttl" によるキャッシュ期間がチェックされ、それがexpireされて始めてDNS問い合わせが発生する。この時DNSサーバからのTTL情報をどう扱うかは、NameServiceの実装に依存することが予想される。

参考資料

networkaddress.cache.ttl 関連:

ネットワーク関連のJavaプロパティ

Eclipse上でプログラミングしている時に、sun.net や com.sun 以下のパッケージやクラスを参照しようとして "Access restriction on class due to restriction on required library rt.jar" みたいなエラーが発生した時

sun/com.sunなどのパッケージを含んだJavaソース

OpenJDK7ミラーソース



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2015-02-08 00:16:40
md5:b80edda6584addecc35afea019d3ae53
sha1:8f1312a6fc6dd77605fafc337827211621d29b05

Java/文字コードメモ3  

所有者: msakamoto-sf    作成日: 2011-12-03 22:38:52
カテゴリ: Java 

使っているJavaのデフォルトの文字セット、および利用できる文字セットの標準名と別名を一覧出力します。

意外にも、MacOSX(Lion, 10.7.2)のJava(JDK 1.6.0_29)のデフォルトはShift_JISになっていた。



プレーンテキスト形式でダウンロード
現在のバージョン : 3
更新者: msakamoto-sf
更新日: 2015-02-08 00:12:03
md5:57108b3018cb4cd9d19308db4102cf52
sha1:a7362dbe033fcabec724986588a4f7b2bcf2839d