001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.persistence; 021 022 import org.apache.commons.dbcp.BasicDataSource; 023 import org.apache.commons.dbcp.BasicDataSourceFactory; 024 import org.apache.commons.lang.StringUtils; 025 import org.hibernate.cfg.Environment; 026 import org.slf4j.Logger; 027 import org.slf4j.LoggerFactory; 028 import org.sonar.api.config.Settings; 029 import org.sonar.api.database.DatabaseProperties; 030 import org.sonar.jpa.dialect.Dialect; 031 import org.sonar.jpa.dialect.DialectRepository; 032 import org.sonar.jpa.session.CustomHibernateConnectionProvider; 033 034 import javax.sql.DataSource; 035 import java.sql.SQLException; 036 import java.util.List; 037 import java.util.Map; 038 import java.util.Properties; 039 040 /** 041 * @since 2.12 042 */ 043 public class DefaultDatabase implements Database { 044 045 private static final Logger LOG = LoggerFactory.getLogger(Database.class); 046 047 private Settings settings; 048 private BasicDataSource datasource; 049 private Dialect dialect; 050 051 public DefaultDatabase(Settings settings) { 052 this.settings = settings; 053 } 054 055 public final DefaultDatabase start() { 056 try { 057 doBeforeStart(); 058 059 Properties properties = getProperties(); 060 dialect = initDialect(properties); 061 datasource = initDatasource(properties); 062 return this; 063 064 } catch (Exception e) { 065 throw new IllegalStateException("Fail to connect to database", e); 066 } 067 } 068 069 BasicDataSource initDatasource(Properties properties) throws Exception { 070 LOG.info("Create JDBC datasource"); 071 return (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties)); 072 } 073 074 Dialect initDialect(Properties properties) { 075 Dialect result = DialectRepository.find(properties.getProperty("sonar.jdbc.dialect"), properties.getProperty("sonar.jdbc.url")); 076 if (result != null && "derby".equals(result.getId())) { 077 LoggerFactory.getLogger(getClass()).warn("Derby database should be used for evaluation purpose only"); 078 } 079 return result; 080 } 081 082 protected void doBeforeStart() { 083 } 084 085 public final DefaultDatabase stop() { 086 doBeforeStop(); 087 if (datasource != null) { 088 try { 089 datasource.close(); 090 } catch (SQLException e) { 091 throw new IllegalStateException("Fail to stop JDBC connection pool", e); 092 } 093 } 094 return this; 095 } 096 097 protected void doBeforeStop() { 098 099 } 100 101 public final Dialect getDialect() { 102 return dialect; 103 } 104 105 public Properties getHibernateProperties() { 106 Properties props = new Properties(); 107 108 List<String> hibernateKeys = settings.getKeysStartingWith("sonar.hibernate."); 109 for (String hibernateKey : hibernateKeys) { 110 props.put(StringUtils.removeStart(hibernateKey, "sonar."), settings.getString(hibernateKey)); 111 } 112 props.put(Environment.DIALECT, getDialect().getHibernateDialectClass().getName()); 113 props.put("hibernate.generate_statistics", settings.getBoolean(DatabaseProperties.PROP_HIBERNATE_GENERATE_STATISTICS)); 114 props.put("hibernate.hbm2ddl.auto", "validate"); 115 props.put(Environment.CONNECTION_PROVIDER, CustomHibernateConnectionProvider.class.getName()); 116 return props; 117 } 118 119 public final DataSource getDataSource() { 120 return datasource; 121 } 122 123 public final Properties getProperties() { 124 Properties properties = new Properties(); 125 completeProperties(settings, properties, "sonar.jdbc."); 126 completeProperties(settings, properties, "sonar.hibernate."); 127 completeDefaultProperties(properties); 128 doCompleteProperties(properties); 129 return properties; 130 } 131 132 protected void doCompleteProperties(Properties properties) { 133 134 } 135 136 static void completeProperties(Settings settings, Properties properties, String prefix) { 137 List<String> jdbcKeys = settings.getKeysStartingWith(prefix); 138 for (String jdbcKey : jdbcKeys) { 139 String value = settings.getString(jdbcKey); 140 properties.setProperty(jdbcKey, value); 141 } 142 } 143 144 static Properties extractCommonsDbcpProperties(Properties properties) { 145 Properties result = new Properties(); 146 for (Map.Entry<Object, Object> entry : properties.entrySet()) { 147 String key = (String) entry.getKey(); 148 if (StringUtils.startsWith(key, "sonar.jdbc.")) { 149 result.setProperty(StringUtils.removeStart(key, "sonar.jdbc."), (String) entry.getValue()); 150 } 151 } 152 153 // This property is required by the Ruby Oracle enhanced adapter. 154 // It directly uses the Connection implementation provided by the Oracle driver 155 result.setProperty("accessToUnderlyingConnectionAllowed", "true"); 156 return result; 157 } 158 159 private static void completeDefaultProperties(Properties props) { 160 completeDefaultProperty(props, DatabaseProperties.PROP_DRIVER, props.getProperty(DatabaseProperties.PROP_DRIVER_DEPRECATED, DatabaseProperties.PROP_DRIVER_DEFAULT_VALUE)); 161 completeDefaultProperty(props, DatabaseProperties.PROP_URL, DatabaseProperties.PROP_URL_DEFAULT_VALUE); 162 completeDefaultProperty(props, DatabaseProperties.PROP_USER, props.getProperty(DatabaseProperties.PROP_USER_DEPRECATED, DatabaseProperties.PROP_USER_DEFAULT_VALUE)); 163 completeDefaultProperty(props, DatabaseProperties.PROP_PASSWORD, DatabaseProperties.PROP_PASSWORD_DEFAULT_VALUE); 164 completeDefaultProperty(props, DatabaseProperties.PROP_HIBERNATE_HBM2DLL, "validate"); 165 } 166 167 private static void completeDefaultProperty(Properties props, String key, String defaultValue) { 168 if (props.getProperty(key) == null) { 169 props.setProperty(key, defaultValue); 170 } 171 } 172 }