Skip to content
Permalink
Browse files Browse the repository at this point in the history
[GEOT-7115] Streamline JNDI lookups
  • Loading branch information
aaime committed Apr 11, 2022
1 parent 8483f1f commit 4f70fa3
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 107 deletions.
14 changes: 10 additions & 4 deletions docs/user/library/metadata/geotools.rst
Expand Up @@ -55,13 +55,19 @@ In rare cases, such as OSGi plug-in system, adding additional jars to the ``CLAS
JNDI
^^^^

If you are working in a Java Enterprise Edition environment, and would like to configure GeoTools to look up services in a specific context use the following::
To configure GeoTools to look up services in a specific context use the following:

.. code-block:: java
GeoTools.init( applicationContext ); // JNDI configuration
GeoTools.init( context ); // JNDI configuration
GeoTools uses names of the format ``jdbc:EPSG`` internally these are adapted for use with your ``applicationContext`` using the ``GeoTools.fixName`` method::
For JNDI lookup GeooTools uses:

String name = GeoTools.fixName("jdbc.EPSG");
.. code-block:: java
DataSource dataSource = (DataSource) GeoTools.jndiLookup(name);
The ``jndiLookup(String)`` is to safe lookups by default. The default use of ``GeoTools.DEFAULT_JNDI_VALIDATOR`` ensures only no-schema and java schema lookups are allowed. To relax this policy you may supply your own approach using ``GeoTools.setJNDINameValidator(Predicate<String>)``.

XML
^^^
Expand Down
22 changes: 22 additions & 0 deletions docs/user/welcome/upgrade.rst
Expand Up @@ -70,6 +70,28 @@ Fixing this required changes to multiple classes:
GeoTools 25.x
-------------

GeoTools
^^^^^^^^

In GeoTools 25.7 ``GeoTools.getInitialContext().look(name)`` and related methods have been deprecated, with ``GeoTools.jndiLookup(name)``. We have also taken an opportunity to remove ``GeoTools.fixName( context, name )``

The use of ``GeoTools.jndiLookup(name)`` is subject to validation with the default ``GeoTools.DEFAULT_JNDI_VALIDATOR`` validator used limit name lookup.

BEFORE

.. code-block:: java
context = GeoTools.getInitialContext();
String fixedName = GeoTools.fixName( context, name );
return (DataSource) context.lookup(fixedName);
AFTER

