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 java.sql.Connection;
023import java.sql.SQLException;
024
025import javax.naming.NamingException;
026import javax.sql.DataSource;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.ecm.core.api.NuxeoException;
031import org.nuxeo.ecm.core.storage.FulltextDescriptor;
032import org.nuxeo.ecm.core.storage.sql.ClusterInvalidator;
033import org.nuxeo.ecm.core.storage.sql.Mapper;
034import org.nuxeo.ecm.core.storage.sql.Model;
035import org.nuxeo.ecm.core.storage.sql.Model.IdType;
036import org.nuxeo.ecm.core.storage.sql.ModelSetup;
037import org.nuxeo.ecm.core.storage.sql.RepositoryBackend;
038import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor;
039import org.nuxeo.ecm.core.storage.sql.RepositoryImpl;
040import org.nuxeo.ecm.core.storage.sql.Session.PathResolver;
041import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect;
042import org.nuxeo.runtime.datasource.ConnectionHelper;
043import org.nuxeo.runtime.datasource.DataSourceHelper;
044import org.nuxeo.runtime.datasource.PooledDataSourceRegistry.PooledDataSource;
045
046/**
047 * JDBC Backend for a repository.
048 */
049public class JDBCBackend implements RepositoryBackend {
050
051    private static final Log log = LogFactory.getLog(JDBCBackend.class);
052
053    private RepositoryImpl repository;
054
055    private Model model;
056
057    private SQLInfo sqlInfo;
058
059    private ClusterInvalidator clusterInvalidator;
060
061    private boolean isPooledDataSource;
062
063    @Override
064    public Model initialize(RepositoryImpl repository) {
065        this.repository = repository;
066        RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor();
067        String dataSourceName = JDBCConnection.getDataSourceName(repositoryDescriptor.name);
068
069        // check datasource
070        try {
071            DataSource ds = DataSourceHelper.getDataSource(dataSourceName);
072            if (ds instanceof PooledDataSource) {
073                isPooledDataSource = true;
074            }
075        } catch (NamingException cause) {
076            throw new NuxeoException("Cannot acquire datasource: " + dataSourceName, cause);
077        }
078
079        // check connection and get dialect
080        Dialect dialect;
081        try (Connection connection = ConnectionHelper.getConnection(dataSourceName)) {
082            dialect = Dialect.createDialect(connection, repositoryDescriptor);
083        } catch (SQLException cause) {
084            throw new NuxeoException("Cannot get connection from datasource: " + dataSourceName, cause);
085        }
086
087        // model setup
088        ModelSetup modelSetup = new ModelSetup();
089        modelSetup.materializeFulltextSyntheticColumn = dialect.getMaterializeFulltextSyntheticColumn();
090        modelSetup.supportsArrayColumns = dialect.supportsArrayColumns();
091        switch (dialect.getIdType()) {
092        case VARCHAR:
093        case UUID:
094            modelSetup.idType = IdType.STRING;
095            break;
096        case SEQUENCE:
097            modelSetup.idType = IdType.LONG;
098            break;
099        default:
100            throw new AssertionError(dialect.getIdType().toString());
101        }
102        modelSetup.repositoryDescriptor = repositoryDescriptor;
103
104        // Model and SQLInfo
105        model = new Model(modelSetup);
106        sqlInfo = new SQLInfo(model, dialect);
107
108        // DDL mode
109        String ddlMode = repositoryDescriptor.getDDLMode();
110        if (ddlMode == null) {
111            // compat
112            ddlMode = repositoryDescriptor.getNoDDL() ? RepositoryDescriptor.DDL_MODE_IGNORE
113                    : RepositoryDescriptor.DDL_MODE_EXECUTE;
114        }
115
116        // create database
117        if (ddlMode.equals(RepositoryDescriptor.DDL_MODE_IGNORE)) {
118            log.info("Skipping database creation");
119        } else {
120            Mapper mapper = newMapper(null, false);
121            try {
122                mapper.createDatabase(ddlMode);
123            } finally {
124                mapper.close();
125            }
126        }
127        if (log.isDebugEnabled()) {
128            FulltextDescriptor fulltextDescriptor = repositoryDescriptor.getFulltextDescriptor();
129            log.debug(String.format("Database ready, fulltext: disabled=%b searchDisabled=%b.",
130                    fulltextDescriptor.getFulltextDisabled(), fulltextDescriptor.getFulltextSearchDisabled()));
131        }
132
133        return model;
134    }
135
136    @Override
137    public void setClusterInvalidator(ClusterInvalidator clusterInvalidator) {
138        this.clusterInvalidator = clusterInvalidator;
139    }
140
141    @Override
142    public Mapper newMapper(PathResolver pathResolver, boolean useInvalidations) {
143        boolean noSharing = !useInvalidations;
144        ClusterInvalidator cnh = useInvalidations ? clusterInvalidator : null;
145        Mapper mapper = new JDBCMapper(model, pathResolver, sqlInfo, cnh, repository);
146        if (isPooledDataSource) {
147            mapper = JDBCMapperConnector.newConnector(mapper, noSharing);
148        } else {
149            mapper.connect(false);
150        }
151        return mapper;
152    }
153
154    @Override
155    public void shutdown() {
156        if (clusterInvalidator != null) {
157            clusterInvalidator.close();
158        }
159    }
160
161}