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 }