/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2016, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.internal.io; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.io.IOException; import java.util.logging.Level; import javax.sql.DataSource; import javax.naming.Binding; import javax.naming.Context; import javax.naming.Name; import javax.naming.NameParser; import javax.naming.NameClassPair; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; import javax.naming.InitialContext; import javax.naming.NameAlreadyBoundException; import javax.naming.event.EventContext; import javax.naming.event.NamingEvent; import javax.naming.event.NamingListener; import javax.naming.event.ObjectChangeListener; import javax.naming.OperationNotSupportedException; import org.apache.sis.internal.metadata.sql.Initializer; import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.factory.MultiAuthoritiesFactory; import org.apache.sis.util.logging.Logging; import org.geotoolkit.internal.sql.DefaultDataSource; import org.geotoolkit.internal.sql.AuthenticatedDataSource; import org.opengis.util.FactoryException; /** * Tiny JNDI context, used only for specifying an EPSG data source to Apache SIS. * * @author Martin Desruisseaux (Geomatys) * @module */ public final class JNDI implements EventContext, InitialContextFactory { /** * Invoked from {@link org.geotoolkit.lang.Setup} for setting a pseudo-JNDI environment. */ public static void install() { // define at least the derby folder if not set boolean reload = false; if (System.getProperty("derby.system.home") == null) { final Path path = Installation.SIS.directory(true); if (!Files.exists(path)) { try { Files.createDirectories(path); } catch (IOException ex) { throw new IllegalStateException(ex); } } if (Files.isDirectory(path)) { System.setProperty("derby.system.home", path.toAbsolutePath().toString()); reload = true; } } if (!Initializer.hasJNDI()) { System.setProperty(INITIAL_CONTEXT_FACTORY, JNDI.class.getName()); reload = true; } if (reload) { try { ((MultiAuthoritiesFactory) CRS.getAuthorityFactory(null)).reload(); } catch (FactoryException ex) { throw new IllegalStateException(ex); // Should never happen. } } } /** * Invoked from {@link org.geotoolkit.lang.Setup} for removing our pseudo-JNDI environment. */ public static void uninstall() { if (JNDI.class.getName().equals(System.getProperty(INITIAL_CONTEXT_FACTORY))) { System.clearProperty(INITIAL_CONTEXT_FACTORY); } } /** * The last {@code JNDI} instance created. Considered as the unique JNDI instance. */ private static volatile JNDI instance; /** * Data source for the EPSG database, or {@code null} if none. */ private static DataSource EPSG; /** * Explicitly set datasource called by {@link #setEPSG}. * */ private static DataSource overrideEPSG; /** * Listeners to notify when {@link #EPSG} changed. * Must be a static list, because JNDI creates new {@code Context} instances on various method calls. */ private static final List<ObjectChangeListener> listeners = new ArrayList<>(); /** * Sets the data source for the EPSG database. * * @param source Data source for the EPSG database, or {@code null} if none. */ public static synchronized void setEPSG(final DataSource source) throws NamingException { if (overrideEPSG == null && source == null) { // Datasource has not been overriden. return; } overrideEPSG = source; EPSG = source; if (Initializer.hasJNDI()) { final Context env = (Context) InitialContext.doLookup("java:comp/env"); if (!(env instanceof JNDI)) { try { env.bind(Initializer.JNDI, source); } catch (NameAlreadyBoundException | OperationNotSupportedException ex) { Logging.getLogger("org.geotoolkit.referencing").log(Level.CONFIG, ex.getMessage(), ex); } } } final JNDI instance = JNDI.instance; if (instance != null) { final ObjectChangeListener[] ls = listeners.toArray(new ObjectChangeListener[listeners.size()]); final NamingEvent event = new NamingEvent(instance, NamingEvent.OBJECT_CHANGED, null, null, null); for (final ObjectChangeListener listener : ls) { listener.objectChanged(event); } } } /** * Returns the EPSG data source. */ public static synchronized DataSource getEPSG() throws IOException { if (EPSG == null) { final Properties properties = Installation.EPSG.getDataSource(); if (properties != null) { final String url = properties.getProperty("URL"); if (url != null) { EPSG = new DefaultDataSource(url); final String user = properties.getProperty("user"); if (user != null) { EPSG = new AuthenticatedDataSource(EPSG, user, properties.getProperty("password"), true); } } } } return EPSG; } /** * Invoked by JNDI through reflection for creating the {@link InitialContextFactory}. */ public JNDI() { instance = this; } /** * Invoked by JNDI for creating the {@link Context}. * * @param environment ignored. * @return The context. */ @Override public Context getInitialContext(Hashtable<?,?> environment) { return this; } /** * Returns the object for the given name. * * @param name The object name. * @return Value associated to the given name. * @throws NamingException if the value can not be obtained. */ @Override public Object lookup(final String name) throws NamingException { switch (name) { case "java:comp/env": return this; case Initializer.JNDI: { final DataSource ds; try { ds = getEPSG(); } catch (IOException e) { throw (NamingException) new NamingException(e.getMessage()).initCause(e); } if (ds != null) { return ds; } throw new NameNotFoundException("EPSG DataSource not set."); } default: throw new NameNotFoundException(name); } } @Override public void bind(String name, Object obj) throws NamingException { throw new NamingException("Not supported yet."); } @Override public void rebind(String name, Object obj) throws NamingException { throw new NamingException("Not supported yet."); } @Override public void unbind(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public void rename(String oldName, String newName) throws NamingException { throw new NamingException("Not supported yet."); } @Override public NamingEnumeration<NameClassPair> list(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public NamingEnumeration<Binding> listBindings(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public void destroySubcontext(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public Context createSubcontext(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public Object lookupLink(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public NameParser getNameParser(String name) throws NamingException { throw new NamingException("Not supported yet."); } @Override public String composeName(String name, String prefix) throws NamingException { throw new NamingException("Not supported yet."); } @Override public Object addToEnvironment(String propName, Object propVal) throws NamingException { throw new NamingException("Not supported yet."); } @Override public Object removeFromEnvironment(String propName) throws NamingException { throw new NamingException("Not supported yet."); } @Override public Hashtable<?, ?> getEnvironment() throws NamingException { throw new NamingException("Not supported yet."); } @Override public String getNameInNamespace() throws NamingException { throw new NamingException("Not supported yet."); } @Override public void addNamingListener(String target, int scope, NamingListener listener) { if (listener instanceof ObjectChangeListener && Initializer.JNDI.equals(target)) { synchronized (JNDI.class) { listeners.add((ObjectChangeListener) listener); } } } @Override public void removeNamingListener(NamingListener listener) { synchronized (JNDI.class) { listeners.remove(listener); } } @Override public boolean targetMustExist() { return false; } @Override public void close() { } /** * Delegates to the methods working on {@link String}. * * @param name The name to convert to string. * @throws NamingException if the string-based method failed. */ @Override public void bind (Name name, Object obj) throws NamingException { bind (name.toString(), obj);} @Override public void unbind (Name name) throws NamingException { unbind (name.toString());} @Override public void rebind (Name name, Object obj) throws NamingException { rebind (name.toString(), obj);} @Override public void rename (Name name, Name newName) throws NamingException { rename (name.toString(), newName.toString());} @Override public Object lookup (Name name) throws NamingException {return lookup (name.toString());} @Override public Object lookupLink (Name name) throws NamingException {return lookupLink (name.toString());} @Override public NamingEnumeration<NameClassPair> list (Name name) throws NamingException {return list (name.toString());} @Override public NamingEnumeration<Binding> listBindings (Name name) throws NamingException {return listBindings (name.toString());} @Override public NameParser getNameParser (Name name) throws NamingException {return getNameParser (name.toString());} @Override public Context createSubcontext (Name name) throws NamingException {return createSubcontext (name.toString());} @Override public void destroySubcontext(Name name) throws NamingException { destroySubcontext(name.toString());} @Override public Name composeName (Name name, Name prefix) throws NamingException { return getNameParser(name).parse(composeName(name.toString(), prefix.toString())); } @Override public void addNamingListener(Name target, int scope, NamingListener l) throws NamingException { addNamingListener(target.toString(), scope, l); } }