001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019 020package org.nuxeo.ecm.core.storage.sql.jdbc; 021 022import static java.lang.Boolean.FALSE; 023import static java.lang.Boolean.TRUE; 024 025import java.sql.Connection; 026import java.sql.SQLException; 027 028import javax.naming.NamingException; 029import javax.sql.DataSource; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.ecm.core.api.NuxeoException; 034import org.nuxeo.ecm.core.storage.FulltextDescriptor; 035import org.nuxeo.ecm.core.storage.sql.ClusterInvalidator; 036import org.nuxeo.ecm.core.storage.sql.Mapper; 037import org.nuxeo.ecm.core.storage.sql.Model; 038import org.nuxeo.ecm.core.storage.sql.Model.IdType; 039import org.nuxeo.ecm.core.storage.sql.ModelSetup; 040import org.nuxeo.ecm.core.storage.sql.RepositoryBackend; 041import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor; 042import org.nuxeo.ecm.core.storage.sql.RepositoryImpl; 043import org.nuxeo.ecm.core.storage.sql.Session.PathResolver; 044import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect; 045import org.nuxeo.runtime.datasource.ConnectionHelper; 046import org.nuxeo.runtime.datasource.DataSourceHelper; 047import org.nuxeo.runtime.datasource.PooledDataSourceRegistry.PooledDataSource; 048 049/** 050 * JDBC Backend for a repository. 051 */ 052public class JDBCBackend implements RepositoryBackend { 053 054 private static final Log log = LogFactory.getLog(JDBCBackend.class); 055 056 private RepositoryImpl repository; 057 058 private Dialect dialect; 059 060 private SQLInfo sqlInfo; 061 062 private boolean firstMapper = true; 063 064 private Boolean initialized; 065 066 private ClusterInvalidator clusterInvalidator; 067 068 private boolean isPooledDataSource; 069 070 @Override 071 public void initialize(RepositoryImpl repository) { 072 this.repository = repository; 073 String dataSourceName = getDataSourceName(); 074 075 try { 076 DataSource ds = DataSourceHelper.getDataSource(dataSourceName); 077 if (ds instanceof PooledDataSource) { 078 isPooledDataSource = true; 079 } 080 } catch (NamingException cause) { 081 throw new NuxeoException("Cannot acquire datasource: " + dataSourceName, cause); 082 } 083 084 // check early that the connection is valid 085 try (Connection connection = ConnectionHelper.getConnection(dataSourceName)) { 086 // do nothing, just acquire it to test 087 } catch (SQLException cause) { 088 throw new NuxeoException("Cannot get connection from datasource: " + dataSourceName, cause); 089 } 090 } 091 092 /** 093 * {@inheritDoc} 094 * <p> 095 * Opens a connection to get the dialect and finish initializing the {@link ModelSetup}. 096 */ 097 @Override 098 public void initializeModelSetup(ModelSetup modelSetup) { 099 String dataSourceName = getDataSourceName(); 100 try (Connection connection = ConnectionHelper.getConnection(dataSourceName)) { 101 dialect = Dialect.createDialect(connection, repository.getRepositoryDescriptor()); 102 } catch (SQLException cause) { 103 throw new NuxeoException("Cannot connect to database", cause); 104 } 105 modelSetup.materializeFulltextSyntheticColumn = dialect.getMaterializeFulltextSyntheticColumn(); 106 modelSetup.supportsArrayColumns = dialect.supportsArrayColumns(); 107 switch (dialect.getIdType()) { 108 case VARCHAR: 109 case UUID: 110 modelSetup.idType = IdType.STRING; 111 break; 112 case SEQUENCE: 113 modelSetup.idType = IdType.LONG; 114 break; 115 default: 116 throw new AssertionError(dialect.getIdType().toString()); 117 } 118 } 119 120 protected String getDataSourceName() { 121 RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor(); 122 return JDBCConnection.getDataSourceName(repositoryDescriptor.name); 123 } 124 125 /** 126 * {@inheritDoc} 127 * <p> 128 * Creates the {@link SQLInfo} from the model and the dialect. 129 */ 130 @Override 131 public void initializeModel(Model model) { 132 sqlInfo = new SQLInfo(model, dialect); 133 } 134 135 @Override 136 public void setClusterInvalidator(ClusterInvalidator clusterInvalidator) { 137 this.clusterInvalidator = clusterInvalidator; 138 } 139 140 @Override 141 public Mapper newMapper(Model model, PathResolver pathResolver, boolean useInvalidations) { 142 boolean noSharing = !useInvalidations; 143 RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor(); 144 145 ClusterInvalidator cnh = useInvalidations ? clusterInvalidator : null; 146 Mapper mapper = new JDBCMapper(model, pathResolver, sqlInfo, cnh, repository); 147 if (isPooledDataSource) { 148 mapper = JDBCMapperConnector.newConnector(mapper, noSharing); 149 } else { 150 mapper.connect(false); 151 } 152 String repositoryName = repository.getName(); 153 if (FALSE.equals(initialized)) { 154 throw new NuxeoException("Database initialization failed previously for: " + repositoryName); 155 } 156 if (firstMapper) { 157 initialized = FALSE; 158 firstMapper = false; 159 String ddlMode = repositoryDescriptor.getDDLMode(); 160 if (ddlMode == null) { 161 // compat 162 ddlMode = repositoryDescriptor.getNoDDL() ? RepositoryDescriptor.DDL_MODE_IGNORE 163 : RepositoryDescriptor.DDL_MODE_EXECUTE; 164 } 165 if (ddlMode.equals(RepositoryDescriptor.DDL_MODE_IGNORE)) { 166 log.info("Skipping database creation"); 167 } else { 168 // first connection, initialize the database 169 mapper.createDatabase(ddlMode); 170 } 171 if (log.isDebugEnabled()) { 172 FulltextDescriptor fulltextDescriptor = repositoryDescriptor.getFulltextDescriptor(); 173 log.debug(String.format("Database ready, fulltext: disabled=%b searchDisabled=%b.", 174 fulltextDescriptor.getFulltextDisabled(), fulltextDescriptor.getFulltextSearchDisabled())); 175 } 176 initialized = TRUE; 177 } 178 return mapper; 179 } 180 181 @Override 182 public void shutdown() { 183 if (clusterInvalidator != null) { 184 clusterInvalidator.close(); 185 } 186 } 187 188}