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.jpa.session;
021
022 import org.slf4j.Logger;
023 import org.slf4j.LoggerFactory;
024 import org.sonar.api.utils.Logs;
025 import org.sonar.core.persistence.Database;
026 import org.sonar.core.persistence.dialect.Dialect;
027 import org.sonar.jpa.entity.SchemaMigration;
028
029 import javax.persistence.EntityManager;
030 import javax.persistence.EntityManagerFactory;
031 import javax.persistence.Persistence;
032 import java.sql.Connection;
033 import java.sql.SQLException;
034 import java.util.Map;
035 import java.util.Properties;
036
037 public abstract class AbstractDatabaseConnector implements DatabaseConnector {
038 protected static final Logger LOG = LoggerFactory.getLogger(AbstractDatabaseConnector.class);
039
040 protected Database database;
041 private EntityManagerFactory factory = null;
042 private int databaseVersion = SchemaMigration.VERSION_UNKNOWN;
043 private boolean operational = false;
044 private boolean started = false;
045
046 protected AbstractDatabaseConnector(Database database) {
047 this.database = database;
048 }
049
050 public String getDialectId() {
051 return database.getDialect().getId();
052 }
053
054 /**
055 * Indicates if the connector is operational : database connection OK and schema version OK
056 */
057 public boolean isOperational() {
058 return operational;
059 }
060
061 /**
062 * Indicates if the connector is started : database connection OK and schema version OK or KO
063 */
064 protected boolean isStarted() {
065 return started;
066 }
067
068 public void start() {
069 if (!started) {
070 testConnection();
071 started = true;
072 }
073 if (!operational) {
074 boolean upToDate = upToDateSchemaVersion();
075 if (upToDate) {
076 Logs.INFO.info("Initializing Hibernate");
077 factory = createEntityManagerFactory();
078 operational = true;
079 }
080 }
081 }
082
083 public void stop() {
084 if (factory != null && factory.isOpen()) {
085 factory.close();
086 factory = null;
087 }
088 operational = false;
089 started = false;
090 database = null;
091 }
092
093 public EntityManagerFactory getEntityManagerFactory() {
094 return factory;
095 }
096
097 protected void setEntityManagerFactory(EntityManagerFactory factory) {
098 this.factory = factory;
099 }
100
101 protected EntityManagerFactory createEntityManagerFactory() {
102 // other settings are stored into /META-INF/persistence.xml
103 Properties props = database.getHibernateProperties();
104 logHibernateSettings(props);
105 return Persistence.createEntityManagerFactory("sonar", props);
106 }
107
108 private void logHibernateSettings(Properties props) {
109 if (LOG.isDebugEnabled()) {
110 for (Map.Entry<Object, Object> entry : props.entrySet()) {
111 LOG.debug(entry.getKey() + ": " + entry.getValue());
112 }
113 }
114 }
115
116 public EntityManager createEntityManager() {
117 return factory.createEntityManager();
118 }
119
120 private String testConnection() throws DatabaseException {
121 Connection connection = null;
122 try {
123 connection = getConnection();
124 return connection.getMetaData().getURL();
125
126 } catch (SQLException e) {
127 throw new DatabaseException("Cannot open connection to database: " + e.getMessage(), e);
128
129 } finally {
130 close(connection);
131 }
132 }
133
134 protected int loadVersion() {
135 Connection connection = null;
136 try {
137 connection = getConnection();
138 return SchemaMigration.getCurrentVersion(connection);
139
140 } catch (SQLException e) {
141 // schema not created
142 return 0;
143 } finally {
144 close(connection);
145 }
146 }
147
148 private void close(Connection connection) {
149 if (connection != null) {
150 try {
151 connection.close();
152 } catch (SQLException e) {
153 // why does close() throw a checked-exception ???
154 }
155 }
156 }
157
158 protected boolean upToDateSchemaVersion() {
159 if (databaseVersion == SchemaMigration.LAST_VERSION) {
160 return true;
161 }
162 databaseVersion = loadVersion();
163 return databaseVersion == SchemaMigration.LAST_VERSION;
164 }
165
166 public final int getDatabaseVersion() {
167 return databaseVersion;
168 }
169
170 public final Dialect getDialect() {
171 return database.getDialect();
172 }
173 }