/* * CorbaTreeClient.java * * Created on June 8, 2003, 5:08 PM */ package hep.aida.ref.remote.corba; import hep.aida.IAnalysisFactory; import hep.aida.IDataPointSet; import hep.aida.IHistogram1D; import hep.aida.IManagedObject; import hep.aida.IPlotter; import hep.aida.ITreeFactory; import hep.aida.dev.IDevMutableStore; import hep.aida.ref.AnalysisFactory; import hep.aida.ref.remote.RemoteConnectionException; import hep.aida.ref.remote.RemoteUpdatableQueue; import hep.aida.ref.remote.RemoteUpdateEvent; import hep.aida.ref.remote.corba.generated.EventFlags; import hep.aida.ref.remote.corba.generated.EventID; import hep.aida.ref.remote.corba.generated.EventStruct; import hep.aida.ref.remote.corba.generated.TreeClient; import hep.aida.ref.remote.corba.generated.TreeClientHelper; import hep.aida.ref.remote.corba.generated.TreeClientPOA; import hep.aida.ref.remote.corba.generated.TreeServant; import hep.aida.ref.remote.corba.generated.TreeServer; import hep.aida.ref.remote.corba.generated.TreeServerHelper; import hep.aida.ref.remote.interfaces.AidaTreeClient; import hep.aida.ref.remote.interfaces.AidaUpdateEvent; import hep.aida.ref.tree.Tree; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.StringTokenizer; import org.omg.CORBA.Any; import org.omg.CORBA.ORB; import org.omg.CosNaming.NameComponent; import org.omg.CosNaming.NamingContext; import org.omg.CosNaming.NamingContextHelper; import org.omg.PortableServer.POA; import org.omg.PortableServer.POAHelper; /** * This is Basic implementation of AidaTreeClient that support both "Duplex" * and "non-Duplex" modes of communication with the AIDA Tree server. * * In "Duplex" mode AidaTreeServant call stateChanged() method to notify * BasicTreeClient about updates/changes in the server-side AIDA tree. * * In "non-Duplex" mode BasicTreeClient runs as a separate thread and * periodically calls updates() method of AidaTreeServant to get information * about updates/changes in the server-side AIDA tree. * * BasicTreeClient also implements IMutableStore, so it can be used as a * Store for any IDevTree. * * @author serbo */ public class CorbaTreeClientImpl extends TreeClientPOA implements AidaTreeClient, Runnable { protected TreeServer server; protected TreeServant servant; protected IDevMutableStore store; protected RemoteUpdatableQueue queue; protected boolean duplex; protected boolean isConnected; protected long updateInterval; protected boolean keepUpdating; protected String clientID; protected ORB orb; protected POA rootPOA; protected TreeClient treeClient; protected String ior; protected String nsName; //protected NamingContext nc; /** Creates a new instance of CorbaTreeClient * Duplex is default to "true". If TreeServer does not support * Duplex mode, try to connect in non-Duplex. */ public CorbaTreeClientImpl() { this(null, null, null); } public CorbaTreeClientImpl(IDevMutableStore store, String ior) { this(store, ior, null); } public CorbaTreeClientImpl(IDevMutableStore store, String ior, String nsName) { init(); this.store = store; this.ior = ior.trim(); this.nsName = nsName; if (nsName != null) { } this.duplex = true; this.keepUpdating = !duplex; System.out.println("Created CorbaTreeClientImpl with nsName="+nsName+", ior="+ior); } // Service methods protected void init() { server = null; servant = null; isConnected = false; updateInterval = 2000; clientID = "CorbaTreeClient"+"_"+System.currentTimeMillis(); } /** * Retrieves reference to the TreeServer. */ protected TreeServer getServer() { TreeServer server = null; if (nsName == null) { // Use IOR string of server object to resolve it try { org.omg.CORBA.Object objRef = orb.string_to_object(ior); server = TreeServerHelper.narrow(objRef); boolean supportsDuplex = server.supportDuplexMode(); System.out.println("\nTreeServer support for the Duplex Mode: "+supportsDuplex); } catch (Exception t) { t.printStackTrace(); server = null; } } else { // Use IOR string of CORBA Name Service StringTokenizer tokenizer = new StringTokenizer(nsName, "/"); int nTokens = tokenizer.countTokens(); NameComponent[] pathName = new NameComponent[nTokens]; for(int i = 0; i < nTokens; i++) { pathName[i] = new NameComponent(tokenizer.nextToken(), ""); //System.out.println("AmbientDataProvider.connect: Token "+i+", pathName: "+pathName[i].id); } // Get the root naming context try { org.omg.CORBA.Object objRef = orb.string_to_object(ior); NamingContext nc = NamingContextHelper.narrow(objRef); // Resolve OdcBdbServer object org.omg.CORBA.Object serverObject = nc.resolve(pathName); server = TreeServerHelper.narrow(objRef); } catch (Exception e) { e.printStackTrace(); server = null; } } return server; } /** * Set time interval (in milliseconds) for AidaTreeClient to check for updates. * Relevant only for non-Duplex Mode, as in Duplex Mode updates are "pushed" * into AidaTreeClient by AidaTreeServer calling "stateChanged()" method. * Default value: updateInterval=2000 milliseconds. */ public synchronized void setUpdateTime(long updateInterval) { this.updateInterval = updateInterval; } /** * Set duplex mode. Default is "true". */ public synchronized void setDuplex(boolean duplex) { if (isConnected) { System.out.println("WARNING: Client is connected, can not change DUPLEX settings. "+ "Please disconnect first"); return; } this.duplex = duplex; this.keepUpdating = !duplex; System.out.println("CorbaTreeClientImpl.setDuplex "+duplex); } /** * Retrieves Duplex AidaTreeServant from the AidaTreeServer. */ protected void connectDuplex() throws RemoteConnectionException { // Get the Root POA try { org.omg.CORBA.Object rootRef = orb.resolve_initial_references("RootPOA"); rootPOA = POAHelper.narrow(rootRef); // Activate POA and create a reference for the TreeServantImpl rootPOA.the_POAManager().activate(); org.omg.CORBA.Object obj = rootPOA.servant_to_reference(this); String ref = orb.object_to_string(obj); System.out.println("TreeClient IOR String: \n\t"+ref); treeClient = TreeClientHelper.narrow(obj); servant = server.connectDuplex(treeClient); } catch (RemoteConnectionException ae) { throw ae; } catch (Exception e) { String name = "null"; if (server != null) name = server.treeName(); throw new RemoteConnectionException("Can not connect to TreeServer: "+name, e); } if (servant == null) { throw new RemoteConnectionException("Can not retrieve Duplex TreeServant from: "+server.treeName()); } new Thread(this).start(); // Wait for requests. isConnected = true; } /** * Retrieves non-Duplex AidaTreeServant from the AidaTreeServer. */ protected void connectNonDuplex() throws RemoteConnectionException { servant = server.connectNonDuplex(clientID); if (servant == null) { throw new RemoteConnectionException("Can not retrieve non-Duplex TreeServant from: "+server.treeName()); } //if (!clientID.equals(newClientID)) clientID = newClientID; new Thread(this).start(); // Start asking servant for updates. isConnected = true; } public void setValid(String path) { servant.setValid( new String[] { path } ); } // AidaTreeClient methods public String[] listObjectNames(String path) throws IllegalArgumentException { if (!isConnected) { System.out.println("WARNING: Client is not connected."); return null; } String[] names = servant.listObjectNames(path); return names; } public String[] listObjectTypes(String path) throws IllegalArgumentException { if (!isConnected) { System.out.println("WARNING: Client is not connected."); return null; } String[] types = servant.listObjectTypes(path); return types; } public java.lang.Object find(String path) throws IllegalArgumentException { if (!isConnected) { System.out.println("WARNING: Client is not connected."); return null; } Any data = servant.find(path); //servant.setValid(new String[] { path }); return data; } /** * In this implementation stateChanged(UpdateEvent[] events) method simply * schedules updates in the UpdatableQueue. Later queue invokes stateChanged(UpdateEvent event) * method on a separate thread to process updates. */ public void stateChanged(EventStruct[] events) { if (events != null && events.length != 0) { RemoteUpdateEvent[] aidaEvents = new RemoteUpdateEvent[events.length]; String[] paths = new String[events.length]; for (int i=0; i<events.length; i++) { String path = events[i].path; boolean folder = (events[i].flags == EventFlags.FOLDER_MASK); String type = events[i].nodeType; if (folder) type = "dir"; int id = -1; if ( events[i].id == EventID.NODE_UPDATED) id = AidaUpdateEvent.NODE_UPDATED; else if ( events[i].id == EventID.NODE_ADDED) id = AidaUpdateEvent.NODE_ADDED; else if ( events[i].id == EventID.NODE_DELETED) id = AidaUpdateEvent.NODE_DELETED; aidaEvents[i] = new RemoteUpdateEvent(id, path, type); paths[i] = path; } stateChanged(aidaEvents); //servant.setValid(paths); } } public void stateChanged(AidaUpdateEvent[] events) { if (events != null && events.length > 0) { for (int i=0; i<events.length; i++) { queue.schedule(store, events[i]); } } } public boolean isConnected() { return isConnected; } public boolean connect() { if (isConnected) { String name = "null"; if (server != null) name = server.treeName(); System.out.println("WARNING: Already connected to TreeServer: "+ name +". No action taken."); return false; } // Create and initialize the ORB String[] orbArgs = {}; orb = ORB.init(orbArgs, null); server = getServer(); // Get server System.out.println("Connecting: duplex="+duplex); if (server == null) throw new RemoteConnectionException("Can not get reference to TreeServer."); queue = new RemoteUpdatableQueue(); try { if (duplex) { boolean supportsDuplex = server.supportDuplexMode(); if (!supportsDuplex) { System.out.println("Warning: TreeServer \""+server.treeName()+ "\" does not support DUPLEX mode. \nWill try to connect using non-DUPLEX mode."); duplex = false; keepUpdating = !duplex; connectNonDuplex(); } else { connectDuplex(); } } else { connectNonDuplex(); } } catch (RemoteConnectionException ae) { throw ae; } catch (Exception e) { String name = "null"; if (server != null) name = server.treeName(); throw new RemoteConnectionException("Can not connect to TreeServer: "+name, e); } return true; } public boolean disconnect() { System.out.print("\nCorbaTreeClientImpl.disconnect: for Client="+clientID+", isConnected="+isConnected); queue.close(); queue = null; if (!isConnected) { keepUpdating = false; server = null; servant = null; try { orb.shutdown(true); orb.destroy(); } catch (Exception ex) { ex.printStackTrace(); } return true; } boolean status = true; keepUpdating = false; if (server != null) { if (duplex) status = server.disconnectDuplex(treeClient); else status = server.disconnectNonDuplex(clientID); } server = null; servant = null; System.out.print(" Done.\n"); System.out.println("Shutting down CORBA Client ... "); try { orb.shutdown(true); orb.destroy(); } catch (Exception ex) { ex.printStackTrace(); } orb = null; rootPOA = null; System.out.println(" Done!\n"); return status; } // Runnable methods public void run() { if (duplex) { orb.run(); } else { while (keepUpdating) { if (isConnected) { EventStruct[] events = servant.updates(); System.out.println("BasicTreeClient.run: GOT "+events.length+" events."); if (events != null && events.length > 0) stateChanged(events); } try { Thread.sleep(updateInterval); } catch (InterruptedException ie) { System.out.println("AidaTreeClient non-DUPLEX Update Thread InterruptedException."); ie.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } } } } // Do some simple tests here public static void main(String[] args) { java.util.Random r = new java.util.Random(); IAnalysisFactory af = new AnalysisFactory(); ITreeFactory tf = af.createTreeFactory(); Tree clientTree = null; IPlotter plotter = af.createPlotterFactory().create("Plot"); plotter.createRegions(2,2,0); plotter.show(); try { //clientTree = (Tree) tf.create("CORBA_Test", "corba", true, false, "iorFileURL=file:///C:/Temp/TreeServer.ior"); clientTree = (Tree) tf.create("CORBA_Test", "corba", true, false, "iorFileURL=http://www.slac.stanford.edu/~serbo/jas3/TreeServer.ior"); } catch (Exception e) { e.printStackTrace(); System.exit(1); } // Setup the io BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); PrintStream out = System.out; ; String menuMain = "\n\n" + "===============================\n" + " cd <path> \n"+ " pwd \n"+ " ls\n"+ " find <path> \n"+ " plot <path> <region> \n"+ " check <path> \n"+ " mkdirs <path> \n"+ " Q \n" + "-------------------------------\n" + "User Input => " ; while (true) { try { out.print(menuMain); String input = console.readLine(); StringTokenizer st = new StringTokenizer(input.trim(), " "); String[] tokens = new String[st.countTokens()]; int i = 0; while (st.hasMoreTokens()) { tokens[i] = (st.nextToken()).trim(); i++; } if (tokens[0].startsWith("cd")) { clientTree.cd(tokens[1]); } else if (tokens[0].startsWith("pwd")) { out.println(clientTree.pwd()); } else if (tokens[0].startsWith("ls")) { clientTree.ls(); } else if (tokens[0].startsWith("find")) { IManagedObject mo = clientTree.find(tokens[1]); out.println("Found: name="+mo.name()+", mo="+mo); } else if (tokens[0].startsWith("check")) { clientTree.checkForChildren(tokens[1]); } else if (tokens[0].startsWith("mkdirs")) { clientTree.mkdirs(tokens[1]); } else if (tokens[0].startsWith("plot")) { IManagedObject mo = clientTree.find(tokens[1]); if (mo instanceof IHistogram1D) plotter.region(Integer.parseInt(tokens[2])).plot((IHistogram1D) mo); else if (mo instanceof IDataPointSet) plotter.region(Integer.parseInt(tokens[2])).plot((IDataPointSet) mo); plotter.show(); } else if (input.toLowerCase().startsWith("q")) { clientTree.close(); System.exit(0); } } catch (Exception e) { e.printStackTrace(); } } // End while } // End main }