# Simple-JNDI **Repository Path**: kevinlights/Simple-JNDI ## Basic Information - **Project Name**: Simple-JNDI - **Description**: Access property files via JNDI lookups. Get a DataSource from JNDI. - **Primary Language**: Unknown - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-12 - **Last Updated**: 2024-06-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Simple-JNDI Simple-JNDI is intended to solve two problems. The first is to test or use classes that depend on JNDI environment objects (most known a DataSource) provided by a Java EE container outside of such a container. So it is recommended by Spring to replace its own deprecated JNDI Mock implementation (See [Introduction to Spring Testing > Unit Testing > Mock Objects > JNDI](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/testing.html#mock-objects-jndi)). The second problem Simple-JNDI is intended to solve is to access application configurations easily from anywhere in your application. If your only intention is to test or use classes that depend on Tomcat's JNDI environment outside of Tomcat or you are only in need of a JNDI based DataSource give [TomcatJNDI](https://github.com/h-thurow/TomcatJNDI) (not to be confused with Simple-JNDI) a try. Simple-JNDI's JNDI implementation is entirely memory based. No server instance is started. A java.util.Properties object, the structure of a root directory or a list of .property files serves as a model for the contexts structure. The contexts get populated with objects defined programmatically or declared in .properties, .xml or .ini files.
<dependency> <groupId>com.github.h-thurow</groupId> <artifactId>simple-jndi</artifactId> <version>0.23.0</version> </dependency>or download from here.
This is where all the work goes in a Simple-JNDI installation. Firstly you need a jndi.properties file, which somehow needs to go into your classpath. This jndi.properties needs two mandatory values:
java.naming.factory.initial = org.osjava.sj.SimpleContextFactory
This property, java.naming.factory.initial
, is a part of the JNDI specification.
The second required parameter, when not creating the context objects programmatically, is org.osjava.sj.root
, where you store the files, that define the context objects you want Simple-JNDI to create. The following code block details a few examples with explanatory comments.
# absolute directory org.osjava.sj.root = /home/hen/gj/simple-jndi/config/
# relative directory org.osjava.sj.root = config/
Not required, but highly recommended is setting org.osjava.sj.jndi.shared = true
too.
NEW in 0.13.0: Specify a list of files and/or directories. Separate them by the platform specific path separator.
org.osjava.sj.root = file1.cfg:directory1/file.properties:directory2
From 0.17.2 on you should also set org.osjava.sj.pathSeparator
to the separator used in org.osjava.sj.root
to ensure platform independency of your jndi.properties file. See also Load property files with any extension from any location.
NEW in 0.18.0: You can load files or directories from JARs on classpath
org.osjava.sj.root = jarMarkerClass=any.class.in.Jar,root=/root/in/jar
The jarMarkerClass is the Name of a class unique over all JARs on classpath to identify the JAR containing the root directory. The JAR must be found in the file system. Very probably JARs encapsulated in WARs or uber jars will not work.
NEW in 0.18.2: You can declare all these parameters as system properties and dispense with jndi.properties file. See Enhancement request: make org.osjava.sj.root not mandatory in jndi.properties.
Simple-JNDI stores values in multiple .properties, .xml or .ini files. The files are located under a root directory as specified with the org.osjava.sj.root
property.
Directory names and file names become part of the lookup key. Each delimited tree-node becomes a JNDI Context, while the leaves are implementations. The only exceptions are pseudo sub-values, which you will see with DataSources.
The easiest way to understand is to consider an example. Imagine a file-structure looking like config/application1/users.properties
in which the file looks like:
admin = fred quantity = 5 enabled = true
Now you can access these properties from anywhere in your application via JNDI:
InitialContext ctx = new InitialContext(); String admin = (String) ctx.lookup("application1.users.admin"); String quantity = (String) ctx.lookup("application1.users.quantity"); String enabled = (String) ctx.lookup("application1.users.enabled");
The example assumes that you set org.osjava.sj.root = config
.
There are some reserved words you can not use as property names: type, converter, javaxNamingSpiObjectFactory
.
See also
System property substitution in resource files (New in 0.19.0)
When only some objects are needed, e. g. just a DataSource, it might be more convenient, to do it programmatically. See Programmatically create your contexts and context objects (no .properties, .xml or .ini files needed)
In the above example it would be favourable to lookup "quantity" as Integer and "enabled" as Boolean. To achieve this you can type your properties:
quantity = 5 quantity.type = java.lang.Integer enabled = true enabled.type = java.lang.Boolean
Thereafter you can call typed properties:
Integer quantity = (Integer) ctxt.lookup("application1.users.quantity"); Boolean enabled = (Boolean) ctxt.lookup("application1.users.enabled");
The following types are supported: Byte, Short, Integer, Long, Float, Double, Character.
Also supported are Maps (0.16.0):
city.type = java.util.Map city.citizens = 3.520.031 city.name = Berlin
Now you can lookup a Map:
Map city = (Map) ctx.lookup("city"); assertEquals("3.520.031", city.get("citizens"));
For further map examples see here.
Note that you have to write quantity/type = java.lang.Integer
and enabled/type = java.lang.Boolean
when setting org.osjava.sj.delimiter = /
unless you follow this description. And as you might anticipate already: type
is a reserved word with Simple-JNDI.
See also
A more elegant way to lookup typed properties (New in 0.14.0)
Load self defined types as Beans or how to hook into the object creation process (New in 0.15.0)
Instantiate beans and set their properties (New in 0.17.0)
Usage of 3rd party SPI ObjectFactory implementations (New in 0.21.0)
So far we used "." as context separator in lookup pathes like in
ctxt.lookup("application1.users.enabled");
But more usual in JNDI world are lookup pathes like
ctxt.lookup("application1/users/enabled");
This is what org.osjava.sj.delimiter
is for. If not specified, then a '.' is chosen. To use "/" as separator in lookup pathes set
org.osjava.sj.delimiter = /
Note that you can not mix up different separators in property names and lookup pathes. When setting org.osjava.sj.delimiter = /
and using namespaced property names you can not declare a.b.c = 123
. You have to declare a/b/c = 123
. See also ENC problem.
See also Use slash separated lookup pathes with dot separated property names (New in 0.14.0)
The most popular object to get from JNDI is an object of type javax.sql.DataSource, allowing the developer to obtain JDBC connections to databases. Simple-JNDI supports this out of the box. See
DataSource Configuration DBCP 2 and Commons Pool 2 (New in 0.15.0)
DataSource Configuration HikariCP (New in 0.15.0)
DataSource Configuration (commons dbcp 1)
Usage with Spring - Inject a DataSource into beans
See also TomcatJNDI: Only interested in a DataSource?
Setting org.osjava.sj.jndi.shared = true
will put the in-memory JNDI implementation into a mode whereby all InitialContexts share the same memory. By default this is not set, so every new InitialContext() call will provide an independent InitialContext that does not share its memory with the other contexts. This could be not what you want when using a DataSource or a connection pool because everytime you call new InitialContext() in your application a new DataSource or a new connection pool is created. Also when binding an object to a specific context by calling Context.bind() this object will be not visible in the context provided by a subsequent "new InitialContext()" call.
Set the org.osjava.sj.space
property. Whatever the property is set to will be automatically prepended to every value loaded into the system. Thus org.osjava.sj.space = java:comp/env
simulates the JNDI environment of Tomcat. The org.osjava.sj.space
property is not subject to delimiter parsing, so even when org.osjava.sj.delimiter
is set to ".", you have to lookup "java:comp/env", not "java:comp.env". See also ENC problem.
Another way to achieve a similar result is putting a default.properties directly under your root. In this file declare all your context objects that should reside under "java:comp/env" by prefixing all properties with "java:comp/env", e. g. java:comp/env/my/size = 186
. This way you can set some context objects in "java:comp/env" and other objects in a different name space.
You could also put a file named "java:comp.properties" in your root directory or name a directory under your root directory "java:comp". But Windows does not like having a ":" in a filename, so to deal with the ":" you can use the org.osjava.sj.colon.replace
property. If, for example, you choose to replace a ":" with "--" (ie org.osjava.sj.colon.replace = --
), then you will need a file named java--comp.properties
, or a directory named java--comp
containing a file "env.properties".
org.osjava.sj.jndi.ignoreClose = trueReally closing those contexts is a little bit tricky now:
Hashtable env = new InitialContext().getEnvironment(); env.remove("org.osjava.sj.jndi.ignoreClose"); env.put("java.naming.factory.initial", "org.osjava.sj.SimpleJndiContextFactory"); new InitialContext(env).close();
Any object manually bound to a context after SimpleJNDI's initialization will be visible in any thread looking up the object. But to guarantee the visibility of modifications to an object in all threads after it was bound you have to use the set-after-write trick:
InitialContext ic = new InitialContext(); List<City> cities = (List<City>) ic.lookup("Cities"); cities.add(new City("Berlin")); ic.rebind("Cities", cities); // rebind guarantees visibility in all threads
This project is based on old https://github.com/hen/osjava/tree/master/simple-jndi .