All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.headlessintrace.client.connection.ConnectionDetail Maven / Gradle / Ivy

package org.headlessintrace.client.connection;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import org.headlessintrace.client.DefaultFactory;
import org.headlessintrace.client.connection.NetworkDataReceiverThread2.INetworkOutputConfig;
import org.headlessintrace.client.connection.command.IAgentCommand;
import org.headlessintrace.client.gui.helper.ControlConnectionThread;
import org.headlessintrace.client.gui.helper.TraceFactory;
import org.headlessintrace.client.gui.helper.ParsedSettingsData;
import org.headlessintrace.client.gui.helper.ControlConnectionThread.IControlConnectionListener;
import org.headlessintrace.client.model.BeanTraceEventImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class manages the control and trace threads for a single connection.
 * Theoretically, there is a one-to-many relationship between this class an a UI window displaying text/graph data from this connection. 
 * This holds true in practice with IConnectionStateCallbackHandlers.
 * In other words, there is one ConnectionController to many IConnectionStateCallbackHandlers.
 * You'll notice, however, that there is not a similar one-to-many for TraceEvents.
 * Instead, window have NatTable grids, and each grid for this connection "listens" to changes in this.traceEvents.
 * See NatTable doc for details on how this is done. 
 * It also performs the following tasks with the connection control thread:
 * 
    *
  • maintains the reference to the thread
  • *
  • starts/initializes it
  • *
  • stops it
  • *
  • keeps up-to-date on the connection status
  • *