.. code-block:: java
return (DataSource) GeoTools.jndiLookup(name);
More variable arguments support in core classes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
Expand Up @@ -64,7 +64,7 @@ public boolean canProcess(Map<String, ?> params) {
public DataSource createNewDataSource(Map<String, ?> params) throws IOException {
String refName = (String) JNDI_REFNAME.lookUp(params);
try {
return (DataSource) GeoTools.getInitialContext().lookup(refName);
return (DataSource) GeoTools.jndiLookup(refName);
} catch (Exception e) {
throw new DataSourceException("Could not find the specified data source in JNDI", e);
}
Expand All @@ -83,11 +83,6 @@ public Param[] getParametersInfo() {
/** Make sure a JNDI context is available */
@Override
public boolean isAvailable() {
try {
GeoTools.getInitialContext();
return true;
} catch (Exception e) {
return false;
}
return GeoTools.isJNDIAvailable();
}
}
Expand Up @@ -21,7 +21,6 @@
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.geotools.data.DataStore;
Expand Down Expand Up @@ -90,23 +89,16 @@ protected DataSource createDataSource(Map<String, ?> params, SQLDialect dialect)
String jndiName = (String) JNDI_REFNAME.lookUp(params);
if (jndiName == null) throw new IOException("Missing " + JNDI_REFNAME.description);

Context ctx = null;
DataSource ds = null;

try {
ctx = GeoTools.getInitialContext();
} catch (NamingException e) {
throw new RuntimeException(e);
}

try {
ds = (DataSource) ctx.lookup(jndiName);
ds = (DataSource) GeoTools.jndiLookup(jndiName);
} catch (NamingException e1) {
// check if the user did not specify "java:comp/env"
// and this code is running in a J2EE environment
try {
if (jndiName.startsWith(J2EERootContext) == false) {
ds = (DataSource) ctx.lookup(J2EERootContext + jndiName);
ds = (DataSource) GeoTools.jndiLookup(J2EERootContext + jndiName);
// success --> issue a waring
Logger.getLogger(this.getClass().getName())
.log(
Expand Down Expand Up @@ -135,12 +127,7 @@ protected DataSource createDataSource(Map<String, ?> params, SQLDialect dialect)
*/
@Override
public boolean isAvailable() {
try {
GeoTools.getInitialContext();
return true;
} catch (NamingException e) {
return false;
}
return GeoTools.isJNDIAvailable();
}

/** Override to omit all those parameters which define the creation of the connection. */
Expand Down
5 changes: 5 additions & 0 deletions modules/library/metadata/pom.xml
Expand Up @@ -134,6 +134,11 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Expand Up @@ -25,6 +25,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
Expand All @@ -38,6 +40,7 @@
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Predicate;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -87,6 +90,7 @@
* @author Martin Desruisseaux
*/
public final class GeoTools {

/** Properties about this geotools build */
private static final Properties PROPS;

Expand Down Expand Up @@ -301,6 +305,29 @@ private static Properties loadProperites(String resource) {
BINDINGS = Collections.unmodifiableMap(bindings);
}

/**
* Default JNDI name validator, allows lookups only on names without a scheme, or using the
* <code>java</code> scheme.
*/
public static final Predicate<String> DEFAULT_JNDI_VALIDATOR =
name -> {
Logger LOGGER = Logging.getLogger(GeoTools.class);
try {
URI uri = new URI(name);
boolean result = uri.getScheme() == null || uri.getScheme().equals("java");
if (!result)
LOGGER.warning(
"JNDI lookup allowed only on java scheme, or no scheme. Found instead: "
+ name);
return result;
} catch (URISyntaxException e) {
LOGGER.log(Level.WARNING, "Invalid JNDI name provided", e);
return false;
}
};

private static Predicate<String> jndiValidator = DEFAULT_JNDI_VALIDATOR;

/**
* Binds the specified {@linkplain System#getProperty(String) system property} to the specified
* key. Only one key can be bound to a given system property. However the same key can be binded
Expand Down Expand Up @@ -735,13 +762,13 @@ public static void init() {
/**
* Provides GeoTools with the JNDI context for resource lookup.
*
* @param applicationContext The initial context to use.
* @see #getInitialContext
* @param initialContext The initial context to use for JNDI lookup
* @see #jndiLookup(String)
* @since 2.4
*/
public static void init(final InitialContext applicationContext) {
public static void init(final InitialContext initialContext) {
synchronized (GeoTools.class) {
context = applicationContext;
context = initialContext;
}
fireConfigurationChanged();
}
Expand Down Expand Up @@ -934,30 +961,24 @@ static <T, D extends T> T instantiate(String className, Class<T> type, D default
}
return defaultValue;
}

/**
* Returns the default initial context.
*
* @param hints An optional set of hints, or {@code null} if none.
* @return The initial context (never {@code null}).
* @throws NamingException if the initial context can't be created.
* @see #init(InitialContext)
* @since 2.4
* @deprecated hints isn't really used. Use the function without hints
* @deprecated Please use {@link #jndiLookup(String)} instead, or provide an {@link
* InitialContext} to the {@link #init(InitialContext)} method and use it directly.
*/
@Deprecated
public static synchronized InitialContext getInitialContext(final Hints hints)
throws NamingException {

return getInitialContext();
public static synchronized InitialContext getInitialContext() throws NamingException {
Logging.getLogger(GeoTools.class)
.severe(
"Please don't use GeoTools.getInitialContext(), perform lookups using GeoTools.jndiLookup(s) instead.");
return getJNDIContext();
}

/**
* Returns the default initial context.
*
* @return The initial context (never {@code null}).
* @throws NamingException if the initial context can't be created.
*/
public static synchronized InitialContext getInitialContext() throws NamingException {
private static synchronized InitialContext getJNDIContext() throws NamingException {
if (context == null) {
try {
context = new InitialContext();
Expand All @@ -968,6 +989,45 @@ public static synchronized InitialContext getInitialContext() throws NamingExcep
return context;
}

/**
* Checks if JNDI is available, either because it was initialized, or because it was possible to
* create one.
*/
public static boolean isJNDIAvailable() {
try {
// see if we have a context, or can create one
return getJNDIContext() != null;
} catch (NamingException e) {
return false;
}
}

/**
* Sets up a function that will be called to validate the JNDI lookups. If not set, the
* DEFAULT_JNDI_VALIDATOR is used. The function may want to log the reason why a given name was
* denied lookup.
*
* @param validator A function returning true if the lookups are meant to be performed, false
* otherwise.
*/
public static void setJNDINameValidator(Predicate<String> validator) {
jndiValidator = validator;
}

/**
* Looks up an object from the JNDI {@link InitialContext}. By default, it only allows lookups
* with no scheme, or inside the <code>java</code> scheme. One can set up a custom name
* validation routine using
*
* @param name
* @return
* @throws NamingException
*/
public static Object jndiLookup(String name) throws NamingException {
if (!jndiValidator.test(name)) return null;
return getJNDIContext().lookup(name);
}

private static NamingException handleException(Exception e) {
final Logger LOGGER = Logging.getLogger(GeoTools.class);
final String propFileName = "jndi.properties";
Expand Down Expand Up @@ -1064,7 +1124,11 @@ public static synchronized void clearInitialContext() throws NamingException {
* @return Name fixed up with {@link Context#composeName(String,String)}, or {@code null} if the
* given name was null.
* @since 2.4
* @deprecated With no replacement, GeoTools now uses JNDI lookups as instructed in {@link
* #jndiLookup(String)}, but does not put any object in the contex, the downstream
* application should do it if necessary instead.
*/
@Deprecated
public static String fixName(final String name) {
return fixName(null, name, null);
}
Expand All @@ -1079,7 +1143,11 @@ public static String fixName(final String name) {
* @return Name fixed up with {@link Context#composeName(String,String)}, or {@code null} if the
* given name was null.
* @since 2.4
* @deprecated With no replacement, GeoTools now uses JNDI lookups as instructed in {@link *
* #jndiLookup(String)}, but does not put any object in the contex, the downstream *
* application should do it if necessary instead.
*/
@Deprecated
public static String fixName(final Context context, final String name) {
return (context != null) ? fixName(context, name, null) : name;
}
Expand All @@ -1088,7 +1156,12 @@ public static String fixName(final Context context, final String name) {
* Implementation of {@code fixName} method. If the context is {@code null}, then the
* {@linkplain #getInitialContext GeoTools initial context} will be fetch only when first
* needed.
*
* @deprecated With no replacement, GeoTools now uses JNDI lookups as instructed in {@link *
* #jndiLookup(String)}, but does not put any object in the contex, the downstream *
* application should do it if necessary instead.
*/
@Deprecated
private static String fixName(Context context, final String name, final Hints hints) {
String fixed = null;
if (name != null) {
Expand Down
Expand Up @@ -68,6 +68,7 @@ public void ensureHintsLoaded() {
* @param creator {@code true} if the registry should be an instance of {@link
* org.geotools.util.factory.FactoryCreator}.
*/
@SuppressWarnings("PMD.UnusedPrivateMethod") // PMD getting confused here?
private FactoryRegistry getRegistry(
final boolean creator,
final Factory factory1,
Expand Down

0 comments on commit 4f70fa3

Please sign in to comment.