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}