/*
* (C) Copyright 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.
*/
package org.nuxeo.runtime.jtajca;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.resource.ResourceException;
import javax.resource.spi.ManagedConnection;
import org.apache.commons.logging.LogFactory;
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.ManagedConnectionInfo;
import org.tranql.connector.AbstractManagedConnection;
import org.tranql.connector.jdbc.ConnectionHandle;
/**
*
*
* @since 8.3
*/
public class NuxeoValidationSupport {
final Validation onBorrow;
final Validation onReturn;
NuxeoValidationSupport(Validation onBorrow, Validation onReturn) {
this.onBorrow = onBorrow == null ? NOOP : onBorrow;
this.onReturn = onReturn == null ? NOOP : onReturn;
}
public interface Validation {
boolean validate(ManagedConnection mc);
}
static final Validation NOOP = new Validation() {
@Override
public boolean validate(ManagedConnection mc) {
return true;
}
};
public static class ValidSQLConnection implements Validation {
@Override
public boolean validate(ManagedConnection mc) {
try {
@SuppressWarnings("unchecked")
AbstractManagedConnection<Connection, ConnectionHandle> jdbcManagedConnection = (AbstractManagedConnection<Connection, ConnectionHandle>) mc;
return jdbcManagedConnection.getPhysicalConnection().isValid(0);
} catch (SQLException cause) {
return false;
}
}
}
public static class QuerySQLConnection implements Validation {
final String sql;
QuerySQLConnection(String sql) {
this.sql = sql;
}
@Override
public boolean validate(ManagedConnection mc) {
@SuppressWarnings("unchecked")
AbstractManagedConnection<Connection, ConnectionHandle> jdbcManagedConnection = (AbstractManagedConnection<Connection, ConnectionHandle>) mc;
try (Statement statement = jdbcManagedConnection.getPhysicalConnection().createStatement()) {
return statement.execute(sql);
} catch (SQLException cause) {
LogFactory.getLog(QuerySQLConnection.class)
.warn(String.format("Caught error executing '%s', invalidating", sql), cause);
return false;
}
}
}
public ConnectionInterceptor addValidationInterceptors(ConnectionInterceptor stack) {
if (onBorrow == NOOP && onReturn == NOOP) {
return stack;
}
return new ValidationInterceptor(stack);
}
class ValidationInterceptor implements ConnectionInterceptor {
public ValidationInterceptor(ConnectionInterceptor next) {
this.next = next;
}
final ConnectionInterceptor next;
@Override
public void getConnection(ConnectionInfo ci) throws ResourceException {
while (true) {
// request for a connection
next.getConnection(ci);
// validate connection
if (onBorrow.validate(ci.getManagedConnectionInfo().getManagedConnection())) {
return;
}
// destroy invalid connection and retry
LogFactory.getLog(NuxeoValidationSupport.class).warn("Returning invalid connection " + ci);
returnConnection(ci, ConnectionReturnAction.DESTROY);
}
}
@Override
public void returnConnection(ConnectionInfo info, ConnectionReturnAction returnAction) {
if (returnAction == ConnectionReturnAction.RETURN_HANDLE) {
if (!onReturn.validate(info.getManagedConnectionInfo().getManagedConnection())) {
returnAction = ConnectionReturnAction.DESTROY;
}
}
try {
next.returnConnection(info, returnAction);
} finally {
if (returnAction == ConnectionReturnAction.DESTROY) {
// recycle managed connection info for a new managed connection
ManagedConnectionInfo mci = info.getManagedConnectionInfo();
mci = new ManagedConnectionInfo(mci.getManagedConnectionFactory(), mci.getConnectionRequestInfo());
info.setManagedConnectionInfo(mci);
}
}
}
@Override
public void info(StringBuilder s) {
next.info(s);
}
@Override
public void destroy() {
next.destroy();
}
}
}