001    /*
002     * Copyright 2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.scaffold.sql;
018    
019    
020    import java.sql.Connection;
021    import java.sql.SQLException;
022    
023    import javax.naming.Context;
024    import javax.naming.InitialContext;
025    import javax.naming.NamingException;
026    import javax.sql.DataSource;
027    
028    
029    /**
030     * Base class for SQL connection adaptors. May be used as-is or subclassed.
031     * <p>
032     * This object does not provide any type of connection pooling.
033     * It simply provides a standard means for obtaining a connection
034     * whatever connection pool your application needs.
035     * Scaffold classes in the SQL package call this object internally.
036     * <p>
037     * To use as-is, a DataSource compliant connection pool must be
038     * available as "DATA_SOURCE" via JNDI. A subclass can
039     * locate the pool under any name by any means necessary.
040     *<p>
041     * A <code>ConnectionAdaptor</code> can be instantiated once at
042     * start-up, either from main() or via a servlet, like the
043     * Scaffold ConnectionServlet.
044     * Objects needing a connection can then call the static
045     * method
046     * <code>ConnectionAdaptor.getPool().getConnection()</code>.
047     * <p>
048     * Most implementations will expect callers to use and
049     * close the connections promptly, since they would
050     * usually be part of a pool.
051     * <p>
052     * Subclasses can provide their own resource name or
053     * getConnection() method, and a new constructor to
054     * set the protected pool field to their own
055     * instance. See <code>PoolmanAdaptor</code> for an example.
056     *
057     * @see org.apache.commons.scaffold.http.ConnectionServlet
058     * @see org.apache.commons.scaffold.sql.PoolmanAdaptor
059     * @author Ted Husted
060     * @version $Revision: 155464 $ $Date: 2005-02-26 13:26:54 +0000 (Sat, 26 Feb 2005) $
061     */
062    public class ConnectionAdaptor {
063    
064    
065    // ------------------------------------------------------------ message
066    
067        /**
068         * A message for an exception if pool returns null.
069         */
070        private String message = "Connection pool " +
071            "not available. Check your configuration.";
072    
073    
074        /**
075         * Return a message for an exception if pool returns null.
076         *
077         * @return Message for an exception if pool returns null
078         */
079        protected String getMessage() {
080            return this.message;
081        }
082    
083    
084        /**
085         * Set a message for an exception if pool returns null.
086         * Adaptor subclasses can call this from their constructor.
087         *
088         * @param Message The message for an exception if pool returns null
089         */
090        protected void setMessage(String message) {
091            this.message = message;
092        }
093    
094    
095    // ---------------------------------------------------------------- key
096    
097    
098        /**
099         * The default DataSource name.
100         */
101        public static final String DATA_SOURCE_KEY = "DATA_SOURCE";
102    
103    
104        /**
105         * The default JNDI context for applications.
106         */
107        public static final String JNDI_CONTEXT_KEY = "java:comp/env";
108    
109    
110        /**
111         * The attribute under which our DataSource is stored
112         * [DATA_SOURCE_KEY].
113         */
114        private String key = DATA_SOURCE_KEY;
115    
116    
117        /**
118         * Return the attribute under which our datasource is stored.
119         * Subclasses may override this method to provide a different key,
120         * or set a new key at construction.
121         */
122        public String getKey() {
123            return this.key;
124        }
125    
126    
127        /**
128         * The attribute under which our datasource is stored.
129         * Adaptor subclasses can call this method from a constructor
130         * to change the default resource name.
131         */
132        public void setKey(String key) {
133            this.key = key;
134        }
135    
136    
137    // --------------------------------------------------------- datasource
138    
139       /**
140        * Field to store singleton reference to default datasource.
141        *
142        * TODO: Add Map to store reference to other datasoruces.
143        */
144        protected static DataSource datasource;
145    
146    
147       /**
148        * Return datasource using JNDI lookup. Subclasses may
149        * override this method to provide other means of obtaining
150        * a datasource reference.
151        *
152        * @param key The attribute name for the resource.
153        * If null is passed, null is returned.
154        * @return null or the datasource object related to "key"
155        *
156        * TODO: Add cache to store references to the datasources by key.
157        */
158        protected DataSource getDataSource(String key)
159                throws SQLException {
160    
161            if (null==key) return null;
162    
163            try {
164    
165                Context env = (Context) new
166                    InitialContext().lookup(JNDI_CONTEXT_KEY);
167    
168                return (DataSource) env.lookup(key);
169    
170            }
171    
172            catch (NamingException ne) {
173                // Recast naming exception,
174                // since that is what other routines only except
175                // and because another implementation may not generate this.
176                // Should only be an issue during initial setup
177    
178                throw new SQLException(ne.getMessage());
179            }
180    
181        } // end getDataSource
182    
183    
184    // --------------------------------------------------------- connection
185    
186        /**
187         * Returns a JDBC connection from the default resource.
188         * Calls <code>getDatasource</code> to return the resource
189         * associated with the default attribute name [getKey()].
190         * <p>
191         * The default datasource object is cached and reused in subsequent
192         * calls.
193         *
194         * @return JDBC connection from resource layer.
195         * @exception SQLException on SQL or other errors. May wrap other
196         * exceptions depending on implementation. Will not return null.
197         */
198        public Connection getConnection() throws SQLException {
199    
200            if (null==datasource) {
201    
202                datasource = getDataSource(getKey());
203    
204                if (null==datasource)
205                    throw new SQLException(getMessage());
206            }
207    
208            return (datasource.getConnection());
209    
210        } // end getConnection
211    
212    
213        /**
214         * Returns a JDBC connection from a connection pool or other
215         * named resource, to be used and closed promptly.
216         * <p>
217         * Default implementation uses JNDI to lookup the resource named by
218         * getResource() ["DATASOURCE"].
219         * Will not return null.
220         *
221         * @return JDBC connection from resource layer.
222         * @exception SQLException on SQL or other errors. May wrap
223         * other exceptions depending on implementation.
224         * @param resource An attribute name for the resource to use for
225         * this connection or null to call getResource()
226         * @return A working connection; will not return null
227         * @exception SQLException On any SQL error or if pool returns null
228         */
229        public Connection getConnection(String key)
230                throws SQLException {
231    
232            if (null==key) return getConnection();
233    
234            DataSource ds = getDataSource(key);
235    
236            if (null==ds)
237                throw new SQLException(getMessage());
238    
239            return (ds.getConnection());
240    
241        } // end getConnection
242    
243    
244    // --------------------------------------------------------------- pool
245    
246    
247       /**
248        * Field to store singleton reference to adaptor pool.
249        * This would usually be private for a singleton,
250        * but adaptor subclasses may set the pool upon initialization.
251        * <p>
252        * <code>if (null==pool) set pool = this</code>.
253        */
254        protected static ConnectionAdaptor pool;
255    
256    
257       /**
258        * Return adaptor instance to use in acquiring connection.
259        * <p>
260        * This is the main entry method to the object.
261        * Client's should call:<br>
262        * <code>Adaptor.getPool().getConnection()</code>
263        * to acquire a connection from the default pool.
264        */
265        public static ConnectionAdaptor getPool() {
266    
267            if (null==pool) pool = new ConnectionAdaptor();
268            return pool;
269    
270        } // end getPool
271    
272    
273    } // end ConnectionAdaptor
274