001/* 002 * (C) Copyright 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 017package org.nuxeo.runtime.jtajca; 018 019import java.sql.Connection; 020import java.sql.SQLException; 021import java.sql.Statement; 022 023import javax.resource.ResourceException; 024import javax.resource.spi.ManagedConnection; 025 026import org.apache.commons.logging.LogFactory; 027import org.apache.geronimo.connector.outbound.ConnectionInfo; 028import org.apache.geronimo.connector.outbound.ConnectionInterceptor; 029import org.apache.geronimo.connector.outbound.ConnectionReturnAction; 030import org.apache.geronimo.connector.outbound.ManagedConnectionInfo; 031import org.tranql.connector.AbstractManagedConnection; 032import org.tranql.connector.jdbc.ConnectionHandle; 033 034/** 035 * 036 * 037 * @since 8.3 038 */ 039public class NuxeoValidationSupport { 040 041 final Validation onBorrow; 042 043 final Validation onReturn; 044 045 NuxeoValidationSupport(Validation onBorrow, Validation onReturn) { 046 this.onBorrow = onBorrow == null ? NOOP : onBorrow; 047 this.onReturn = onReturn == null ? NOOP : onReturn; 048 } 049 050 public interface Validation { 051 boolean validate(ManagedConnection mc); 052 } 053 054 static final Validation NOOP = new Validation() { 055 @Override 056 public boolean validate(ManagedConnection mc) { 057 return true; 058 } 059 }; 060 061 public static class ValidSQLConnection implements Validation { 062 @Override 063 public boolean validate(ManagedConnection mc) { 064 try { 065 @SuppressWarnings("unchecked") 066 AbstractManagedConnection<Connection, ConnectionHandle> jdbcManagedConnection = (AbstractManagedConnection<Connection, ConnectionHandle>) mc; 067 return jdbcManagedConnection.getPhysicalConnection().isValid(0); 068 } catch (SQLException cause) { 069 return false; 070 } 071 } 072 } 073 074 public static class QuerySQLConnection implements Validation { 075 final String sql; 076 077 QuerySQLConnection(String sql) { 078 this.sql = sql; 079 } 080 081 @Override 082 public boolean validate(ManagedConnection mc) { 083 @SuppressWarnings("unchecked") 084 AbstractManagedConnection<Connection, ConnectionHandle> jdbcManagedConnection = (AbstractManagedConnection<Connection, ConnectionHandle>) mc; 085 try (Statement statement = jdbcManagedConnection.getPhysicalConnection().createStatement()) { 086 return statement.execute(sql); 087 } catch (SQLException cause) { 088 LogFactory.getLog(QuerySQLConnection.class) 089 .warn(String.format("Caught error executing '%s', invalidating", sql), cause); 090 return false; 091 } 092 } 093 } 094 095 public ConnectionInterceptor addValidationInterceptors(ConnectionInterceptor stack) { 096 if (onBorrow == NOOP && onReturn == NOOP) { 097 return stack; 098 } 099 return new ValidationInterceptor(stack); 100 } 101 102 class ValidationInterceptor implements ConnectionInterceptor { 103 104 public ValidationInterceptor(ConnectionInterceptor next) { 105 this.next = next; 106 } 107 108 final ConnectionInterceptor next; 109 110 @Override 111 public void getConnection(ConnectionInfo ci) throws ResourceException { 112 while (true) { 113 // request for a connection 114 next.getConnection(ci); 115 // validate connection 116 if (onBorrow.validate(ci.getManagedConnectionInfo().getManagedConnection())) { 117 return; 118 } 119 // destroy invalid connection and retry 120 LogFactory.getLog(NuxeoValidationSupport.class).warn("Returning invalid connection " + ci); 121 returnConnection(ci, ConnectionReturnAction.DESTROY); 122 } 123 } 124 125 @Override 126 public void returnConnection(ConnectionInfo info, ConnectionReturnAction returnAction) { 127 if (returnAction == ConnectionReturnAction.RETURN_HANDLE) { 128 if (!onReturn.validate(info.getManagedConnectionInfo().getManagedConnection())) { 129 returnAction = ConnectionReturnAction.DESTROY; 130 } 131 } 132 try { 133 next.returnConnection(info, returnAction); 134 } finally { 135 if (returnAction == ConnectionReturnAction.DESTROY) { 136 // recycle managed connection info for a new managed connection 137 ManagedConnectionInfo mci = info.getManagedConnectionInfo(); 138 mci = new ManagedConnectionInfo(mci.getManagedConnectionFactory(), mci.getConnectionRequestInfo()); 139 info.setManagedConnectionInfo(mci); 140 } 141 } 142 } 143 144 @Override 145 public void info(StringBuilder s) { 146 next.info(s); 147 } 148 149 @Override 150 public void destroy() { 151 next.destroy(); 152 } 153 154 } 155 156}