今天跟踪代码,发现在IntialContext的构造方法中会调用System.getProperties(),竟然从中得到了在jndi.properties文件中配置的信息,于是就将InitialContext的API中内容又重新读了一遍。
API中写道:
JNDI 通过按顺序合并取自以下两个源的值来确定每个属性值:
- 构造方法的环境参数、(适当属性的)applet 参数,以及系统属性中最先出现的属性。
- 应用程序资源文件 (jndi.properties)。
对于同时存在于两个源或多个应用程序资源文件中的每个属性,用以下方式确定属性值。如果该属性是指定 JNDI 工厂列表的标准 JNDI 属性之一(参见 Context),则所有值都被串联成一个以冒号分隔的列表。对于其他属性,只使用最先找到的值。
这一定位初始上下文和 URL 上下文工厂的默认策略可以通过调用 NamingManager.setInitialContextFactory
资源文件
要简化设置 JNDI 应用程序所需环境的任务,可以将资源文件 与应用程序组件和服务提供程序一起发布。JNDI 资源文件是使用属性文件格式的文件(参见 java.util.Properties
),包括一个键/值对列表。键是属性的名称(例如 "java.naming.factory.object"),而值是使用为该属性定义的格式的字符串。以下是 JNDI 资源文件的一个示例:
java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
JNDI 类库读取资源文件,并使属性值随意可用。因此应该认为 JNDI 资源文件是“所有人可读的”,敏感信息(比如明文密码)不应该存储在那里。
有两种 JNDI 资源文件:提供程序 和应用程序。
提供程序资源文件
每个服务提供程序都有一个可选的资源,该资源列出了特定于该提供程序的属性。此资源的名称是:
[prefix/]jndiprovider.properties
其中 prefix 是提供程序的上下文实现的包名称,其每个句点 (".") 都被转换成一个斜杠 ("/")。 例如,假设服务提供程序定义了一个带有类名称 com.sun.jndi.ldap.LdapCtx 的上下文实现。此提供程序的提供程序资源被命名为 com/sun/jndi/ldap/jndiprovider.properties。如果该类不在一个包中,则资源的名称就是jndiprovider.properties。
JNDI 类库中的某些方法使用指定 JNDI 工厂列表的标准 JNDI 属性:
- java.naming.factory.object
- java.naming.factory.state
- java.naming.factory.control
- java.naming.factory.url.pkgs
在确定这些属性的值时,JNDI 库将参考提供程序资源文件。这以外的属性可由服务提供程序在提供程序资源文件中设置。服务提供程序的文档应该明确声明哪些属性是被允许的;文件中的其他属性将被忽略。
应用程序资源文件
在部署应用程序时,该应用程序通常将在其类路径中生成若干代码基目录和 JAR。类似地,在部署 applet 时,它将有一个指定 applet 类所处地址的代码基和档案文件。JNDI 查找(使用 ClassLoader.getResources()
)类路径中所有名为 jndi.properties 的应用程序资源文件。此外,如果文件java.home/lib/jndi.properties 存在并且是可读的,则 JNDI 会将其视为一个额外的应用程序资源文件。(java.home 指示由 java.home 系统属性命名的目录。)包含在这些文件中的所有属性都被放置在初始上下文环境中。然后此环境由其他上下文继承。
对于同时出现在多个应用程序资源文件中的每个属性,JNDI 使用最先找到的值,或者在少数有意义的情况下串联所有这些值(细节在下文给出)。例如,如果在三个 jndi.properties 资源文件中存在 "java.naming.factory.object" 属性,则对象工厂列表是所有三个文件中的属性值的串联。使用此方案,每个可部署组件都要负责列出它导出的工厂。JNDI 在搜索工厂类时自动收集和使用所有这些导出列表。
从 Java 2 Platform 开始可使用应用程序资源文件,java.home/lib 中的文件除外,它在较早的 Java 平台上也可以使用。
属性的搜索算法
当 JNDI 构造一个初始上下文时,该上下文的环境是使用传递给构造方法的环境参数中定义的属性、系统属性、applet 参数和应用程序资源文件进行初始化的。有关细节请参见 InitialContext。然后此初始环境由其他上下文实例继承。
如果 JNDI 类库需要确定某一属性的值,它将通过按顺序合并取自以下两个源的值来实现这一点:
- 将在其上执行操作的上下文的环境。
- 将在其上执行操作的上下文的提供程序资源文件 (jndiprovider.properties)。
对于每个同时存在于这两个源中的属性,JNDI 用以下方式确定属性的值。如果该属性是指定 JNDI 工厂列表的标准 JNDI 属性之一(如上文所列),则这些值被串联成一个以冒号分隔的列表。对于其他属性,只使用最先找到的值。
当服务提供程序需要确定某一属性的值时,它通常将直接从环境中获取该值。服务提供程序可以定义将置于其本身提供程序资源文件中的特定于提供程序的属性。在这种情况下,它应该根据上文所述合并这些值。
这样,每个服务提供程序开发人员便可以指定与该服务提供程序一起使用的工厂列表。这可以由应用程序或 applet 的部署方指定的应用程序资源修改,而这些资源又可以由用户修改。
如上即为所查API关于jndi.properties文件的全部说明。总结起来即为:
jndi.properties是jndi初始化文件。通常我们有两种方式来创建一个初始上下文:
1.通过创建一个Properties对象,设置Context.PROVIDER_UR,Context.InitialContextFactroy等等属性,创建InitialContext,例如:
Properties
p.put(Cotnext.PROVIDER_URL, "localhost:1099 ");//主机名和端口号
//InitialContext的创建工厂类(类名是我乱写的)
p.put(Context.InitialContextFactroy, "com.sun.InitialContextFactory ");
InitialContext
2.通过jndi.properties文件创建初始上下文
java.naming.factory.initial=com.sun.NamingContextFactory
java.naming.provider.url=localhost:1099
如果直接创建初始上下文,如下:
InitialContext
InitialContext的构造器会在类路径中找jndi.properties文件,如果找到,通过里面的属性,创建初始上下文。
所以从上面可以看出,两种方式完成的目标是相同的。
因此,对于本地测试(并且JNDI资源没有设置安全属性)可以不添加properties属性,但是如果要访问远程的JNDI资源,就必须用饱含JNDI环境参数Hashtable初始化InitialContext。
必要的环境参数如:
Context.INITIAL_CONTEXT_FACTORY//连接工厂
Context.PROVIDER_URL//访问连接
Context.SECURITY_PRINCIPAL//安全用户
Context.SECURITY_CREDENTIALS//用户密码
有时会出现如NoInitialContextExceptio
但是在单机环境下,可没有JNDI服务在运行,那就手动启动一个JNDI服务。我在JDK 5的rt.jar中一共找到了4种SUN自带的JNDI实现:LDAP,CORBA,RMI,DNS。
这4种JNDI要正常运行还需要底层的相应服务。一般我们没有LDAP或CORBA服务器,也就无法启动这两种JNDI服务,DNS用于查域名的,以后再研究,唯一可以在main()中启动的就是基于RMI的JNDI服务。
现在我们就在main()中启动基于RMI的JNDI服务并且绑一个Date对象到JNDI上:
LocateRegistry.createRegistry(1099);
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL, "rmi://localhost:1099");
InitialContext ctx = new InitialContext();
class RemoteDate extends Date implements Remote {};
ctx.bind("java:comp/env/systemStartTime", new RemoteDate());
ctx.close();
注意,我直接把JNDI的相关参数放入了System.properties中,这样,后面的代码如果要查JNDI,直接new InitialContext()就可以了,否则,你又得写Hashtable env = ...
这段话里提到了system.properties属性,这就是调用system.getProperties的来由,猜想应该是先找到jndi.properties文件,之后通过System.setProperties(),而后通过System.getProperties()来得到。
转自:http://blog.sina.com.cn/s/blog_605f5b4f0100qwra.html
Application Resource Files
An application resource file has the name jndi.properties . It contains a list of key-value pairs presented in the properties file format (see java.util.Properties ). The key is the name of the property (for example, java.naming.factory.object ) and the value is a string in the format defined for that property. Here is an example of an application resource file:Notice that there is no restriction on the type of environment property that you can have in this file.java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory java.naming.provider.url=ldap://localhost:389/o=jnditutorial com.sun.jndi.ldap.netscape.schemaBugs=true
The JNDI automatically reads the application resource files from all components in the applications' classpath and JAVA_HOME/lib/jndi.properties, where JAVA_HOME is the file directory where your Java runtime (JRE) has been installed. The JNDI then makes the properties from these files available to the service providers and other components that need to use them. Therefore, these files should be considered world readable and should not contain sensitive information such as clear-text passwords.
For example, here is a program that lists a context without specifying any environment properties in the InitialContext constructor:
Note: Except for JAVA_HOME/lib/jndi.properties, application resource files are only supported when you use the Java 2 Platform. If you use the JDK 1.1, you can only see JAVA_HOME/lib/jndi.properties.
首先,应用程序会从classpath 里面载入 jndi.properties,如果没有发现,那么会载入JAVA_HOME/lib/jndi.properties。
注意:如果你使用这种方式配置JNDI,那么当找不到装个文件的时候,会抛出异常。
If you run this program with the jndi.properties file shown above, it will list the contents of the o=jnditutorial entry on of the specified LDAP server.InitialContext ctx = new InitialContext(); NamingEnumeration enum = ctx.list("");
The use of application resource files to specify any JNDI environment properties allows the JNDI to be configured with minimal programmatic setup. By using theJAVA_HOME/lib/jndi.properties file, you can also configure the JNDI for all applications and applets using the same Java interpreter.
If you use application resource files, you must remember to grant your applet or application permission to read all of the application resource files.
System Properties
A system property is a key-value pair that the Java runtime defines to describe the user, system environment and Java system. The runtime defines and uses a set of default system properties. Other properties can be made available to a Java program via the -D command line option to the Java interpreter. For example, running the interpreter as follows:adds the property myenviron with value abc to the list of system properties visible to the program Main . The java.lang.System class contains static methods for reading and updating system properties. The ability to read or update any system property is controlled by the security policy of the Java runtime system.#java -Dmyenviron=abc Main
The JNDI reads the following standard JNDI properties from the system properties:
When set as system properties, these environment properties affect all of the application's or applet's (if the applet is allowed permission to read these properties) contexts.java.naming.factory.initial java.naming.factory.object java.naming.factory.state java.naming.factory.control java.naming.factory.url.pkgs java.naming.provider.url java.naming.dns.url
Using the same program used for the application resource file example above, specify the initial context factory to use by specifying the initial context to use on the command line. Here are two examples:
The first one uses LDAP while the second one uses the file system.#java -Djava.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory \ -Djava.naming.provider.url=file:/tmp \ List #java -Djava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory \ -Djava.naming.provider.url=ldap://localhost:389/o=jnditutorial \ List
The use of system properties to specify standard JNDI environment properties allows the JNDI to be configured with minimal programmatic setup. However, they are probably convenient to use only from scripts because items with long property names must be specified on the command line. Also, applets generally do not have permission to read arbitrary system properties and must be explicitly granted permission to do so.
Applet Parameters
You can pass parameters to an applet by using simple key-value pairs. These are specified in the HTML file that references the applet. How you specify these parameters depends on the applet context. For example, if the applet is referenced from an applet tag, you specify the parameters by using the param tag. Here is an example :If the applet is referenced from the Java Plug-in , its parameters are specified by using key-value pairs. Here is an example :<param name=java.naming.factory.initial value=com.sun.jndi.ldap.LdapCtxFactory> <param name=java.naming.provider.url value=ldap://localhost:389/o=jnditutorial>
java.naming.provider.url="ldap://localhost:389/o=jnditutorial" java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
In order for the JNDI to access an applet's parameters, you must set theContext.APPLET("java.naming.applet") environment property. The JNDI reads the following standard JNDI properties from the applet parameters:
java.naming.factory.initial java.naming.factory.object java.naming.factory.state java.naming.factory.control java.naming.factory.url.pkgs java.naming.provider.url java.naming.dns.url
Here is an example that adds a single property (java.naming.applet) to the environment:
// Put this applet instance into environment Hashtable env = new Hashtable(); env.put(Context.APPLET, this); // Pass environment to initial context constructor Context ctx = new InitialContext(env); // List objects NamingEnumeration enum = ctx.list(target); while (enum.hasMore()) { out.println(enum.next()); } ctx.close();
The JNDI then obtains the necessary environment properties from the applet parameters (shown earlier).
This use of applet parameters to specify standard JNDI environment properties allows the JNDI to be configured in the same way that an applet typically performs configuration for other subsystems or components. System properties and application resource files are not good mechanisms for applets to depend upon because applets typically cannot read system properties or arbitrary files (including jndi.properties).
2、内容上下文环境的探讨
java 代码
// Initial environment with various properties
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.FSContextFactory");
env.put(Context.PROVIDER_URL, "file:/");
env.put(Context.OBJECT_FACTORIES, "foo.bar.ObjFactory");
env.put("foo", "bar");
// Call the constructor
Context ctx = new InitialContext(env);
// See what environment properties you have
System.out.println(ctx.getEnvironment());
java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url=ldap://localhost:389/o=jnditutorial
com.sun.jndi.ldap.netscape.schemaBugs=true
com.sun.jndi.ldap.netscape.schemaBugs=true
java.naming.factory.object=foo.bar.ObjFactory:com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
java.naming.factory.initial=com.sun.jndi.fscontext.FSContextFactory
foo=bar
java.naming.provider.url=file:/
java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
3、定制使用SPI
引用自:http://hi.baidu.com/zim_it/blog/item/23bfb31562b70215962b4319
http://gnu.private.webstar.co.uk/docs/other/jndi/beyond/env/source.html
java.naming.factory.object
java.naming.factory.state
java.naming.factory.control
java.naming.factory.url.pkgs
NamingManager.getObjectInstance(Object, Name, Context, Hashtable)
DirectoryManager.getObjectInstance(Object, Name, Context, Hashtable, Attributes)
NamingManager.getStateToBind(Object, Name, Context, Hashtable)
DirectoryManager.getStateToBind(Object, Name, Context, Hashtable, Attributes)
ControlFactory.getControlInstance(Control, Context, Hashtable)
转自:http://blog.sina.com.cn/s/blog_605f5b4f0100r094.html