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 */ 019package org.nuxeo.ecm.core.storage.sql.jdbc; 020 021import java.sql.Connection; 022import java.sql.ResultSet; 023import java.sql.SQLException; 024import java.sql.Statement; 025import java.util.concurrent.atomic.AtomicLong; 026 027import javax.transaction.xa.XAResource; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.ecm.core.api.ConcurrentUpdateException; 032import org.nuxeo.ecm.core.api.NuxeoException; 033import org.nuxeo.ecm.core.storage.sql.Mapper.Identification; 034import org.nuxeo.ecm.core.storage.sql.Model; 035import org.nuxeo.ecm.core.storage.sql.jdbc.dialect.Dialect; 036import org.nuxeo.runtime.api.Framework; 037import org.nuxeo.runtime.datasource.ConnectionHelper; 038 039/** 040 * Holds a connection to a JDBC database. 041 */ 042public class JDBCConnection { 043 044 private static final Log log = LogFactory.getLog(JDBCConnection.class); 045 046 /** JDBC application name parameter for setClientInfo. */ 047 private static final String APPLICATION_NAME = "ApplicationName"; 048 049 private static final String SET_CLIENT_INFO_PROP = "org.nuxeo.vcs.setClientInfo"; 050 051 private static final String SET_CLIENT_INFO_DEFAULT = "false"; 052 053 /** The model used to do the mapping. */ 054 protected final Model model; 055 056 /** The SQL information. */ 057 protected final SQLInfo sqlInfo; 058 059 /** The dialect. */ 060 protected final Dialect dialect; 061 062 /** The actual connection. */ 063 public Connection connection; 064 065 protected boolean supportsBatchUpdates; 066 067 protected XAResource xaresource = new XAResourceConnectionAdapter(this); 068 069 /** Whether this connection must never be shared (long-lived). */ 070 protected final boolean noSharing; 071 072 // for tests 073 public boolean countExecutes; 074 075 // for tests 076 public int executeCount; 077 078 // for debug 079 private static final AtomicLong instanceCounter = new AtomicLong(0); 080 081 // for debug 082 private final long instanceNumber = instanceCounter.incrementAndGet(); 083 084 // for debug 085 public final JDBCLogger logger = new JDBCLogger(String.valueOf(instanceNumber)); 086 087 protected boolean setClientInfo; 088 089 /** 090 * Creates a new Mapper. 091 * 092 * @param model the model 093 * @param sqlInfo the sql info 094 * @param noSharing whether to use no-sharing mode for the connection 095 */ 096 public JDBCConnection(Model model, SQLInfo sqlInfo, boolean noSharing) { 097 this.model = model; 098 this.sqlInfo = sqlInfo; 099 this.noSharing = noSharing; 100 dialect = sqlInfo.dialect; 101 setClientInfo = Boolean.parseBoolean(Framework.getProperty(SET_CLIENT_INFO_PROP, SET_CLIENT_INFO_DEFAULT)); 102 } 103 104 /** 105 * for tests only 106 * 107 * @since 5.9.3 108 */ 109 public JDBCConnection() { 110 sqlInfo = null; 111 noSharing = false; 112 model = null; 113 dialect = null; 114 } 115 116 public String getRepositoryName() { 117 return model.getRepositoryDescriptor().name; 118 } 119 120 public Identification getIdentification() { 121 return new Identification(null, "" + instanceNumber); 122 } 123 124 protected void countExecute() { 125 if (countExecutes) { 126 executeCount++; 127 } 128 } 129 130 /** 131 * Gets the datasource to use for the given repository. 132 * 133 * @since 8.4 134 */ 135 public static String getDataSourceName(String repositoryName) { 136 return "repository_" + repositoryName; 137 } 138 139 protected void openConnections() { 140 try { 141 openBaseConnection(); 142 supportsBatchUpdates = connection.getMetaData().supportsBatchUpdates(); 143 dialect.performPostOpenStatements(connection); 144 } catch (SQLException cause) { 145 throw new NuxeoException("Cannot connect to database: " + getRepositoryName(), cause); 146 } 147 } 148 149 protected void openBaseConnection() throws SQLException { 150 String dataSourceName = getDataSourceName(getRepositoryName()); 151 connection = ConnectionHelper.getConnection(dataSourceName, noSharing); 152 if (setClientInfo) { 153 // log the mapper number (m=123) 154 connection.setClientInfo(APPLICATION_NAME, "nuxeo m=" + instanceNumber); 155 } 156 } 157 158 public void close() { 159 closeConnections(); 160 } 161 162 public void closeConnections() { 163 if (connection != null) { 164 try { 165 try { 166 if (setClientInfo) { 167 // connection will become idle in the pool 168 connection.setClientInfo(APPLICATION_NAME, "nuxeo"); 169 } 170 } finally { 171 connection.close(); 172 } 173 } catch (SQLException e) { 174 log.error(e, e); 175 } finally { 176 connection = null; 177 } 178 } 179 } 180 181 /** 182 * Checks the SQL error we got and determine if a concurrent update happened. Throws if that's the case. 183 * 184 * @param e the exception 185 * @since 5.8 186 */ 187 protected void checkConcurrentUpdate(Throwable e) throws ConcurrentUpdateException { 188 if (dialect.isConcurrentUpdateException(e)) { 189 throw new ConcurrentUpdateException(e); 190 } 191 } 192 193 protected void closeStatement(Statement s) throws SQLException { 194 try { 195 if (s != null) { 196 s.close(); 197 } 198 } catch (IllegalArgumentException e) { 199 // ignore 200 // http://bugs.mysql.com/35489 with JDBC 4 and driver <= 5.1.6 201 } 202 } 203 204 protected void closeStatement(Statement s, ResultSet r) throws SQLException { 205 try { 206 if (r != null) { 207 r.close(); 208 } 209 if (s != null) { 210 s.close(); 211 } 212 } catch (IllegalArgumentException e) { 213 // ignore 214 // http://bugs.mysql.com/35489 with JDBC 4 and driver <= 5.1.6 215 } 216 } 217 218}