001/*
002 *  Copyright 2012 Anyware Services
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 */
016package org.ametys.core.datasource;
017
018import java.sql.Connection;
019import java.sql.ResultSet;
020import java.sql.SQLException;
021import java.sql.Statement;
022import java.util.Map;
023
024import javax.sql.DataSource;
025
026import org.apache.avalon.framework.activity.Disposable;
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.commons.lang3.StringUtils;
032import org.apache.commons.lang3.Strings;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import org.ametys.core.datasource.AbstractDataSourceManager.DataSourceDefinition;
037
038/**
039 * Helper component used to retrieve java.sql.Connection from pools
040 */
041public final class ConnectionHelper implements Component, Serviceable, Disposable
042{
043    /** The Avalon role */
044    public static final String ROLE = ConnectionHelper.class.getName();
045
046    /** ID of database extension for Unknown */
047    public static final String DATABASE_UNKNOWN = "";
048    /** ID of database extension for Mysql */
049    public static final String DATABASE_MYSQL = "mysql";
050    /** ID of database extension for Oracle */
051    public static final String DATABASE_ORACLE = "oracle";
052    /** ID of database extension for Postgres */
053    public static final String DATABASE_POSTGRES = "postgresql";
054    /** ID of database extension for Derby */
055    public static final String DATABASE_DERBY = "derby";
056    /** ID of database extension for Hsqldb */
057    public static final String DATABASE_HSQLDB = "hsqldb";
058    
059    
060    /** Logger for traces */
061    private static Logger _logger = LoggerFactory.getLogger(ConnectionHelper.class.getName());
062    
063    /** The manager for SQL data source */
064    private static SQLDataSourceManager _sqlDataSourceManager;
065    
066    private static ServiceManager _manager;
067    
068    @Override
069    public void service(ServiceManager serviceManager) throws ServiceException
070    {
071        _manager = serviceManager;
072    }
073    
074    public void dispose()
075    {
076        _sqlDataSourceManager = null;
077    }
078    
079    private static SQLDataSourceManager getSQLDataSourceManager()
080    {
081        if (_sqlDataSourceManager == null)
082        {
083            try
084            {
085                _sqlDataSourceManager = (SQLDataSourceManager) _manager.lookup(SQLDataSourceManager.ROLE);
086            }
087            catch (ServiceException e)
088            {
089                throw new RuntimeException(e);
090            }
091        }
092        return _sqlDataSourceManager;
093    }
094
095    /**
096     * Get a connection to the internal sql data source
097     * @return java.sql.Connection to query the internal SQL database
098     */
099    public static Connection getInternalSQLDataSourceConnection()
100    {
101        return getSQLDataSourceManager().getInternalSQLDataSourceConnection();
102    }
103    
104    /**
105     * Returns a Connection from the pool.
106     * @param id the id of the data source
107     * @return a java.sql.Connection to query a SQL database
108     */
109    public static Connection getConnection(String id)
110    {
111        DataSource dataSource;
112        Connection connection = null;
113        
114        if (getSQLDataSourceManager() == null)
115        {
116            throw new RuntimeException("ConnectionHelper cannot be used statically during or before components initialization");
117        }
118        
119        try
120        {
121            dataSource =  getSQLDataSourceManager().getSQLDataSource(id);
122            connection = dataSource.getConnection();
123        }
124        catch (SQLException e)
125        {
126            throw new RuntimeException("Unable to get Connection from pool " + id, e);
127        }
128
129        return connection;
130    }
131    
132    /**
133     * Commit and closes a java.sql.Connection
134     * @param con the Connection to close
135     */
136    public static void cleanup(Connection con)
137    {
138        if (con != null)
139        {
140            try
141            {
142                if (!con.getAutoCommit())
143                {
144                    con.commit();
145                }
146            }
147            catch (SQLException s)
148            {
149                _logger.error("Error while closing database", s);
150            }
151
152            try
153            {
154                con.close();
155            }
156            catch (SQLException s)
157            {
158                _logger.error("Error while closing database", s);
159            }
160        }
161    }
162    
163    /**
164     * Closes a java.sql.Statement
165     * @param stmt the Statement to close
166     */
167    public static void cleanup(Statement stmt)
168    {
169        if (stmt != null)
170        {
171            try
172            {
173                stmt.close();
174            }
175            catch (SQLException s)
176            {
177                _logger.error("Error while closing statement", s);
178            }
179        }
180    }
181    
182    /**
183     * Closes a java.sql.ResultSet
184     * @param rs the ResultSet to close
185     */
186    public static void cleanup(ResultSet rs)
187    {
188        if (rs != null)
189        {
190            try
191            {
192                rs.close();
193            }
194            catch (SQLException s)
195            {
196                _logger.error("Error while closing statement", s);
197            }
198        }
199    }   
200
201    /**
202     * Determine the database type
203     * @param connection The jdbc connection to the database
204     * @return The database type id or empty string if unknown
205     */
206    public static String getDatabaseType(Connection connection)
207    {
208        try
209        {
210            return getDatabaseType(connection.getMetaData().getURL());
211        }
212        catch (SQLException e)
213        {
214            LoggerFactory.getLogger(ConnectionHelper.class).error("Cannot determine database type", e);
215            return DATABASE_UNKNOWN;
216        }
217    }
218    
219    /**
220     * Determine the database type
221     * @param jdbcURL The jdbc url used to connect to the database
222     * @return The database type id or null if unknown
223     */
224    public static String getDatabaseType(String jdbcURL)
225    {
226        // Get the definition url without jdbc parameters (e.g. internal-db have ;create=true)
227        String jdbcURLWithoutParams = StringUtils.substringBefore(jdbcURL, ";");
228        
229        Map<String, DataSourceDefinition> dataSourceDefinitions = getSQLDataSourceManager().getDataSourceDefinitions(true, true, false);
230        for (DataSourceDefinition definition : dataSourceDefinitions.values())
231        {
232            // Get the definition url without jdbc parameters (e.g. internal-db have ;create=true)
233            String url = StringUtils.substringBefore((String) definition.getParameters().get("url"), ";");
234            if (Strings.CS.equals(url, jdbcURLWithoutParams))
235            {
236                return (String) definition.getParameters().get("dbtype");
237            }
238        }
239        
240        return DATABASE_UNKNOWN;
241    }
242    
243    /**
244     * Returns the SQL {@link DataSourceDefinition} corresponding to the given id.
245     * @param id the id of the data source
246     * @return the {@link DataSourceDefinition}.
247     */
248    public static DataSourceDefinition getDataSourceDefinition(String id)
249    {
250        return getSQLDataSourceManager().getDataSourceDefinition(id);
251    }
252}