| /* |
| * 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<>(); |
| 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) { |
| 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 |