001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2012 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    }