View Javadoc

1   /*
2    * Copyright (C) 2003-2012 David E. Berry
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   *
18   * A copy of the GNU Lesser General Public License may also be found at
19   * http://www.gnu.org/licenses/lgpl.txt
20   */
21  package org.synchronoss.cpo.jdbc;
22  
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.sql.Connection;
26  import java.sql.SQLException;
27  import java.util.LinkedList;
28  import java.util.Queue;
29  import java.util.SortedMap;
30  import javax.sql.*;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  import org.synchronoss.cpo.CpoException;
34  import org.synchronoss.cpo.helper.CpoClassLoader;
35  import org.synchronoss.cpo.helper.ExceptionHelper;
36  
37  /**
38   * Collects the info required to instantiate a DataSource from a JDBC Driver
39   *
40   * Provides the DataSourceInfo factory method getDataSource which instantiates the DataSource
41   *
42   * @author dberry
43   */
44  public class ClassDataSourceInfo extends AbstractDataSource implements ConnectionEventListener {
45  
46    private Logger logger = LoggerFactory.getLogger(this.getClass());
47    private ConnectionPoolDataSource poolDataSource = null;
48    private String className = null;
49    private SortedMap<String, String> properties = null;
50    // Make sure DataSource creation is thread safe.
51    final private Object LOCK = new Object();
52    private Queue<PooledConnection> freeConnections = new LinkedList<PooledConnection>();
53    private Queue<PooledConnection> usedConnections = new LinkedList<PooledConnection>();
54  
55    /**
56     * Creates a ClassDataSourceInfo from a Jdbc Driver
57     *
58     * @param classname The classname of a class that implements datasource
59     * @param properties - The connection properties for connecting to the database
60     */
61    public ClassDataSourceInfo(String className, SortedMap<String, String> properties) throws CpoException {
62      super(className, properties);
63      this.className=className;
64      this.properties=properties;
65    }
66  
67    @Override
68    public Connection getConnection() throws SQLException {
69      return getPooledConnection();
70    }
71  
72    private Connection getPooledConnection() throws SQLException {
73      PooledConnection pooledConn;
74      synchronized (LOCK) {
75        if (!freeConnections.isEmpty()) {
76          pooledConn = freeConnections.poll();
77        } else {
78          pooledConn = poolDataSource.getPooledConnection();
79          pooledConn.addConnectionEventListener(this);
80        }
81        usedConnections.add(pooledConn);
82      }
83      return pooledConn.getConnection();
84    }
85  
86    @Override
87    public synchronized String toString() {
88      StringBuilder info = new StringBuilder();
89      info.append("JdbcDataSource(");
90      info.append(getDataSourceName());
91      info.append(")");
92      return (info.toString());
93    }
94  
95    @Override
96    protected DataSource createDataSource() throws CpoException {
97      DataSource dataSource = null;
98      try {
99        Class dsClass = CpoClassLoader.forName(className);
100       CommonDataSource ds = (CommonDataSource) dsClass.newInstance();
101 
102       if (ds instanceof ConnectionPoolDataSource) {
103         this.poolDataSource = (ConnectionPoolDataSource) ds;
104         dataSource = this;
105       } else if (ds instanceof DataSource) {
106         dataSource = (DataSource) ds;
107       } else {
108         throw new CpoException(className + "is not a DataSource");
109       }
110       if (properties !=null)
111         setClassProperties(ds,properties);
112     } catch (ClassNotFoundException cnfe) {
113       throw new CpoException("Could Not Find Class: " + className, cnfe);
114     } catch (InstantiationException ie) {
115       throw new CpoException("Could Not Instantiate Class: " + className + ":" + ExceptionHelper.getLocalizedMessage(ie));
116     } catch (IllegalAccessException iae) {
117       throw new CpoException("Could Not Access Class: " + className, iae);
118     }
119     
120     return dataSource;
121   }
122 
123   @Override
124   public void connectionClosed(ConnectionEvent ce) {
125     synchronized (LOCK) {
126       PooledConnection pc = (PooledConnection) ce.getSource();
127       if (usedConnections.remove(pc)) {
128         freeConnections.add(pc);
129       }
130     }
131   }
132 
133   @Override
134   public void connectionErrorOccurred(ConnectionEvent ce) {
135     synchronized (LOCK) {
136       PooledConnection pc = (PooledConnection) ce.getSource();
137       if (!usedConnections.remove(pc)) {
138         // just in case the error is on a connection in the free pool
139         freeConnections.remove(pc);
140       }
141     }
142   }
143 
144   @Override
145   protected void finalize() throws Throwable {
146     super.finalize();
147     for (PooledConnection pc : freeConnections) {
148       pc.removeConnectionEventListener(this);
149       try {
150         pc.close();
151       } catch (SQLException se) {
152       }
153     }
154     for (PooledConnection pc : usedConnections) {
155       pc.removeConnectionEventListener(this);
156       try {
157         pc.close();
158       } catch (SQLException se) {
159       }
160     }
161   }
162 
163   private void setClassProperties(CommonDataSource ds, SortedMap<String, String> properties) {
164     for (String key : properties.keySet()) {
165       setObjectProperty(ds, key, properties.get(key));
166     }
167   }
168 
169   private void setObjectProperty(Object obj, String key, String value) {
170     String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
171     logger.debug("Calling "+methodName+"("+value+")");
172     try {
173       Method setter = obj.getClass().getMethod(methodName, String.class);
174       setter.invoke(obj, value);
175     } catch (NoSuchMethodException nsme) {
176       logger.error("=========>>> Could not find setter Method:" + methodName + " for property:" + key + " please check the java docs for " + obj.getClass().getName());
177     } catch (InvocationTargetException ite) {
178       logger.error("Error Invoking setter Method:" + methodName, ite);
179     } catch (IllegalAccessException iae) {
180       logger.error("Error accessing setter Method:" + methodName, iae);
181     }
182   }
183 }