001package org.apache.commons.jcs.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.Iterator;
025import java.util.Map;
026
027import javax.naming.Context;
028import javax.naming.InitialContext;
029import javax.naming.NamingException;
030import javax.sql.DataSource;
031
032import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035
036/**
037 * A factory that looks up the DataSource from JNDI.  It is also able
038 * to deploy the DataSource based on properties found in the
039 * configuration.
040 *
041 * This factory tries to avoid excessive context lookups to improve speed.
042 * The time between two lookups can be configured. The default is 0 (no cache).
043 *
044 * Borrowed and adapted from Apache DB Torque
045 *
046 * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
047 * @author <a href="mailto:thomas@vandahl.org">Thomas Vandahl</a>
048 */
049public class JndiDataSourceFactory implements DataSourceFactory
050{
051    /** The log. */
052    private static Log log = LogFactory.getLog(JndiDataSourceFactory.class);
053
054    /** The name of the factory. */
055    private String name;
056
057    /** The path to get the resource from. */
058    private String path;
059
060    /** The context to get the resource from. */
061    private Context ctx;
062
063    /** A locally cached copy of the DataSource */
064    private DataSource ds = null;
065
066    /** Time of last actual lookup action */
067    private long lastLookup = 0;
068
069    /** Time between two lookups */
070    private long ttl = 0; // ms
071
072    /**
073     * @return the name of the factory.
074     */
075    @Override
076        public String getName()
077    {
078        return name;
079    }
080
081    /**
082     * @see org.apache.commons.jcs.auxiliary.disk.jdbc.dsfactory.DataSourceFactory#getDataSource()
083     */
084    @Override
085        public DataSource getDataSource() throws SQLException
086    {
087        long time = System.currentTimeMillis();
088
089        if (ds == null || time - lastLookup > ttl)
090        {
091            try
092            {
093                synchronized (ctx)
094                {
095                    ds = ((DataSource) ctx.lookup(path));
096                }
097                lastLookup = time;
098            }
099            catch (NamingException e)
100            {
101                throw new SQLException(e);
102            }
103        }
104
105        return ds;
106    }
107
108    /**
109     * @see org.apache.commons.jcs.auxiliary.disk.jdbc.dsfactory.DataSourceFactory#initialize(JDBCDiskCacheAttributes)
110     */
111    @Override
112        public void initialize(JDBCDiskCacheAttributes config) throws SQLException
113    {
114        this.name = config.getConnectionPoolName();
115        initJNDI(config);
116    }
117
118    /**
119     * Initializes JNDI.
120     *
121     * @param config where to read the settings from
122     * @throws SQLException if a property set fails
123     */
124    private void initJNDI(JDBCDiskCacheAttributes config) throws SQLException
125    {
126        log.debug("Starting initJNDI");
127
128        try
129        {
130            this.path = config.getJndiPath();
131            if (log.isDebugEnabled())
132            {
133                log.debug("JNDI path: " + path);
134            }
135
136            this.ttl = config.getJndiTTL();
137            if (log.isDebugEnabled())
138            {
139                log.debug("Time between context lookups: " + ttl);
140            }
141
142                Hashtable<String, Object> env = new Hashtable<String, Object>();
143            ctx = new InitialContext(env);
144
145            if (log.isDebugEnabled())
146            {
147                log.debug("Created new InitialContext");
148                debugCtx(ctx);
149            }
150        }
151        catch (NamingException e)
152        {
153            throw new SQLException(e);
154        }
155    }
156
157    /**
158     * Does nothing. We do not want to close a dataSource retrieved from Jndi,
159     * because other applications might use it as well.
160     */
161    @Override
162        public void close()
163    {
164        // do nothing
165    }
166
167    /**
168     *
169     * @param ctx the context
170     * @throws NamingException
171     */
172    private void debugCtx(Context ctx) throws NamingException
173    {
174        log.debug("InitialContext -------------------------------");
175        Map<?, ?> env = ctx.getEnvironment();
176        Iterator<?> qw = env.entrySet().iterator();
177        log.debug("Environment properties:" + env.size());
178        while (qw.hasNext())
179        {
180            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) qw.next();
181            log.debug("    " + entry.getKey() + ": " + entry.getValue());
182        }
183        log.debug("----------------------------------------------");
184    }
185}