/**
* Copyright (c) 2009, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.apache.synapse.transport.passthru.connections;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.synapse.transport.passthru.SourceContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Keeps track of the connections coming in to the transport.
*/
public class SourceConnections {
private static Log log = LogFactory.getLog(SourceConnections.class);
/** The pool of connections in use */
private volatile List<NHttpServerConnection> busyConnections = new ArrayList<NHttpServerConnection>();
/** The pool of connections that are not being used */
private volatile List<NHttpServerConnection> freeConnections = new ArrayList<NHttpServerConnection>();
/** Lock for synchronizing the access to the pools */
private final Lock lock = new ReentrantLock();
/**
* Add a connection to the pool.
*
* @param conn connection to be added
*/
public void addConnection(NHttpServerConnection conn) {
lock.lock();
try {
freeConnections.add(conn);
} finally {
lock.unlock();
}
}
/**
* This method should be called when ever a connection being used for processing
* a request-response.
*
* @param conn the connection to be used
*/
public void useConnection(NHttpServerConnection conn) {
lock.lock();
try {
boolean freeConnection = freeConnections.remove(conn);
if (freeConnection) {
busyConnections.add(conn);
} else {
if (busyConnections.contains(conn)) {
throw new IllegalStateException("The connection is busy. " +
"Cannot use it for new request");
} else {
throw new IllegalStateException("Trying to use a connection " +
"which is not in free connections " + conn);
}
}
} finally {
lock.unlock();
}
}
/**
* This method should be called after a connection is being used for a request-response.
*
* @param conn the connection being used
*/
public void releaseConnection(NHttpServerConnection conn) {
lock.lock();
try {
SourceContext.get(conn).reset();
if (busyConnections.remove(conn)) {
freeConnections.add(conn);
} else {
if (log.isDebugEnabled()) {
log.debug("Trying to finish using a connection " +
"which is not in busy connections " + conn);
}
}
} finally {
lock.unlock();
}
}
/**
* Shutdown a connection
*
* @param conn the connection that needs to be shut down
*/
public void shutDownConnection(NHttpServerConnection conn) {
shutDownConnection(conn, false);
}
/**
* Shutdown a connection
*
* @param conn the connection that needs to be shut down
* @param isError whether an error is causing this shutdown of the connection
* It is very important to set this flag correctly.
* When an error causing the shutdown of the connections we should not
* release associated writer buffer to the pool as it might lead into
* situations like same buffer is getting released to both source and target
* buffer factories
*/
public void shutDownConnection(NHttpServerConnection conn, boolean isError) {
if (log.isDebugEnabled()) {
log.debug("Shutting down connection forcefully " + conn);
}
lock.lock();
try {
SourceContext.get(conn).reset(isError);
if (!busyConnections.remove(conn)) {
freeConnections.remove(conn);
}
try {
conn.shutdown();
} catch (IOException ignored) {
}
} finally {
lock.unlock();
}
}
/**
* Close a connection gracefully.
*
* @param conn the connection that needs to be closed.
*/
public void closeConnection(NHttpServerConnection conn) {
if (log.isDebugEnabled()) {
log.debug("Shutting down connection forcefully " + conn);
}
lock.lock();
try {
SourceContext.get(conn).reset();
if (!busyConnections.remove(conn)) {
freeConnections.remove(conn);
}
try {
conn.close();
} catch (IOException ignored) {
}
} finally {
lock.unlock();
}
}
public void destroy() {
for (NHttpServerConnection conn : freeConnections) {
shutDownConnection(conn);
}
// for all the busy connections we have to notify that their cannot
// be anymore requests over them
for (NHttpServerConnection conn : busyConnections) {
SourceContext.get(conn).setShutDown(true);
}
}
}