/* * Copyright 2013 Cloud4SOA, www.cloud4soa.eu * * 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. */ /* * Copyright 2009-2012 the original author or authors. * * 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.cloudfoundry.caldecott.client; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cloudfoundry.caldecott.TunnelException; import org.springframework.core.task.TaskExecutor; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.Observable; import java.util.Observer; import java.util.concurrent.ConcurrentHashMap; /** * The class responsible for listening for client connection attempts and handing off to * a TunnelHandler for handling the actual tunneling communications. * * @author Thomas Risberg */ public class TunnelAcceptor implements Runnable { public static final int SOCKET_TIMEOUT = 10000; protected final Log logger = LogFactory.getLog(getClass()); // configuration options private final TunnelFactory tunnelFactory; private final ServerSocket serverSocket; private final TaskExecutor taskExecutor; // variable to keep acceptor active // this is volatile since it can we altered by another thread via stop() private volatile boolean keepGoing = true; // concurrent map to keep track of handlers, will be modified from handler threads private ConcurrentHashMap<TunnelHandler, Boolean> handlers = new ConcurrentHashMap<TunnelHandler, Boolean>(); public TunnelAcceptor(ServerSocket serverSocket, TunnelFactory tunnelFactory, TaskExecutor taskExecutor) { this.serverSocket = serverSocket; this.tunnelFactory = tunnelFactory; this.taskExecutor = taskExecutor; try { this.serverSocket.setSoTimeout(SOCKET_TIMEOUT); } catch (SocketException ignore) {} } public void start() { logger.info("Starting new acceptor thread: " + this); taskExecutor.execute(this); logger.debug("Completed start of: " + taskExecutor); } public boolean isActive() { if (handlers.size() > 0) { return true; } else { return false; } } public void stop() { logger.info("Stop requested for: " + this); keepGoing = false; } public void run() { while (keepGoing) { try { logger.trace("Waiting for client connection"); Socket sourceSocket = serverSocket.accept(); logger.debug("Accepted client connection"); TunnelHandler handler = new TunnelHandler(sourceSocket, tunnelFactory, taskExecutor); handler.addObserver(new Observer() { public void update(Observable observable, Object o) { if (logger.isDebugEnabled()) { logger.debug("Notified that " + observable + " is now " + o); } handlers.remove(observable); } }); handlers.put(handler, true); handler.start(); } catch (SocketTimeoutException ste) {} catch (IOException e) { if (!keepGoing && serverSocket.isClosed()) { // time to quit so we can ignore this exception } else { throw new TunnelException("Error while accepting connections", e); } } } if (!handlers.isEmpty()) { while (!handlers.isEmpty()) { logger.debug("Waiting for " + handlers.size() + " client connections to close"); for (TunnelHandler handler : handlers.keySet()) { logger.debug("Poking " + handler); handler.poke(); } try { Thread.sleep(10000); } catch (InterruptedException ignore) {} } } logger.info("Completed acceptor thread for: " + this); } }