/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.clb.proxy.outbound;
// Grizzly 1.0 APIs
import com.sun.enterprise.web.connector.grizzly.SelectorThread;
// Grillzy 1.x.x APIs
import com.sun.grizzly.CallbackHandler;
import com.sun.grizzly.ConnectorHandler;
import com.sun.grizzly.Controller;
import com.sun.grizzly.SelectorHandler;
import com.sun.grizzly.TCPConnectorHandler;
import com.sun.grizzly.TCPSelectorHandler;
import java.net.InetSocketAddress;
import org.jvnet.glassfish.comms.clb.proxy.ProxyRequestHandler;
import org.jvnet.glassfish.comms.clb.proxy.api.Endpoint;
import org.jvnet.glassfish.comms.clb.proxy.config.ProxyConfig;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.clb.proxy.outbound.connectioncache.HttpClbConnectionCache;
/**
* Manages the outbound connections.
* @author
*/
public class ConnectionManager {
/**
* Table for holding the proxy to backend instance mapprings.
*/
private ConcurrentHashMap<Endpoint, ConnectorHandler> proxyTobackend = new ConcurrentHashMap<Endpoint, ConnectorHandler>();
/**
* Table for holding the client to proxy channel mappring.
*/
private ConcurrentHashMap<SelectionKey, ProxyRequestHandler> clientToproxy = new ConcurrentHashMap<SelectionKey, ProxyRequestHandler>();
/**
* Cacheable pool of handlers.
*/
protected HttpClbConnectionCache cacheableHandlerPool;
/**
* Grizzly 15 controller.
*/
protected Controller controller;
/**
* Selector handler for this controller.
*/
private SelectorHandler selectorHandler;
/**
* Logger
*/
private Logger _logger = null;
private SelectorThread selectorThread = null;
private long wakeuptime = 0;
// in ms
private static long WAKEUP_TIME = 500000;
/** Creates a new instance of ConnectionManager */
public ConnectionManager() {
wakeuptime = System.nanoTime();
_logger = ProxyConfig.getInstance().getLogger();
}
public synchronized void cleanUpHandler(ConnectorHandler handler, Endpoint ep) {
Socket socket = null;
if (handler == null) {
return;
}
if (handler.getUnderlyingChannel() != null) {
socket = ((SocketChannel) handler.getUnderlyingChannel()).socket();
try {
socket.shutdownInput();
} catch (Exception e) {
;
}
try {
socket.shutdownOutput();
} catch (Exception e) {
;
}
try {
socket.close();
} catch (Exception e) {
;
}
}
cacheableHandlerPool.removeConnection(handler, ep);
}
public void setKeepAliveMaxConnections(long conn){
if (cacheableHandlerPool != null){
cacheableHandlerPool.setKeepAliveMaxConnections(conn);
}
}
public void setKeepAliveTimeout(int time){
if (cacheableHandlerPool != null){
cacheableHandlerPool.setKeepAliveTimeout(time);
}
}
/**
* Creates the handler pool and registers the controller.
*/
@SuppressWarnings("static-access")
public void createConnectionHandlerPool() {
controller = new Controller();
controller.setLogger(_logger);
controller.getPipeline().setName("http-proxy-outbound");
/*
* TODOD : Implement idle time out so that connections are
* closed after not being used for certain time.
*/
selectorHandler = new TCPSelectorHandler(true);
controller.setSelectorHandler(selectorHandler);
controller.setHandleReadWriteConcurrently(false);
cacheableHandlerPool = new HttpClbConnectionCache(
ProxyConfig.getInstance().getKeepAliveMaxConnections(),
ProxyConfig.getInstance().getKeepAliveIdleTimeout());
/**
* Have to move this thread creation part to the
* ConvergedProxy.
*/
try {
Thread t1 = new Thread(controller);
t1.start();
} catch (Exception e) {
_logger.log(Level.SEVERE, "ConnectionManager->Controller->", e);
}
}
public void destroyConnectionHandlerPool() {
// stop the controller
}
public ConnectorHandler getHandler(ProxyRequestHandler task) throws Exception{
// add code for caching and returning
return createHandler(task);
}
/**
* Creater a TCP client connection handler.
*/
private ConnectorHandler createHandler(ProxyRequestHandler task)
throws Exception {
Endpoint ep = task.getEndpoint();
SocketAddress remote = ep.getSocketAddress();
CallbackHandler callbackHandler = null;
ConnectorHandler connectorHandler =
cacheableHandlerPool.getConnection(ep);
if ((connectorHandler == null) ||
(connectorHandler.getUnderlyingChannel() == null) ||
(!connectorHandler.getUnderlyingChannel().isOpen())){
connectorHandler =
controller.acquireConnectorHandler(Controller.Protocol.TCP);
callbackHandler =
new DefaultCallBackHandler(connectorHandler, task);
((TCPConnectorHandler)connectorHandler).setConnectionTimeout(10*1000);
try {
if (ep.getBindAddress() == null){
connectorHandler.connect(remote, callbackHandler);
} else {
connectorHandler.connect(remote,
new InetSocketAddress(ep.getBindAddress(), 0), callbackHandler);
}
} catch (Exception ex) {
throw ex;
}
} else {
callbackHandler = connectorHandler.getCallbackHandler();
((DefaultCallBackHandler)callbackHandler).
refresh(connectorHandler, task);
}
if (_logger.isLoggable(Level.FINEST)){
_logger.log(Level.FINEST,
"clb.proxy.connectionmanager.handler_created",
connectorHandler.getUnderlyingChannel() + "Local :" +
((SocketChannel) connectorHandler.getUnderlyingChannel()).socket()
.getLocalAddress() + "Remote :" +
((SocketChannel) connectorHandler.getUnderlyingChannel()).socket()
.getRemoteSocketAddress() + " for " + task.getEndpoint());
}
return connectorHandler;
}
public void setSelectorThread(SelectorThread thread) {
this.selectorThread = thread;
}
public void registerServerEndpoint(SelectionKey key, ProxyRequestHandler beEndpoint) {
clientToproxy.put(key, beEndpoint);
}
public ProxyRequestHandler getServerEndpoint(SelectionKey key) {
return clientToproxy.get(key);
}
public void removeClientEndpoint(SelectionKey key) {
if (key != null){
clientToproxy.remove(key);
}
}
public void releaseConnection(ConnectorHandler handler,
Endpoint endpoint) {
if (handler != null && handler.getUnderlyingChannel() != null) {
if (!cacheableHandlerPool.releaseConnection(handler, endpoint)) {
cleanUpHandler(handler, endpoint);
}
} else {
cacheableHandlerPool.removeConnection(handler, endpoint);
}
}
public void cancelClientKey(SelectionKey key) {
if (key != null && key.isValid() && (selectorThread != null)) {
selectorThread.cancelKey(key);
}
}
private synchronized boolean shouldWakeUp(){
long currenttime = System.nanoTime();
if ((currenttime - wakeuptime) > WAKEUP_TIME){
wakeuptime = currenttime;
return true;
}
return false;
}
public void registerClientKey(SelectionKey key) {
if (key.isValid() && (selectorThread != null)) {
selectorThread.registerKey(key);
/* if (shouldWakeUp()) {
selectorThread.registerKey(key);
} else {
selectorThread.getKeysToEnable().add(key);
}*/
}
}
}