View Javadoc

1   /***
2    * @(#) DataSourceFactory.java
3    * 
4    * JFoxSOAF, Service-Oriented Application Framework
5    * 
6    * Copyright(c) JFoxSOAF Team
7    * 
8    * Licensed under the GNU LGPL, Version 2.1 (the "License"); 
9    * you may not use this file except in compliance with the License. 
10   * You may obtain a copy of the License at  
11   * 
12   * http://www.gnu.org/copyleft/lesser.html
13   * 
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, 
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
17   * See the License for the specific language governing permissions and 
18   * limitations under the License. 
19   * 
20   * For more information, please visit:
21   * http://www.jfox.cn/confluence/display/JFoxSOAF/Home
22   * http://www.huihoo.org/jfox/jfoxsoaf
23   */
24  
25  package org.huihoo.jfox.soaf.services.jdbc;
26  
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.security.AccessController;
30  import java.security.PrivilegedAction;
31  import java.sql.SQLException;
32  import java.util.Enumeration;
33  import java.util.Hashtable;
34  import java.util.Properties;
35  
36  import javax.sql.DataSource;
37  
38  import org.huihoo.jfox.soaf.exception.DataSourceConfigurationException;
39  import org.huihoo.jfox.soaf.util.resource.ResourceHelper;
40  
41  /***
42   * <p>
43   * Factory for creating database connection instances, with discovery and
44   * configuration features
45   * </p>
46   * 
47   * @author <a href="mailto:founder_chen@yahoo.com.cn">Peter Cheng </a>
48   * @version $Revision: 1.6 $ $Date: 2005/05/22 06:49:48 $
49   * @version Revision: 1.0
50   */
51  
52  public abstract class DataSourceFactory {
53  
54      /***
55       * Protected constructor that is not available for public use.
56       */
57      protected DataSourceFactory() {
58      }
59  
60      /***
61       * The previously constructed <code>LogFactory</code> instances, keyed by
62       * the <code>ClassLoader</code> with which it was created.
63       */
64      protected static Hashtable factories = new Hashtable();
65  
66      /***
67       * Get singlton database connection instance.
68       */
69      public abstract DataSource getInstance()
70              throws DataSourceConfigurationException, SQLException;
71  
72      /***
73       * Return the configuration attribute with the specified name (if any), or
74       * <code>null</code> if there is no such attribute.
75       * 
76       * @param name Name of the attribute to return
77       */
78      public abstract Object getAttribute(String name);
79  
80      /***
81       * Return an array containing the names of all currently defined
82       * configuration attributes. If there are no such attributes, a zero length
83       * array is returned.
84       */
85      public abstract String[] getAttributeNames();
86  
87      /***
88       * Remove any configuration attribute associated with the specified name. If
89       * there is no such attribute, no action is taken.
90       * 
91       * @param name Name of the attribute to remove
92       */
93      public abstract void removeAttribute(String name);
94  
95      /***
96       * Set the configuration attribute with the specified name. Calling this
97       * with a <code>null</code> value is equivalent to calling
98       * <code>removeAttribute(name)</code>.
99       * 
100      * @param name Name of the attribute to set
101      * @param value Value of the attribute to set, or <code>null</code> to
102      *            remove any setting for this attribute
103      */
104     public abstract void setAttribute(String name, Object value);
105 
106     public static DataSourceFactory getFactory()
107             throws DataSourceConfigurationException {
108         // Return any previously registered factory for this class loader
109         DataSourceFactory factory = getCachedFactory(DataSourceFactory.class
110                 .getClassLoader());
111 
112         if (factory != null) {
113             return factory;
114         }
115 
116         // Load properties file
117         Properties props = null;
118         try {
119             InputStream stream = ResourceHelper
120                     .getResourceAsStream(DatabaseConstant.JDBC_FACTORY_PROPERTIES);
121 
122             if (stream != null) {
123                 props = new Properties();
124                 props.load(stream);
125                 stream.close();
126             }
127         } catch (IOException e) {
128             throw new DataSourceConfigurationException(
129                     "Load Database configuration IO exception " + e);
130         } catch (SecurityException e) {
131             throw new DataSourceConfigurationException(
132                     "Database configuration access security exception " + e);
133         }
134 
135         // Second try a properties file.
136         if (factory == null && props != null) {
137             String factoryClass = props
138                     .getProperty(DatabaseConstant.JDBC_DATABASE_FACTORY);
139             if (factoryClass != null && !factoryClass.equals("")) {
140                 factory = newFactory(factoryClass, DataSourceFactory.class
141                         .getClassLoader());
142             }
143         }
144 
145         // Third, try the fallback implementation class
146         if (factory == null) {
147             factory = newFactory(DatabaseConstant.JDBC_DEFAULT_FACTORY,
148                     DataSourceFactory.class.getClassLoader());
149         }
150 
151         if (factory != null) {
152             /***
153              * Always cache using context class loader..
154              */
155             cacheFactory(DataSourceFactory.class.getClassLoader(), factory);
156 
157             if (props != null) {
158                 Enumeration names = props.propertyNames();
159                 while (names.hasMoreElements()) {
160                     String name = (String) names.nextElement();
161                     String value = props.getProperty(name);
162                     factory.setAttribute(name, value);
163                 }
164             }
165         }
166 
167         return factory;
168     }
169 
170     /***
171      * Check cached factories (keyed by classLoader)
172      */
173     private static DataSourceFactory getCachedFactory(
174             ClassLoader contextClassLoader) {
175         DataSourceFactory factory = null;
176 
177         if (contextClassLoader != null) {
178             factory = (DataSourceFactory) factories.get(contextClassLoader);
179         }
180 
181         return factory;
182     }
183 
184     private static void cacheFactory(ClassLoader classLoader,
185             DataSourceFactory factory) {
186         if (classLoader != null && factory != null) {
187             factories.put(classLoader, factory);
188         }
189     }
190 
191     /***
192      * Return a new instance of the specified <code>LogFactory</code>
193      * implementation class, loaded by the specified class loader. If that
194      * fails, try the class loader used to load this (abstract) LogFactory.
195      * 
196      * @param factoryClass Fully qualified name of the <code>LogFactory</code>
197      *            implementation class
198      * @param classLoader ClassLoader from which to load this class
199      * @exception DataSourceConfigException if a suitable instance cannot be
200      *                created
201      */
202     protected static DataSourceFactory newFactory(final String factoryClass,
203             final ClassLoader classLoader)
204             throws DataSourceConfigurationException {
205         Object result = AccessController.doPrivileged(new PrivilegedAction() {
206 
207             public Object run() {
208                 try {
209                     if (classLoader != null) {
210                         try {
211                             return classLoader.loadClass(factoryClass)
212                                     .newInstance();
213                         } catch (ClassNotFoundException ex) {
214                             if (classLoader == DataSourceFactory.class
215                                     .getClassLoader()) {
216                                 // Nothing more to try, onwards.
217                                 throw ex;
218                             }
219                             // ignore exception, continue
220                         } catch (NoClassDefFoundError e) {
221                             if (classLoader == DataSourceFactory.class
222                                     .getClassLoader()) {
223                                 throw e;
224                             }
225 
226                         } catch (ClassCastException e) {
227 
228                             if (classLoader == DataSourceFactory.class
229                                     .getClassLoader()) {
230                                 throw e;
231                             }
232                         }
233                     }
234                     return Class.forName(factoryClass).newInstance();
235                 } catch (Exception e) {
236                     return new DataSourceConfigurationException(e);
237                 }
238             }
239         });
240 
241         if (result instanceof DataSourceConfigurationException) {
242             throw (DataSourceConfigurationException) result;
243         }
244 
245         return (DataSourceFactory) result;
246     }
247 
248     /***
249      * Retrive concreate datasource.
250      * 
251      * @return PoolDataSource
252      * @throws DataSourceConfigurationException
253      */
254     public static DataSource getDataSource()
255             throws DataSourceConfigurationException {
256         try {
257             return getFactory().getInstance();
258         } catch (Exception e) {
259             throw new DataSourceConfigurationException(e.getMessage(), e);
260         }
261     }
262 
263 }