タイトル/名前 | 更新者 | 更新日 |
---|---|---|
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 |
Twitter発祥のシンプルなテンプレート記法とその実装。色々な言語で実装されている。
Javaの実装:
Groovyの実装(いくつか独自拡張を含んでいる)
練習コード:
参考:
おんなじようなこと考えてる人は沢山いるっぽい。
ただ、今のところ、どれも最終的に取り込まれたのかどうか、状況がはっきりしない、というか各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でも困らないといえば困らない。
勉強用の、参考URLのリンク集、メモ書きです。
JavaEEのバージョン:
2015-02現在、Java8も踏まえた2.0を策定中らしい。JavaEEに組み込まれるとしたら、JavaEE8からかな?
個人的には以下の記事にある通り、testabilityの向上と依存関係の排除という効果に期待が大きい。
同時に、オブジェクトのライフサイクルや注入されるタイミングなど、CDIの仕様とのすり合わせ・調査などで、CDIのレイヤーに関わる検証やトラブルが増えることも予想される。
また、DIコンテナは便利「すぎる」部分もあると思う。本来の想定範囲を踏まえた上での意図的な逸脱ならまだしも、技術的に可能というだけでイレギュラーな設定や使い方を当たり前のものとしてしまうと、後々大きなトラブルに繋がりそうなので、気にしておきたい。
他参考
近接仕様としてのJSR300 : Dependency Injection for Java
実装の代表
参考:
RESTfulなAPIやWebアプリケーションを開発する際に、一つの疑問が生じる。
RESTでは「ステートレス」を重視して、サーバサイドでのセッション管理ではなく、クライアント側で認証情報や状態を保持して、サーバに都度送る方式を唱えている。これはHTTPで実装するなら、Cookieを使ったセッション管理ではなく、BasicやDigest認証など、HTTP認証を使うことになる。
しかし現実問題として、モダンなWebサイトでBasic/Digest認証を扱うことはなく、サーバサイドのCookieを使ったセッション管理を使うことになる。
RESTにおいて、ステートレスという特性と、現実のセッション管理をどうバランシングすれば良いのか?
うまく整理しきれていないが、結論を三行に濃縮してみる:
以下、自分なりにRESTについて調べ、上記観点についてまとめた内容となる。
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にセット)
・・・できるかな・・・?
なお、"@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)
参考URL、特に整理せず。
http://blog.goo.ne.jp/sohgoh/e/da265cc4da113befa20340f3a1f52d5d
http://thinking.ne.jp/2012/10/aws-backup/
http://koseki.hatenablog.com/entry/20110804/ebssnapshot
http://blog.goo.ne.jp/sohgoh/e/659c34a797c3774ad48cae0c5fe73e93
http://www.mori-soft.com/2008-08-15-01-36-37/os/136-ec2
http://blog.genies.jp/2012/12/amazon-ec2-amazon-linux.html
http://labs.septeni.co.jp/?p=958
http://rmrf.netspiders.net/post/25991242597/amazon-linux-ebs
http://shanon-tech.blogspot.jp/2013/02/awstagsnapshot.html
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/role-usecase-ec2app.html
http://recipe.kc-cloud.jp/archives/2517
http://dev.classmethod.jp/cloud/iam-roles-for-ec2-instances/
考え方としては、IAM role でEC2インスタンスにSnapshotとったりEBS Volumeの一覧取得するpermissionを与えた後、AWSCLIなどを使って以下のような処理を、スクリプトとか使って組み立てる。
EclipseやNetBeansからServletアプリをTomcat上でローカルデバッグするとき、Javaソース編集してclass更新された後のHotSwapが時々失敗する。
JRebelという有償製品が、HotSwapを安定して動かせるようになるらしい。
購入する前の、調査メモです。
ユーザの声:
技術的に関連してそうなもの:
クラスファイルの更新監視ということであれば、ファイル更新の監視関連トピック:
こんなのもある:
JavaのClassLoaderの闇は深いので、有償製品で楽できるなら、それに頼るのもありか。
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: "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サーバのアドレスは、ご利用のプロバイダのアドレスなどに書き換えて試して下さい。
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.
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
JavaでのSocketおよびInetAddressを使ったプログラミングで、DNSの名前解決はどのように行われているのか、ソースなどを追いかけてみた。
三行で結論:
まず "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); } }
簡単にまとめると、以下のような処理になっている。
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での名前解決処理をカスタマイズするには、これまで見てきた内容を整理すると以下のようになる。
また実装サンプルと思わしきコードがあるので、これも参考になる。
JavaのDNSキャッシュとして "%JAVA_HOME%\lib\security\java.security" に設定する"networkaddress.cache.ttl"という値がある。InetAddressのJavaDocに説明がある。
これまで見てきたように、Javaにおける名前解決は以下の2種類の実装が提供されている。
では、"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(); } } /* ... */
これはあくまでも「Javaライブラリのレイヤーで名前解決したのをキャッシュする期間」である。そして、InetAddress経由の名前解決は、デフォルトではnative(=JNI)の実装になってしまい、その内部までは調べきれなかった。
そのため、以下のようなケースではどうなるか、確認していない。
この点を確認するのであれば、自前で 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ミラーソース
使っているJavaのデフォルトの文字セット、および利用できる文字セットの標準名と別名を一覧出力します。
意外にも、MacOSX(Lion, 10.7.2)のJava(JDK 1.6.0_29)のデフォルトはShift_JISになっていた。