/*
* (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 java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.atomic.AtomicLong;
import javax.transaction.xa.XAResource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.ConcurrentUpdateException;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.storage.sql.Mapper.Identification;
import org.nuxeo.ecm.core.storage.sql.Model;
import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.datasource.ConnectionHelper;
/**
* Holds a connection to a JDBC database.
*/
public class JDBCConnection {
private static final Log log = LogFactory.getLog(JDBCConnection.class);
/** JDBC application name parameter for setClientInfo. */
private static final String APPLICATION_NAME = "ApplicationName";
private static final String SET_CLIENT_INFO_PROP = "org.nuxeo.vcs.setClientInfo";
private static final String SET_CLIENT_INFO_DEFAULT = "false";
/** The model used to do the mapping. */
protected final Model model;
/** The SQL information. */
protected final SQLInfo sqlInfo;
/** The dialect. */
protected final Dialect dialect;
/** The actual connection. */
public Connection connection;
protected boolean supportsBatchUpdates;
protected XAResource xaresource = new XAResourceConnectionAdapter(this);
// for tests
public boolean countExecutes;
// for tests
public int executeCount;
// for debug
private static final AtomicLong instanceCounter = new AtomicLong(0);
// for debug
private final long instanceNumber = instanceCounter.incrementAndGet();
// for debug
public final JDBCLogger logger = new JDBCLogger(String.valueOf(instanceNumber));
protected boolean setClientInfo;
/**
* Creates a new Mapper.
*
* @param model the model
* @param sqlInfo the sql info
* @param noSharing whether to use no-sharing mode for the connection
*/
public JDBCConnection(Model model, SQLInfo sqlInfo) {
this.model = model;
this.sqlInfo = sqlInfo;
dialect = sqlInfo.dialect;
setClientInfo = Boolean.parseBoolean(Framework.getProperty(SET_CLIENT_INFO_PROP, SET_CLIENT_INFO_DEFAULT));
}
/**
* for tests only
*
* @since 5.9.3
*/
public JDBCConnection() {
sqlInfo = null;
model = null;
dialect = null;
}
public String getRepositoryName() {
return model.getRepositoryDescriptor().name;
}
public Identification getIdentification() {
return new Identification(null, "" + instanceNumber);
}
protected void countExecute() {
if (countExecutes) {
executeCount++;
}
}
/**
* Gets the datasource to use for the given repository.
*
* @since 8.4
*/
public static String getDataSourceName(String repositoryName) {
return "repository_" + repositoryName;
}
protected void openConnections(boolean noSharing) {
try {
openBaseConnection(noSharing);
supportsBatchUpdates = connection.getMetaData().supportsBatchUpdates();
dialect.performPostOpenStatements(connection);
} catch (SQLException cause) {
throw new NuxeoException("Cannot connect to database: " + getRepositoryName(), cause);
}
}
protected void openBaseConnection(boolean noSharing) throws SQLException {
String dataSourceName = getDataSourceName(getRepositoryName());
connection = ConnectionHelper.getConnection(dataSourceName, noSharing);
if (setClientInfo) {
// log the mapper number (m=123)
connection.setClientInfo(APPLICATION_NAME, "nuxeo m=" + instanceNumber);
}
}
public void close() {
closeConnections();
}
public void closeConnections() {
if (connection != null) {
try {
try {
if (setClientInfo) {
// connection will become idle in the pool
connection.setClientInfo(APPLICATION_NAME, "nuxeo");
}
} finally {
connection.close();
}
} catch (SQLException e) {
log.error(e, e);
} finally {
connection = null;
}
}
}
/**
* Checks the SQL error we got and determine if a concurrent update happened. Throws if that's the case.
*
* @param e the exception
* @since 5.8
*/
protected void checkConcurrentUpdate(Throwable e) throws ConcurrentUpdateException {
if (dialect.isConcurrentUpdateException(e)) {
throw new ConcurrentUpdateException(e);
}
}
}