/* * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.storage.sql.jdbc; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import java.sql.Connection; import java.sql.SQLException; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.core.storage.FulltextDescriptor; import org.nuxeo.ecm.core.storage.sql.ClusterInvalidator; import org.nuxeo.ecm.core.storage.sql.Mapper; import org.nuxeo.ecm.core.storage.sql.Model; import org.nuxeo.ecm.core.storage.sql.Model.IdType; import org.nuxeo.ecm.core.storage.sql.ModelSetup; import org.nuxeo.ecm.core.storage.sql.RepositoryBackend; import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor; import org.nuxeo.ecm.core.storage.sql.RepositoryImpl; import org.nuxeo.ecm.core.storage.sql.Session.PathResolver; import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect; import org.nuxeo.runtime.datasource.ConnectionHelper; import org.nuxeo.runtime.datasource.DataSourceHelper; import org.nuxeo.runtime.datasource.PooledDataSourceRegistry.PooledDataSource; /** * JDBC Backend for a repository. */ public class JDBCBackend implements RepositoryBackend { private static final Log log = LogFactory.getLog(JDBCBackend.class); private RepositoryImpl repository; private Dialect dialect; private SQLInfo sqlInfo; private boolean firstMapper = true; private Boolean initialized; private ClusterInvalidator clusterInvalidator; private boolean isPooledDataSource; @Override public void initialize(RepositoryImpl repository) { this.repository = repository; String dataSourceName = getDataSourceName(); try { DataSource ds = DataSourceHelper.getDataSource(dataSourceName); if (ds instanceof PooledDataSource) { isPooledDataSource = true; } } catch (NamingException cause) { throw new NuxeoException("Cannot acquire datasource: " + dataSourceName, cause); } // check early that the connection is valid try (Connection connection = ConnectionHelper.getConnection(dataSourceName)) { // do nothing, just acquire it to test } catch (SQLException cause) { throw new NuxeoException("Cannot get connection from datasource: " + dataSourceName, cause); } } /** * {@inheritDoc} * <p> * Opens a connection to get the dialect and finish initializing the {@link ModelSetup}. */ @Override public void initializeModelSetup(ModelSetup modelSetup) { String dataSourceName = getDataSourceName(); try (Connection connection = ConnectionHelper.getConnection(dataSourceName)) { dialect = Dialect.createDialect(connection, repository.getRepositoryDescriptor()); } catch (SQLException cause) { throw new NuxeoException("Cannot connect to database", cause); } modelSetup.materializeFulltextSyntheticColumn = dialect.getMaterializeFulltextSyntheticColumn(); modelSetup.supportsArrayColumns = dialect.supportsArrayColumns(); switch (dialect.getIdType()) { case VARCHAR: case UUID: modelSetup.idType = IdType.STRING; break; case SEQUENCE: modelSetup.idType = IdType.LONG; break; default: throw new AssertionError(dialect.getIdType().toString()); } } protected String getDataSourceName() { RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor(); return JDBCConnection.getDataSourceName(repositoryDescriptor.name); } /** * {@inheritDoc} * <p> * Creates the {@link SQLInfo} from the model and the dialect. */ @Override public void initializeModel(Model model) { sqlInfo = new SQLInfo(model, dialect); } @Override public void setClusterInvalidator(ClusterInvalidator clusterInvalidator) { this.clusterInvalidator = clusterInvalidator; } @Override public Mapper newMapper(Model model, PathResolver pathResolver, boolean useInvalidations) { boolean noSharing = !useInvalidations; RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor(); ClusterInvalidator cnh = useInvalidations ? clusterInvalidator : null; Mapper mapper = new JDBCMapper(model, pathResolver, sqlInfo, cnh, repository); if (isPooledDataSource) { mapper = JDBCMapperConnector.newConnector(mapper, noSharing); } else { mapper.connect(false); } String repositoryName = repository.getName(); if (FALSE.equals(initialized)) { throw new NuxeoException("Database initialization failed previously for: " + repositoryName); } if (firstMapper) { initialized = FALSE; firstMapper = false; String ddlMode = repositoryDescriptor.getDDLMode(); if (ddlMode == null) { // compat ddlMode = repositoryDescriptor.getNoDDL() ? RepositoryDescriptor.DDL_MODE_IGNORE : RepositoryDescriptor.DDL_MODE_EXECUTE; } if (ddlMode.equals(RepositoryDescriptor.DDL_MODE_IGNORE)) { log.info("Skipping database creation"); } else { // first connection, initialize the database mapper.createDatabase(ddlMode); } if (log.isDebugEnabled()) { FulltextDescriptor fulltextDescriptor = repositoryDescriptor.getFulltextDescriptor(); log.debug(String.format("Database ready, fulltext: disabled=%b searchDisabled=%b.", fulltextDescriptor.getFulltextDisabled(), fulltextDescriptor.getFulltextSearchDisabled())); } initialized = TRUE; } return mapper; } @Override public void shutdown() { if (clusterInvalidator != null) { clusterInvalidator.close(); } } }