/* * 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.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * Class that implements a connection with this telnet daemon.<br> * It is derived from java.lang.Thread, which reflects the architecture * constraint of one thread per connection. This might seem a waste of * resources, but as a matter of fact sharing threads would require a * far more complex imlementation, due to the fact that telnet is not a * stateless protocol (i.e. alive throughout a session of multiple requests * and responses).<br> * Each Connection instance is created by the listeners ConnectionManager * instance, making it part of a threadgroup and passing in an associated * ConnectionData instance, that holds vital information about the connection. * Be sure to take a look at their documention.<br> * <p/> * Once the thread has started and is running, it will get a login * shell instance from the ShellManager and run passing its own reference. * * @author Dieter Wimberger * @version 2.0 (16/07/2006) * @see ConnectionManager * @see ConnectionData */ public abstract class Connection extends Thread { private static final Logger LOG = Logger.getLogger(Connection.class.getName()); private static int number; //unique number for a thread in the thread group private boolean dead; private List<ConnectionListener> listeners; //Associations private ConnectionData connectionData; //associated information /** * Constructs a TelnetConnection by invoking its parent constructor * and setting of various members.<br> * Subsequently instantiates the whole i/o subsystem, negotiating * telnet protocol level options etc.<br> * * @param tcg ThreadGroup that this instance is running in. * @param cd ConnectionData instance containing all vital information * of this connection. * @see ConnectionData */ public Connection(ThreadGroup tcg, ConnectionData cd) { super(tcg, ("Connection" + (++number))); connectionData = cd; //init the connection listeners for events //(there should actually be only one or two) listeners = new CopyOnWriteArrayList<ConnectionListener>(); dead = false; }//constructor /** * Method overloaded to implement following behaviour: * <ol> * <li> On first entry, retrieve an instance of the configured * login shell from the ShellManager and run it. * <li> Handle a shell switch or close down disgracefully when * problems (i.e. unhandled unchecked exceptions) occur in the * running shell. * </ol> */ public void run() { try { doRun(); } catch (Exception ex) { LOG.log(Level.SEVERE, "run()", ex); //Handle properly } finally { //call close if not dead already if (!dead) { close(); } } LOG.log(Level.FINE, "run():: Returning from " + this.toString()); }//run protected abstract void doRun() throws Exception; protected abstract void doClose() throws Exception; /** * Method to access the associated connection data. * * @return ConnectionData associated with the Connection instance. * @see ConnectionData */ public ConnectionData getConnectionData() { return connectionData; }//getConnectionData /** * Closes the connection and its underlying i/o and network * resources.<br> */ public synchronized void close() { if (dead) { return; } else { try { //connection dead dead = true; //close i/o doClose(); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } try { //close socket connectionData.getSocket().close(); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } try { //register closed connection in ConnectionManager connectionData.getManager().registerClosedConnection(this); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } try { //try to interrupt it interrupt(); } catch (Exception ex) { LOG.log(Level.SEVERE, "close()", ex); //handle } LOG.log(Level.FINE, "Closed " + this.toString() + " and inactive."); } }//close /** * Returns if a connection has been closed.<br> * * @return the state of the connection. */ public boolean isActive() { return !dead; }//isClosed /****** Event handling ****************/ /** * Method that registers a ConnectionListener with the * Connection instance. * * @param cl ConnectionListener to be registered. * @see ConnectionListener */ public void addConnectionListener(ConnectionListener cl) { listeners.add(cl); }//addConnectionListener /** * Method that removes a ConnectionListener from the * Connection instance. * * @param cl ConnectionListener to be removed. * @see ConnectionListener */ public void removeConnectionListener(ConnectionListener cl) { listeners.remove(cl); }//removeConnectionListener /** * Method called by the io subsystem to pass on a * "low-level" event. It will be properly delegated to * all registered listeners. * * @param ce ConnectionEvent to be processed. * @see ConnectionEvent */ public void processConnectionEvent(ConnectionEvent ce) { for (ConnectionListener cl : listeners) { switch (ce.getType()) { case CONNECTION_IDLE: cl.connectionIdle(ce); break; case CONNECTION_TIMEDOUT: cl.connectionTimedOut(ce); break; case CONNECTION_LOGOUTREQUEST: cl.connectionLogoutRequest(ce); break; case CONNECTION_BREAK: cl.connectionSentBreak(ce); break; case CONNECTION_TERMINAL_GEOMETRY_CHANGED: cl.connectionTerminalGeometryChanged(ce); } } }//processConnectionEvent }//class Connection