| /* |
| * 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. |
| */ |
| |
| /*** |
| * Java TelnetD library (embeddable telnet daemon) |
| * Copyright (c) 2000-2005 Dieter Wimberger |
| * All rights reserved. |
| * <p/> |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * <p/> |
| * Neither the name of the author nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * <p/> |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS |
| * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| ***/ |
| |
| package org.apache.felix.gogo.jline.telnet; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.net.Socket; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Stack; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * Class that takes care for active and queued connection. |
| * Housekeeping is done also for connections that were just broken |
| * off, or exceeded their timeout. |
| * |
| * @author Dieter Wimberger |
| * @version 2.0 (16/07/2006) |
| */ |
| public abstract class ConnectionManager implements Runnable { |
| |
| private static Logger LOG = Logger.getLogger(ConnectionManager.class.getName()); |
| private final List<Connection> openConnections; |
| private Thread thread; |
| private ThreadGroup threadGroup; //ThreadGroup all connections run in |
| private Stack<Connection> closedConnections; |
| private ConnectionFilter connectionFilter; //reference to the connection filter |
| private int maxConnections; //maximum allowed connections stored from the properties |
| private int warningTimeout; //time to idle warning |
| private int disconnectTimeout; //time to idle diconnection |
| private int housekeepingInterval; //interval for managing cleanups |
| private String loginShell; |
| private boolean lineMode = false; |
| private boolean stopping = false; |
| |
| public ConnectionManager() { |
| threadGroup = new ThreadGroup(toString() + "Connections"); |
| closedConnections = new Stack<Connection>(); |
| openConnections = Collections.synchronizedList(new ArrayList<Connection>(100)); |
| } |
| |
| public ConnectionManager(int con, int timew, int timedis, int hoke, ConnectionFilter filter, String lsh, boolean lm) { |
| this(); |
| connectionFilter = filter; |
| loginShell = lsh; |
| lineMode = lm; |
| maxConnections = con; |
| warningTimeout = timew; |
| disconnectTimeout = timedis; |
| housekeepingInterval = hoke; |
| }//constructor |
| |
| /** |
| * Gets the active ConnectionFilter instance or |
| * returns null if no filter is set. |
| * |
| * @return the managers ConnectionFilter. |
| */ |
| public ConnectionFilter getConnectionFilter() { |
| return connectionFilter; |
| }//getConnectionFilter |
| |
| /** |
| * Set a connection filter for this |
| * ConnectionManager instance. The filter is used to handle |
| * IP level allow/deny of incoming connections. |
| * |
| * @param filter ConnectionFilter instance. |
| */ |
| public void setConnectionFilter(ConnectionFilter filter) { |
| connectionFilter = filter; |
| }//setConnectionFilter |
| |
| /** |
| * Returns the number of open connections. |
| * @return the number of open connections as <tt>int</tt>. |
| */ |
| public int openConnectionCount() { |
| return openConnections.size(); |
| }//openConnectionCount |
| |
| /** |
| * Returns the {@link Connection} at the given index. |
| * @param idx |
| * @return |
| */ |
| public Connection getConnection(int idx) { |
| synchronized (openConnections) { |
| return openConnections.get(idx); |
| } |
| }//getConnection |
| |
| /** |
| * Get all {@link Connection} instances with the given |
| * <tt>InetAddress</tt>. |
| * |
| * @return all {@link Connection} instances with the given |
| * <tt>InetAddress</tt>. |
| */ |
| public Connection[] getConnectionsByAdddress(InetAddress addr) { |
| ArrayList<Connection> l = new ArrayList<Connection>(); |
| synchronized (openConnections) { |
| for (Connection connection : openConnections) { |
| if (connection.getConnectionData().getInetAddress().equals(addr)) { |
| l.add(connection); |
| } |
| } |
| } |
| Connection[] conns = new Connection[l.size()]; |
| return l.toArray(conns); |
| }//getConnectionsByAddress |
| |
| /** |
| * Starts this <tt>ConnectionManager</tt>. |
| */ |
| public void start() { |
| thread = new Thread(this); |
| thread.start(); |
| }//start |
| |
| /** |
| * Stops this <tt>ConnectionManager</tt>. |
| */ |
| public void stop() { |
| LOG.log(Level.FINE, "stop()::" + this.toString()); |
| stopping = true; |
| //wait for thread to die |
| try { |
| if (thread != null) { |
| thread.join(); |
| } |
| } catch (InterruptedException iex) { |
| LOG.log(Level.SEVERE, "stop()", iex); |
| } |
| synchronized (openConnections) { |
| for (Connection tc : openConnections) { |
| try { |
| //maybe write a disgrace to the socket? |
| tc.close(); |
| } catch (Exception exc) { |
| LOG.log(Level.SEVERE, "stop()", exc); |
| } |
| } |
| openConnections.clear(); |
| } |
| LOG.log(Level.FINE, "stop():: Stopped " + this.toString()); |
| }//stop |
| |
| /** |
| * Method that that tries to connect an incoming request. |
| * Properly queueing. |
| * |
| * @param insock Socket thats representing the incoming connection. |
| */ |
| public void makeConnection(Socket insock) { |
| LOG.log(Level.FINE, "makeConnection()::" + insock.toString()); |
| if (connectionFilter == null || connectionFilter.isAllowed(insock.getInetAddress())) { |
| //we create the connection data object at this point to |
| //store certain information there. |
| ConnectionData newCD = new ConnectionData(insock, this); |
| newCD.setLoginShell(loginShell); |
| newCD.setLineMode(lineMode); |
| if (openConnections.size() < maxConnections) { |
| //create a new Connection instance |
| Connection con = createConnection(threadGroup, newCD); |
| //log the newly created connection |
| Object[] args = {openConnections.size() + 1}; |
| LOG.info(MessageFormat.format("connection #{0,number,integer} made.", args)); |
| //register it for being managed |
| synchronized (openConnections) { |
| openConnections.add(con); |
| } |
| //start it |
| con.start(); |
| } |
| } else { |
| LOG.info("makeConnection():: Active Filter blocked incoming connection."); |
| try { |
| insock.close(); |
| } catch (IOException ex) { |
| //do nothing or log. |
| } |
| } |
| }//makeConnection |
| |
| protected abstract Connection createConnection(ThreadGroup threadGroup, ConnectionData newCD); |
| |
| /** |
| * Periodically does following work: |
| * <ul> |
| * <li> cleaning up died connections. |
| * <li> checking managed connections if they are working properly. |
| * <li> checking the open connections. |
| * </ul> |
| */ |
| public void run() { |
| //housekeep connections |
| try { |
| do { |
| //clean up and close all broken connections |
| //cleanupBroken(); |
| //clean up closed connections |
| cleanupClosed(); |
| //check all active connections |
| checkOpenConnections(); |
| //sleep interval |
| Thread.sleep(housekeepingInterval); |
| } while (!stopping); |
| |
| } catch (Exception e) { |
| LOG.log(Level.SEVERE, "run()", e); |
| } |
| LOG.log(Level.FINE, "run():: Ran out " + this.toString()); |
| }//run |
| |
| /* |
| private void cleanupBroken() { |
| //cleanup loop |
| while (!m_BrokenConnections.isEmpty()) { |
| Connection nextOne = (Connection) m_BrokenConnections.pop(); |
| log.info("cleanupBroken():: Closing broken connection " + nextOne.toString()); |
| //fire logoff event for shell site cleanup , beware could hog the daemon thread |
| nextOne.processConnectionEvent(new ConnectionEvent(nextOne, ConnectionEvent.CONNECTION_BROKEN)); |
| //close the connection, will be automatically registered as closed |
| nextOne.close(); |
| } |
| }//cleanupBroken |
| */ |
| private void cleanupClosed() { |
| if (stopping) { |
| return; |
| } |
| //cleanup loop |
| while (!closedConnections.isEmpty()) { |
| Connection nextOne = closedConnections.pop(); |
| LOG.info("cleanupClosed():: Removing closed connection " + nextOne.toString()); |
| synchronized (openConnections) { |
| openConnections.remove(nextOne); |
| } |
| } |
| }//cleanupBroken |
| |
| private void checkOpenConnections() { |
| if (stopping) { |
| return; |
| } |
| //do routine checks on active connections |
| synchronized (openConnections) { |
| for (Connection conn : openConnections) { |
| ConnectionData cd = conn.getConnectionData(); |
| //check if it is dead and remove it. |
| if (!conn.isActive()) { |
| registerClosedConnection(conn); |
| continue; |
| } |
| /* Timeouts check */ |
| //first we caculate the inactivity time |
| long inactivity = System.currentTimeMillis() - cd.getLastActivity(); |
| //now we check for warning and disconnection |
| if (inactivity > warningTimeout) { |
| //..and for disconnect |
| if (inactivity > (disconnectTimeout + warningTimeout)) { |
| //this connection needs to be disconnected :) |
| LOG.log(Level.FINE, "checkOpenConnections():" + conn.toString() + " exceeded total timeout."); |
| //fire logoff event for shell site cleanup , beware could hog the daemon thread |
| conn.processConnectionEvent(new ConnectionEvent(conn, ConnectionEvent.Type.CONNECTION_TIMEDOUT)); |
| //conn.close(); |
| } else { |
| //this connection needs to be warned :) |
| if (!cd.isWarned()) { |
| LOG.log(Level.FINE, "checkOpenConnections():" + conn.toString() + " exceeded warning timeout."); |
| cd.setWarned(true); |
| //warning event is fired but beware this could hog the daemon thread!! |
| conn.processConnectionEvent(new ConnectionEvent(conn, ConnectionEvent.Type.CONNECTION_IDLE)); |
| } |
| } |
| } |
| } |
| /* end Timeouts check */ |
| } |
| }//checkConnections |
| |
| public void registerClosedConnection(Connection con) { |
| if (stopping) { |
| return; |
| } |
| if (!closedConnections.contains(con)) { |
| LOG.log(Level.FINE, "registerClosedConnection()::" + con.toString()); |
| closedConnections.push(con); |
| } |
| }//unregister |
| |
| public int getDisconnectTimeout() { |
| return disconnectTimeout; |
| } |
| |
| public void setDisconnectTimeout(int disconnectTimeout) { |
| this.disconnectTimeout = disconnectTimeout; |
| } |
| |
| public int getHousekeepingInterval() { |
| return housekeepingInterval; |
| } |
| |
| public void setHousekeepingInterval(int housekeepingInterval) { |
| this.housekeepingInterval = housekeepingInterval; |
| } |
| |
| public boolean isLineMode() { |
| return lineMode; |
| } |
| |
| public void setLineMode(boolean lineMode) { |
| this.lineMode = lineMode; |
| } |
| |
| public String getLoginShell() { |
| return loginShell; |
| } |
| |
| public void setLoginShell(String loginShell) { |
| this.loginShell = loginShell; |
| } |
| |
| public int getMaxConnections() { |
| return maxConnections; |
| } |
| |
| public void setMaxConnections(int maxConnections) { |
| this.maxConnections = maxConnections; |
| } |
| |
| public int getWarningTimeout() { |
| return warningTimeout; |
| } |
| |
| public void setWarningTimeout(int warningTimeout) { |
| this.warningTimeout = warningTimeout; |
| } |
| |
| }//class ConnectionManager |