* * Much of the initial code was performed by the InTraceUI class. * * There are two ways to filter the list of events. * 1) Take the EventList field of this class (getTraceEvents) and use it to construct a NatTable, * as shown in the NatTable example here: C:\src\jdist\natTable\2.3.2\example (net.sourceforge.nattable.examples.examples._101_Data.Using_the_ListDataProvider_MT) * The filter should consider two pieces for include/exclude: * a) The window's list of classes * b) The window's list of filters * 2) For Windows not using NatTable (Gantt, aggregation chargs), need to create a FilteredDispatchClass * that subscribes to all events here: * getTraceEvents().addListEventListener(listChangeListener) * ...but only dispatches those events that agree with the a) and b) data items detailed above. * * @author Erik Ostermueller * @place San Francisco * @date May 1, 2012 * */ public class ConnectionDetail implements IControlConnectionListener, IConnectionStateCallback { //private static final Logger LOG = Logger.getLogger( ConnectionDetail.class.getName() ); private static final Logger LOG = LoggerFactory.getLogger(ConnectionDetail.class); // Settings private List m_connCallbacks = new CopyOnWriteArrayList(); private ConnectState m_connectState = ConnectState.DISCONNECTED; /** * Commands that the user would like executed at startup time. */ private IAgentCommand[] m_startupCommands = null; private ParsedSettingsData m_settingsData = new ParsedSettingsData( new HashMap()); // Network details private InetAddress m_remoteAddress; private int port = HostPort.UNINITIALIZED_PORT; public ConnectState getConnectState() { return m_connectState; } /** * Immediately after the connection is first established, execute all the commands in this.m_startupCommands. * This method concatenates the commands of all those given in the array and attempts to execute them * in a single round-trip call. */ void executeStartupCommands() { if (m_startupCommands !=null) { StringBuilder sb = new StringBuilder(); for (IAgentCommand cmd : m_startupCommands) { sb.append(cmd.getMessage()); } getControlThread().sendMessage(sb.toString()); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("getConnectState[" + getConnectState() + "]\n"); sb.append("isConnected [" + this.isConnected() + "]\n"); sb.append("getConnCallbacks.size [" + getConnCallbacks().size() + "]"); sb.append("Connection state [" + this.m_connectState + "]\n"); if (m_remoteAddress!=null) { sb.append(m_remoteAddress.getCanonicalHostName()).append("-->CanonicalHostName\n"); sb.append(m_remoteAddress.getHostAddress()).append("-->HostAddress\n"); sb.append(m_remoteAddress.isLinkLocalAddress()).append("-->isLinkLocal\n"); } if (getControlThread()!=null) sb.append("control thread [" + getControlThread().toString() + "]\n"); if (this.getNetworkTraceThread2()!=null) sb.append("trace thread [" + this.getNetworkTraceThread2().toString() + "]\n"); return sb.toString(); } // Threads private NetworkDataReceiverThread2 m_networkTraceThread2 = null; private ControlConnectionThread m_controlThread; /** * Multiple NatTable windows will listen for events being added to this single list for this single connection. * CopyOnWriteArrayList would NOT work well, because the number of events could grow large....don't want to expend the * overhead of repeatedly copying a list that is frequently added to. */ private EventList m_traceEvents = new BasicEventList(); /** * @todo: Delete activity to all listening windows. */ @Override public void setProgress(Map progress) { // TODO Multiplex to listening windows. for(IConnectionStateCallback cb : this.m_connCallbacks) cb.setProgress(progress); } /** * @todo: Delete activity to all listening windows. */ @Override public void setStatus(Map progress) { // TODO Multiplex to listening windows. for(IConnectionStateCallback cb : this.m_connCallbacks) cb.setStatus(progress); } /** * @todo: Delegate activity to all listening windows. */ @Override public void setConfig(Map settingsMap) { m_settingsData = new ParsedSettingsData(settingsMap); LOG.debug("Client got updated config from server. gzip [" + m_settingsData.gzipEnabled + "] all settings[" + m_settingsData.toString() + "]"); for(IConnectionStateCallback cb : this.m_connCallbacks) cb.setConfig(settingsMap); } /** * TODO: Would really like to make this private, but cannot because it is public in the super. * Why do I want this to be private? Because the disconnect should only happen when _all_ windows have finished using the connection, * not just when a single window disconnects. * * This should only used when a problem has been detected with the connection. * If you must requests a disconnect, please use ConnectionList.disconnect() */ @Override public void disconnect() { if (getNetworkTraceThread2()!=null) getNetworkTraceThread2().requestDisconnect(); //throw new UnsupportedOperationException(ClientStrings.HARD_DISSCONNECT_NO_LONGER_SUPPORTED); //ControlConnectionThread#run is calling the above method, so can't throw the above exception quite yet. unHappyDisconnect(); } void unHappyDisconnect() { if (m_controlThread != null) { m_controlThread.disconnect(); } if (m_networkTraceThread2 != null) { m_networkTraceThread2.disconnect(); } setConnectState(ConnectState.DISCONNECTED); } public boolean isConnected() { return (getConnectState().equals(ConnectState.CONNECTED)); } /** * The following isHealthy() looks like good code, but not sure if I need it yet. */ // /** // * TODO: ask Martin whether there is a better way to implement a health check. // * @return // */ // public boolean isHealthy() { // boolean ynHealthy = false; //assume that connection is bad. // m_controlThread.sendMessage("[out-network"); // String networkTracePortStr = m_controlThread.getMessage(); // int networkTracePort = -1; // try { // networkTracePort = Integer.parseInt(networkTracePortStr); // if (networkTracePort > 0) { // ynHealthy = true; // } // } catch (Exception e) { // ynHealthy = false; // } // return ynHealthy; // } //TODO ? // /** // * // * @return Returns null if IP was not available at time of call, probably b/c the connection was not complate. // * @throws Exception // */ // public HostPort getHostPortIfConnected() { // HostPort rc = null; // if (m_remoteAddress!=null) { // rc = new HostPort(); // rc.hostNameOrIpAddress = getIp(); // rc.port = this.port; // rc.setIp(getIp()); // } // return rc; // } /** * Why do we have ConnectionPartOne and ConnectionPartTwo? * There is a hand off, and it happens right here. * Once ConnectionPartOne is finished with it's business, * it invokes this setSocket() method. */ public void setSocket(Socket socket) { if (socket != null) { m_remoteAddress = socket.getInetAddress(); this.port = socket.getPort(); m_controlThread = new DebugControlConnectionThread(socket, this); m_controlThread.start(); executeStartupCommands(); m_controlThread.sendMessage("getsettings"); m_controlThread.sendMessage("[out-network"); //m_controlThread.sendMessage("[out-network-true"); String networkTracePortStr = m_controlThread.getMessage(); int networkTracePort = Integer.parseInt(networkTracePortStr); if (LOG.isDebugEnabled()) LOG.debug("Received network trace port [" + networkTracePort + "]"); try { INetworkOutputConfig config = new INetworkOutputConfig() { @Override public boolean isNetOutputEnabled() { return m_settingsData.netOutEnabled; } @Override public boolean isGzipEnabled() { return m_settingsData.gzipEnabled; } }; m_networkTraceThread2 = new NetworkDataReceiverThread2( m_remoteAddress, networkTracePort, config ); m_networkTraceThread2.start(); setConnectState(ConnectState.CONNECTED); setConnectionStatusMsg(DefaultFactory.getFactory().getMessages().getConnected()); } catch (IOException ex) { m_traceEvents.add( TraceFactory.createExceptionTraceEvent( org.headlessintrace.rcp.ClientStrings.TRACE_CREATION_ERROR, ex, m_remoteAddress, networkTracePortStr)); } } else { setConnectState(ConnectState.DISCONNECTED_ERR); } } /** * TODO: Need to see if setConnectionState and this method do the same thing, so one can be eliminated. */ public void setConnectionStatusMsg(String statusText) { for(IConnectionStateCallback callback : this.m_connCallbacks) callback.setConnectionStatusMsg(statusText); } /** * TODO: Need to see if setConnectionStatus and this method do the same thing, so one can be eliminated. * @param connectionState */ public void setConnectState(ConnectState connectionState) { synchronized(this.m_connectState) { this.m_connectState = connectionState; } broadcastConnectionState(); } public void broadcastConnectionState() { for(IConnectionStateCallback callback : this.m_connCallbacks) callback.setConnectState(getConnectState()); } // public void broadcastConnectionState(IConnectionStateCallback callback) { // callback.setConnectState(getConnectState()); // } // /** // * TODO: This needs some serious unit testing. // * @param connCallbackToRemove // */ // public void removeConnCallback(IConnectionStateCallback connCallbackToRemove) { // for(IConnectionStateCallback connCallback : this.m_connCallbacks) { // if (connCallback==connCallbackToRemove) { // this.m_connCallbacks.remove(connCallbackToRemove); // } // } // } public List getConnCallbacks() { return m_connCallbacks; } public void addConnCallback(IConnectionStateCallback connCallback) { if (connCallback!=null) { this.m_connCallbacks.add(connCallback); connCallback.setConnectState(m_connectState); } } public EventList getTraceEvents() { return m_traceEvents; } public void setTraceEvents(EventList traceEvents) { this.m_traceEvents = traceEvents; } public NetworkDataReceiverThread2 getNetworkTraceThread2() { return m_networkTraceThread2; } public void setNetworkTraceThread2( NetworkDataReceiverThread2 networkTraceThread2) { this.m_networkTraceThread2 = networkTraceThread2; } public ControlConnectionThread getControlThread() { return m_controlThread; } public void setControlThread(ControlConnectionThread controlThread) { this.m_controlThread = controlThread; } public IAgentCommand[] getStartupCommands() { return m_startupCommands; } public void setCommandArray(IAgentCommand[] startupCommands) { this.m_startupCommands = startupCommands; } } class DebugControlConnectionThread extends ControlConnectionThread { private static final Logger LOG = LoggerFactory.getLogger(DebugControlConnectionThread.class); public DebugControlConnectionThread(Socket socket, IControlConnectionListener listener) { super(socket, listener); } @Override public void sendMessage(String xiString) { if (LOG.isDebugEnabled()) LOG.debug("sendMessage[" + xiString + "]"); super.sendMessage(xiString); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy