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.ConnectionRequestInfo; 025import javax.resource.spi.ManagedConnectionFactory; 026 027import org.apache.commons.logging.LogFactory; 028import org.apache.geronimo.connector.outbound.ConnectionInfo; 029import org.apache.geronimo.connector.outbound.ConnectionInterceptor; 030import org.apache.geronimo.connector.outbound.ConnectionReturnAction; 031import org.apache.geronimo.connector.outbound.ManagedConnectionInfo; 032 033/** 034 * 035 * 036 * @since 8.3 037 */ 038public class NuxeoValidationSupport { 039 040 final Validation onBorrow; 041 042 final Validation onReturn; 043 044 NuxeoValidationSupport(Validation onBorrow, Validation onReturn) { 045 this.onBorrow = onBorrow == null ? NOOP : onBorrow; 046 this.onReturn = onReturn == null ? NOOP : onReturn; 047 } 048 049 interface Validation { 050 boolean validate(Object handle); 051 052 } 053 054 static final Validation NOOP = new Validation() { 055 @Override 056 public boolean validate(Object handle) { 057 return true; 058 } 059 }; 060 061 static class ValidSQLConnection implements Validation { 062 @Override 063 public boolean validate(Object handle) { 064 try { 065 return ((Connection) handle).isValid(0); 066 } catch (SQLException cause) { 067 return false; 068 } 069 } 070 } 071 072 static class QuerySQLConnection implements Validation { 073 final String sql; 074 075 QuerySQLConnection(String sql) { 076 this.sql = sql; 077 } 078 079 @Override 080 public boolean validate(Object handle) { 081 try (Statement statement = ((Connection) handle).unwrap(Connection.class).createStatement()) { 082 return statement.execute(sql); 083 } catch (SQLException cause) { 084 LogFactory.getLog(QuerySQLConnection.class).warn(String.format("Caught error executing '%s', invalidating", sql), cause); 085 return false; 086 } 087 } 088 } 089 090 public ConnectionInterceptor addTransactionInterceptor(ConnectionInterceptor stack) { 091 if (onBorrow == NOOP && onReturn == NOOP) { 092 return stack; 093 } 094 return new ValidationHandleInterceptor(stack); 095 } 096 097 class ValidationHandleInterceptor implements ConnectionInterceptor { 098 099 public ValidationHandleInterceptor(ConnectionInterceptor next) { 100 this.next = next; 101 } 102 103 final ConnectionInterceptor next; 104 105 @Override 106 public void getConnection(ConnectionInfo ci) throws ResourceException { 107 ManagedConnectionInfo mci = ci.getManagedConnectionInfo(); 108 ManagedConnectionFactory mcf = mci.getManagedConnectionFactory(); 109 ConnectionRequestInfo cri = mci.getConnectionRequestInfo(); 110 while (true) { 111 // request for a connection 112 ConnectionInfo tryee = new ConnectionInfo(new ManagedConnectionInfo(mcf, cri)); 113 next.getConnection(tryee); 114 // validate connection 115 Object handle = tryee.getConnectionProxy(); 116 if (handle == null) { 117 handle = tryee.getConnectionHandle(); 118 } 119 if (onBorrow.validate(handle)) { 120 // save handle an return connection 121 if (tryee.getConnectionProxy() != null) { 122 ci.setConnectionProxy(handle); 123 } else { 124 ci.setConnectionHandle(handle); 125 } 126 return; 127 } 128 // destroy invalid connection and retry 129 LogFactory.getLog(NuxeoValidationSupport.class).warn("Returning invalid connection " + tryee); 130 returnConnection(tryee, ConnectionReturnAction.DESTROY); 131 } 132 } 133 134 @Override 135 public void returnConnection(ConnectionInfo info, ConnectionReturnAction returnAction) { 136 if (returnAction == ConnectionReturnAction.RETURN_HANDLE) { 137 if (!onReturn.validate(info.getConnectionHandle())) { 138 returnAction = ConnectionReturnAction.DESTROY; 139 } 140 } 141 next.returnConnection(info, returnAction); 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}