View Javadoc

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