001package org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.sql.SQLException;
023import java.util.Hashtable;
024import java.util.Map;
025
026import javax.naming.Context;
027import javax.naming.InitialContext;
028import javax.naming.NamingException;
029import javax.sql.DataSource;
030
031import org.apache.commons.jcs3.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
032import org.apache.commons.jcs3.log.Log;
033import org.apache.commons.jcs3.log.LogManager;
034
035/**
036 * A factory that looks up the DataSource from JNDI.  It is also able
037 * to deploy the DataSource based on properties found in the
038 * configuration.
039 *
040 * This factory tries to avoid excessive context lookups to improve speed.
041 * The time between two lookups can be configured. The default is 0 (no cache).
042 *
043 * Borrowed and adapted from Apache DB Torque
044 */
045public class JndiDataSourceFactory implements DataSourceFactory
046{
047    /** The log. */
048    private static final Log log = LogManager.getLog(JndiDataSourceFactory.class);
049
050    /** The name of the factory. */
051    private String name;
052
053    /** The path to get the resource from. */
054    private String path;
055
056    /** The context to get the resource from. */
057    private Context ctx;
058
059    /** A locally cached copy of the DataSource */
060    private DataSource ds;
061
062    /** Time of last actual lookup action */
063    private long lastLookup;
064
065    /** Time between two lookups */
066    private long ttl; // ms
067
068    /**
069     * @return the name of the factory.
070     */
071    @Override
072        public String getName()
073    {
074        return name;
075    }
076
077    /**
078     * @see org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.DataSourceFactory#getDataSource()
079     */
080    @Override
081        public DataSource getDataSource() throws SQLException
082    {
083        final long time = System.currentTimeMillis();
084
085        if (ds == null || time - lastLookup > ttl)
086        {
087            try
088            {
089                synchronized (ctx)
090                {
091                    ds = (DataSource) ctx.lookup(path);
092                }
093                lastLookup = time;
094            }
095            catch (final NamingException e)
096            {
097                throw new SQLException(e);
098            }
099        }
100
101        return ds;
102    }
103
104    /**
105     * @see org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.DataSourceFactory#initialize(JDBCDiskCacheAttributes)
106     */
107    @Override
108        public void initialize(final JDBCDiskCacheAttributes config) throws SQLException
109    {
110        this.name = config.getConnectionPoolName();
111        initJNDI(config);
112    }
113
114    /**
115     * Initializes JNDI.
116     *
117     * @param config where to read the settings from
118     * @throws SQLException if a property set fails
119     */
120    private void initJNDI(final JDBCDiskCacheAttributes config) throws SQLException
121    {
122        log.debug("Starting initJNDI");
123
124        try
125        {
126            this.path = config.getJndiPath();
127            log.debug("JNDI path: {0}", path);
128
129            this.ttl = config.getJndiTTL();
130            log.debug("Time between context lookups: {0}", ttl);
131
132                final Hashtable<String, Object> env = new Hashtable<>();
133            ctx = new InitialContext(env);
134
135            if (log.isTraceEnabled())
136            {
137                log.trace("Created new InitialContext");
138                debugCtx(ctx);
139            }
140        }
141        catch (final NamingException e)
142        {
143            throw new SQLException(e);
144        }
145    }
146
147    /**
148     * Does nothing. We do not want to close a dataSource retrieved from Jndi,
149     * because other applications might use it as well.
150     */
151    @Override
152        public void close()
153    {
154        // do nothing
155    }
156
157    /**
158     *
159     * @param ctx the context
160     * @throws NamingException
161     */
162    private static void debugCtx(final Context ctx) throws NamingException
163    {
164        log.trace("InitialContext -------------------------------");
165        final Map<?, ?> env = ctx.getEnvironment();
166        log.trace("Environment properties: {0}", env.size());
167        env.forEach((key, value) -> log.trace("    {0}: {1}", key, value));
168        log.trace("----------------------------------------------");
169    }
170}