org.nuxeo.ecm.core.storage.sql.jdbc.JDBCBackend.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.core.storage.sql.jdbc.JDBCBackend.java

Source

/*
 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Florent Guillaume
 */

package org.nuxeo.ecm.core.storage.sql.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map.Entry;

import javax.naming.NamingException;
import javax.resource.ResourceException;
import javax.sql.DataSource;
import javax.sql.XAConnection;
import javax.sql.XADataSource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.storage.StorageException;
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.api.Framework;
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 String pseudoDataSourceName;

    private XADataSource xadatasource;

    private Dialect dialect;

    private SQLInfo sqlInfo;

    private boolean firstMapper = true;

    private ClusterNodeHandler clusterNodeHandler;

    private JDBCConnectionPropagator connectionPropagator;

    public JDBCBackend() {
        connectionPropagator = new JDBCConnectionPropagator();
    }

    protected boolean isPooledDataSource;

    @Override
    public void initialize(RepositoryImpl repository) throws StorageException {
        this.repository = repository;
        RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor();
        pseudoDataSourceName = ConnectionHelper.getPseudoDataSourceNameForRepository(repositoryDescriptor.name);

        try {
            DataSource ds = DataSourceHelper.getDataSource(pseudoDataSourceName);
            if (ds instanceof PooledDataSource) {
                isPooledDataSource = true;
                return;
            }
        } catch (NamingException cause) {
            ;
        }

        // try single-datasource non-XA mode
        try (Connection connection = ConnectionHelper.getConnection(pseudoDataSourceName)) {
            if (connection != null) {
                return;
            }
        } catch (SQLException cause) {
            throw new StorageException("Connection error", cause);
        }

        // standard XA mode
        // instantiate the XA datasource
        String className = repositoryDescriptor.xaDataSourceName;
        Class<?> klass;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new StorageException("Unknown class: " + className, e);
        }
        Object instance;
        try {
            instance = klass.newInstance();
        } catch (Exception e) {
            throw new StorageException("Cannot instantiate class: " + className, e);
        }
        if (!(instance instanceof XADataSource)) {
            throw new StorageException("Not a XADataSource: " + className);
        }
        xadatasource = (XADataSource) instance;

        // set JavaBean properties on the datasource
        for (Entry<String, String> entry : repositoryDescriptor.properties.entrySet()) {
            String name = entry.getKey();
            Object value = Framework.expandVars(entry.getValue());
            if (name.contains("/")) {
                // old syntax where non-String types were explicited
                name = name.substring(0, name.indexOf('/'));
            }
            // transform to proper JavaBean convention
            if (Character.isLowerCase(name.charAt(1))) {
                name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
            }
            try {
                BeanUtils.setProperty(xadatasource, name, value);
            } catch (Exception e) {
                log.error(String.format("Cannot set %s = %s", name, value));
            }
        }
    }

    /**
     * {@inheritDoc}
     * <p>
     * Opens a connection to get the dialect and finish initializing the
     * {@link ModelSetup}.
     */
    @Override
    public void initializeModelSetup(ModelSetup modelSetup) throws StorageException {
        try {
            XAConnection xaconnection = null;
            // try single-datasource non-XA mode
            Connection connection = ConnectionHelper.getConnection(pseudoDataSourceName);
            try {
                if (connection == null) {
                    // standard XA mode
                    xaconnection = xadatasource.getXAConnection();
                    connection = xaconnection.getConnection();
                }
                dialect = Dialect.createDialect(connection, repository.getBinaryManager(),
                        repository.getRepositoryDescriptor());
            } finally {
                if (connection != null) {
                    connection.close();
                }
                if (xaconnection != null) {
                    xaconnection.close();
                }
            }
        } catch (SQLException | ResourceException cause) {
            throw new StorageException("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());
        }
    }

    /**
     * {@inheritDoc}
     * <p>
     * Creates the {@link SQLInfo} from the model and the dialect.
     */
    @Override
    public void initializeModel(Model model) throws StorageException {
        sqlInfo = new SQLInfo(model, dialect);
    }

    @Override
    public Mapper newMapper(Model model, PathResolver pathResolver, MapperKind kind) throws StorageException {
        boolean noSharing = kind == MapperKind.LOCK_MANAGER || kind == MapperKind.CLUSTER_NODE_HANDLER;
        boolean noInvalidationPropagation = kind == MapperKind.LOCK_MANAGER;
        RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor();

        ClusterNodeHandler cnh = noInvalidationPropagation ? null : clusterNodeHandler;
        Mapper mapper = new JDBCMapper(model, pathResolver, sqlInfo, xadatasource, cnh, connectionPropagator,
                noSharing, repository);
        if (isPooledDataSource) {
            mapper = JDBCMapperConnector.newConnector(mapper);
            if (noSharing) {
                mapper = JDBCMapperTxSuspender.newConnector(mapper);
            }
        } else {
            mapper.connect();
        }
        if (firstMapper) {
            firstMapper = false;
            if (repositoryDescriptor.getNoDDL()) {
                log.info("Skipping database creation");
            } else {
                // first connection, initialize the database
                mapper.createDatabase();
            }
        }
        if (kind == MapperKind.CLUSTER_NODE_HANDLER) {
            clusterNodeHandler = new ClusterNodeHandler(mapper, repositoryDescriptor);
            connectionPropagator.setClusterNodeHandler(clusterNodeHandler);
        }
        return mapper;
    }

    @Override
    public void shutdown() throws StorageException {
        if (clusterNodeHandler != null) {
            clusterNodeHandler.close();
        }
    }

}