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