/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.aries.transaction.jdbc.internal; import org.apache.geronimo.connector.outbound.AbstractSinglePoolConnectionInterceptor; import org.apache.geronimo.connector.outbound.ConnectionInfo; import org.apache.geronimo.connector.outbound.ConnectionInterceptor; import org.apache.geronimo.connector.outbound.ConnectionReturnAction; import org.apache.geronimo.connector.outbound.GenericConnectionManager; import org.apache.geronimo.connector.outbound.ManagedConnectionInfo; import org.apache.geronimo.connector.outbound.MultiPoolConnectionInterceptor; import org.apache.geronimo.connector.outbound.SinglePoolConnectionInterceptor; import org.apache.geronimo.connector.outbound.SinglePoolMatchAllConnectionInterceptor; import org.apache.geronimo.connector.outbound.SubjectSource; import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PoolingSupport; import org.apache.geronimo.connector.outbound.connectionmanagerconfig.TransactionSupport; import org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTracker; import org.apache.geronimo.transaction.manager.RecoverableTransactionManager; import javax.resource.ResourceException; import javax.resource.spi.ManagedConnection; import javax.resource.spi.ManagedConnectionFactory; import javax.resource.spi.ValidatingManagedConnectionFactory; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.locks.ReadWriteLock; @SuppressWarnings({ "unchecked", "serial" }) public final class ValidatingGenericConnectionManager extends GenericConnectionManager { private static final Timer TIMER = new Timer("ValidatingGenericConnectionManagerTimer", true); private transient final TimerTask validatingTask; private final long validatingInterval; private final ReadWriteLock lock; private final Object pool; public ValidatingGenericConnectionManager(TransactionSupport transactionSupport, PoolingSupport pooling, SubjectSource subjectSource, ConnectionTracker connectionTracker, RecoverableTransactionManager transactionManager, ManagedConnectionFactory mcf, String name, ClassLoader classLoader, long interval) { super(transactionSupport, pooling, subjectSource, connectionTracker, transactionManager, mcf, name, classLoader); validatingInterval = interval; ConnectionInterceptor stack = interceptors.getStack(); ReadWriteLock foundLock = null; ConnectionInterceptor current = stack; do { if (current instanceof AbstractSinglePoolConnectionInterceptor) { try { foundLock = (ReadWriteLock) Reflections.get(current, "resizeLock"); } catch (Exception e) { // no-op } break; } // look next try { current = (ConnectionInterceptor) Reflections.get(current, "next"); } catch (Exception e) { current = null; } } while (current != null); this.lock = foundLock; Object foundPool = null; if (current instanceof AbstractSinglePoolConnectionInterceptor) { foundPool = Reflections.get(current, "pool"); } else if (current instanceof MultiPoolConnectionInterceptor) { log.warn("validation on stack {} not supported", stack); } this.pool = foundPool; if (pool != null) { validatingTask = new ValidatingTask(current, lock, pool); } else { validatingTask = null; } } @Override public void doStart() throws Exception { super.doStart(); if (validatingTask != null) { TIMER.schedule(validatingTask, validatingInterval, validatingInterval); } } @Override public void doStop() throws Exception { if (validatingTask != null) { validatingTask.cancel(); } super.doStop(); } private class ValidatingTask extends TimerTask { private final ConnectionInterceptor stack; private final ReadWriteLock lock; private final Object pool; public ValidatingTask(ConnectionInterceptor stack, ReadWriteLock lock, Object pool) { this.stack = stack; this.lock = lock; this.pool = pool; } @Override public void run() { if (lock != null) { lock.writeLock().lock(); } try { final Map<ManagedConnection, ManagedConnectionInfo> connections; if (stack instanceof SinglePoolConnectionInterceptor) { connections = new HashMap<ManagedConnection, ManagedConnectionInfo>(); for (ManagedConnectionInfo info : (List<ManagedConnectionInfo>) pool) { connections.put(info.getManagedConnection(), info); } } else if (stack instanceof SinglePoolMatchAllConnectionInterceptor) { connections = (Map<ManagedConnection, ManagedConnectionInfo>) pool; } else { log.warn("stack {} currently not supported", stack); return; } // destroy invalid connections try { Set<ManagedConnection> invalids = ValidatingManagedConnectionFactory.class.cast(getManagedConnectionFactory()).getInvalidConnections(connections.keySet()); if (invalids != null) { for (ManagedConnection invalid : invalids) { stack.returnConnection(new ConnectionInfo(connections.get(invalid)), ConnectionReturnAction.DESTROY); } } } catch (ResourceException e) { log.error(e.getMessage(), e); } } finally { if (lock != null) { lock.writeLock().unlock(); } } } } }