/* * BasicMutableStore.java * * Created on May 28, 2003, 4.39 PM */ package hep.aida.ref.remote; import java.io.IOException; import java.util.*; import java.util.logging.*; import hep.aida.IManagedObject; import hep.aida.IBaseHistogram; import hep.aida.IDataPointSet; import hep.aida.dev.IDevTree; import hep.aida.dev.IDevMutableStore; import hep.aida.ref.ManagedObject; import hep.aida.ref.Annotation; import hep.aida.ref.AidaUtils; import hep.aida.ref.remote.interfaces.AidaUpdatable; import hep.aida.ref.remote.interfaces.AidaTreeClient; import hep.aida.ref.remote.interfaces.AidaUpdateEvent; import hep.aida.ref.tree.Tree; /* import java.awt.Component; import org.freehep.application.mdi.PageContext; import org.freehep.application.studio.Studio; import org.freehep.jas.services.WebBrowser; import org.freehep.jas.plugin.web.*; */ import org.freehep.util.FreeHEPLookup; import org.openide.util.Lookup; /** * This is Basic implementation of Read-Only IDevMutableStore. * It has extra methods that allow to change state of the tree * and to create IManagedObject in that tree and update its data. * This implementation creates appropriate subclass of RemoteClient * to connect to a remote server. * * All subclasses need to implement 3 methods: * * protected RemoteClient createClient(Map options); * public IManagedObject createObject(String name, String type); * public void updateData(String path, String type); * * @author serbo */ public abstract class RemoteMutableStore implements AidaUpdatable, IDevMutableStore { protected IDevTree tree; protected RemoteClient client; protected boolean initDone; protected boolean recursive; protected RemoteUpdateEvent[] events; protected Logger remoteLogger; protected boolean acceptEvents = true; protected boolean hurry; // If true, do not wait for data update, // just schedule data update and return. /** * Creates a new instance of BasicMutableStore. */ public RemoteMutableStore() { this(false); } public RemoteMutableStore(boolean hurry) { this(null, null, hurry); } public RemoteMutableStore(IDevTree tree) { this(tree, null, false); } public RemoteMutableStore(IDevTree tree, boolean hurry) { this(tree, null, hurry); } private RemoteMutableStore(IDevTree tree, RemoteClient client, boolean hurry) { this.tree = tree; this.client = client; this.hurry = hurry; this.remoteLogger = Logger.getLogger("hep.aida.ref.remote"); init(); } // Service methods and classes public class TreeEntry { public String[] entryNames = null; public String[] entryTypes = null; } TreeEntry getEntries(IDevTree t, String path, boolean rec, boolean check) throws IOException { TreeEntry entries = new TreeEntry(); boolean wrong = true; String[] names = t.listObjectNames(path, rec); String[] types = t.listObjectTypes(path, rec); while (check && wrong) { int l1 = names.length; int l2 = types.length; remoteLogger.fine("RemoteMutableStore.getEntries Tree l1="+l1+", l2="+l2+", recursive="+rec+", path="+path);; wrong = (l1 != l2); if (wrong) { try { Thread.sleep(200); names = t.listObjectNames(path, rec); types = t.listObjectTypes(path, rec); } catch (Exception ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.getEntries: \n\t"+ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } } else { entries.entryNames = names; entries.entryTypes = types; } } return entries; } TreeEntry getEntries(RemoteClient t, String path, boolean rec, boolean check) throws IOException { TreeEntry entries = new TreeEntry(); boolean wrong = true; String[] names = t.listObjectNames(path, rec); String[] types = t.listObjectTypes(path, rec); while (check && wrong) { int l1 = names.length; int l2 = types.length; remoteLogger.fine("RemoteMutableStore.getEntries Client l1="+l1+", l2="+l2+", recursive="+rec+", path="+path);; wrong = (l1 != l2); if (wrong) { try { Thread.sleep(400); names = t.listObjectNames(path, rec); types = t.listObjectTypes(path, rec); } catch (Exception ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.getEntries: \n\t"+ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } } else { entries.entryNames = names; entries.entryTypes = types; } } return entries; } void setRecursive(boolean b) { this.recursive = b; } public boolean isRecursive() { return recursive; } public void init() { this.recursive = true; this.events = new RemoteUpdateEvent[1]; this.initDone = false; this.acceptEvents = true; } public void setHurry(boolean hurry) { this.hurry = hurry; remoteLogger.fine("RemoteMutableStore.setHurry to "+hurry); } // Can schedule data update for later or just directly execute it now public void handleDataUpdate(IManagedObject mo, String path, String type) throws IllegalArgumentException { handleDataUpdate(mo, path, type, hurry); } public void handleDataUpdate(IManagedObject mo, String path, String type, boolean hurryUp) throws IllegalArgumentException { if (hurryUp) { // Schedule data update here and pretend that object is up-to-date RemoteUpdateEvent evt = new RemoteUpdateEvent(AidaUpdateEvent.DO_DATA_UPDATE_NOW, path, type); RemoteUpdateEvent[] eventArray = new RemoteUpdateEvent[1]; eventArray[0] = evt; if (client != null) { client.stateChanged(eventArray); if (mo instanceof RemoteManagedObject) { ((RemoteManagedObject) mo).setDataValid(true); } } else { updateData(path, type); } } else { updateData(path, type); } } // This method must be overwritten by subclass protected abstract RemoteClient createClient(Map options); // IDevMutableStore methods public abstract void updateData(String path, String type) throws IllegalArgumentException; public abstract IManagedObject createObject(String name, String aidaType) throws IllegalArgumentException; // IMutableStore methods public void close() throws IOException { acceptEvents = false; if (tree instanceof Tree) ((Tree) tree).fireConnectionEvent("/", false); if (client != null) client.disconnect(); client = null; tree = null; events = null; } public void commit(IDevTree tree, Map options) throws IOException { throw new UnsupportedOperationException("Can not commit changes to the Read-Only Store"); } public boolean isReadOnly() { return true; } // This method is only called for Tree (not RemoteTree) public void reloadFolder(String path) throws IllegalArgumentException, IOException { reloadFolder(path, isRecursive()); } public void reloadFolder(String path, boolean localRec) throws IllegalArgumentException, IOException { remoteLogger.fine("RemoteMutableStore.reloadFolder: path="+path+", recursive="+localRec); if (!client.isConnected()) throw new IOException("Can not read from the Store: client is not connected!"); long t0 = System.currentTimeMillis(); TreeEntry entries = getEntries(client, path, localRec, true); String[] names = entries.entryNames; String[] types = entries.entryTypes; long t1 = System.currentTimeMillis(); IDevTree localTree = (IDevTree) tree; TreeEntry localEntries = getEntries(localTree, path, localRec, true); String[] oldNames = localEntries.entryNames; String[] oldTypes = localEntries.entryTypes; long t2 = System.currentTimeMillis(); if (names != null && types != null) { /* for (int i=0; i<names.length; i++) { System.out.println("reloadFolder: \t"+i+" name: "+names[i]+"\t type: "+types[i]); } */ // add new nodes as needed for (int i=0; i<names.length; i++) { int id = AidaUpdateEvent.DO_ADD_NODE_NOW; String pathString = names[i]; String typeString = types[i]; String type = typeString; String xType = "double"; // Parse out possible X Axis type int indexT = typeString.lastIndexOf(":"); if (indexT > 0) { type = typeString.substring(0, indexT); String tmpType = typeString.substring(indexT+1); if (tmpType != null && !tmpType.equals("")) xType = tmpType; } try { int index = AidaUtils.findInArray(names[i], oldNames); if (index < 0) { // no local node with such name, need to add id = AidaUpdateEvent.DO_ADD_NODE_NOW; } else { if (type.equals(oldTypes[index]) || typeString.equals(oldTypes[index])) { oldNames[index] = null; oldTypes[index] = null; id = AidaUpdateEvent.NODE_UPDATED; if (type.equalsIgnoreCase("dir") || type.equalsIgnoreCase("mnt")) continue; } else { // delete node with wrong type first oldNames[index] = null; oldTypes[index] = null; id = AidaUpdateEvent.NODE_DELETED; executeStateChanged(id, pathString, type, xType); id = AidaUpdateEvent.DO_ADD_NODE_NOW; } } executeStateChanged(id, pathString, type, xType); // If "recursive", mark all sub-directories as "filled" if (localRec) { if (type.equalsIgnoreCase("dir") || type.equalsIgnoreCase("mnt")) tree.hasBeenFilled(pathString); } } catch (Exception e) { remoteLogger.log(Level.FINE, "Exception in RemoteMutableStore.reloadFolder: "+ path+"\n\t"+ e.getMessage()); remoteLogger.log(Level.FINEST, "Exception in RemoteMutableStore.reloadFolder: "+ path, e); } } // now delete all old nodes that are not used anymore String xType = "double"; int id = AidaUpdateEvent.NODE_DELETED; for (int i=0; i<oldNames.length; i++) { if (oldNames[i] == null) continue; try { executeStateChanged(id, oldNames[i], oldTypes[i], xType); } catch (Exception e) { remoteLogger.log(Level.FINE, "Exception in RemoteMutableStore.reloadFolder: "+ path+"\n\t"+ e.getMessage()); remoteLogger.log(Level.FINEST, "Exception in RemoteMutableStore.reloadFolder: "+ path, e); } } } long t3 = System.currentTimeMillis(); long dt1 = (t1-t0); long dt2 = (t2-t1); long dt3 = (t3-t0); remoteLogger.fine("RemoteMutableStore.reloadFolder: Client time: "+dt1+", Tree time: "+dt2+", Total: "+dt3+", path="+path+", recursive="+localRec); } public void read(IDevTree tree, String path) throws IllegalArgumentException, IOException { if (this.tree == null) this.tree = tree; if (!client.isConnected()) throw new IOException("Can not read from the Store: client is not connected!"); remoteLogger.fine("RemoteMutableStore.read: path="+path+", recursive="+isRecursive()); String[] names = null; String[] types = null; TreeEntry entries = getEntries(client, path, isRecursive(), true); names = entries.entryNames; types = entries.entryTypes; /* for (int i=0; i<names.length; i++) { System.out.println("read: \t"+i+" name: "+names[i]+"\t type: "+types[i]); } for (int i=0; i<types.length; i++) { System.out.println("\t"+i+" type: "+types[i]); } System.out.println("\n"); */ //initDone = false; if (names != null && types != null) { String pathString = ""; for (int i=0; i<names.length; i++) { try { int id = AidaUpdateEvent.DO_ADD_NODE_NOW; pathString = names[i]; String typeString = types[i]; String type = typeString; String xType = "double"; // Parse out possible X Axis type int indexT = typeString.lastIndexOf(":"); if (indexT > 0) { type = typeString.substring(0, indexT); String tmpType = typeString.substring(indexT+1); if (tmpType != null && !tmpType.equals("")) xType = tmpType; } executeStateChanged(id, pathString, type, xType); // If "recursive", mark all sub-directories as "filled" if (isRecursive()) { if (type.equalsIgnoreCase("dir") || type.equalsIgnoreCase("mnt")) tree.hasBeenFilled(pathString); } } catch (Exception e) { remoteLogger.log(Level.FINE, "Exception in RemoteMutableStore.read: "+ pathString+"\n\t"+ e.getMessage()); remoteLogger.log(Level.FINEST, "Exception in RemoteMutableStore.read: "+ pathString, e); } } tree.hasBeenFilled(path); } //initDone = true; } public void read(IDevTree tree, Map options, boolean readOnly, boolean createNew) throws IOException { if (this.tree == null) this.tree = tree; //if (tree != null && tree.getLock() == null) tree.setLock(new Object()); if (client == null) { client = createClient(options); } if (!client.isConnected()) { client.connect(); } boolean rec = options.containsKey("recursive"); setRecursive(rec); initDone = false; remoteLogger.fine("RemoteMutableStore.read: initial read for the top directory, recursive="+isRecursive()); read(tree, "/"); initDone = true; } // AidaUpdatable methods /** * This method actually does the job of modifying the client tree. * If directory or node already does exist, it will not be overwritten. * Synchronizes with Tree lock, if the Tree has lock object set. */ public void stateChanged(AidaUpdateEvent event) { if (!acceptEvents) return; Object lock = tree.getLock(); remoteLogger.finest("RemoteMutableStore.stateChanged for EVENT:: id="+event.id()+", path="+event.path()+", type="+event.nodeType()); if (lock != null) { synchronized (lock) { executeStateChanged(event); } } else { executeStateChanged(event); } } protected void executeStateChanged(AidaUpdateEvent event) { int id = event.id(); String path = event.path(); String typeString = event.nodeType(); String type = typeString; String xType = "double"; // Parce out possible X Axis type int indexT = typeString.lastIndexOf(":"); if (indexT > 0) { type = typeString.substring(0, indexT); String tmpType = typeString.substring(indexT+1); if (tmpType != null && !tmpType.equals("")) xType = tmpType; } else if (event instanceof RemoteUpdateEvent) { String tmp = ((RemoteUpdateEvent) event).getXAxisType(); if (tmp != null && !tmp.equals("")) xType = tmp; } executeStateChanged(id, path, type, xType); remoteLogger.finest("RemoteMutableStore.executeStateChanged for EVENT:: id="+id+", path="+path+", type="+type+", xAxisType="+xType); if(tree instanceof RemoteTree && id != AidaUpdateEvent.NODE_UPDATED) ((RemoteTree) tree).submitEventToListeners(event); } protected void executeStateChanged(int id, String path, String type, String xType) { Object lock = tree.getLock(); remoteLogger.finest("RemoteMutableStore.executeStateChanged:: id="+id+", path="+path+", type="+type+", xAxisType="+xType+", acceptEvents="+acceptEvents+", lock="+lock); if (!acceptEvents) return; if (id == AidaUpdateEvent.NODE_ADDED || id == AidaUpdateEvent.DO_ADD_NODE_NOW) { try { if (type.equalsIgnoreCase("dir") || type.equalsIgnoreCase("mnt")) { if(tree instanceof RemoteTree) { ((RemoteTree) tree).addFolder(path); } else { tree.mkdirs(path); } try { if (!(id == AidaUpdateEvent.DO_ADD_NODE_NOW) && isRecursive()) read(tree, path); } catch (Exception e) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: Exception while adding node: "+type+", "+path+", Skip this.\n\t", e.getMessage()); remoteLogger.log(Level.FINEST, "RemoteMutableStore.executeStateChanged: Exception while adding node: "+type+", "+path+", Skip this.", e.getStackTrace()); } } else { // Parse object name and folder path if (path.endsWith("/")) path = path.substring(0, path.length()-1); if (path.endsWith("/")) path = path.substring(0, path.length()-1); String name = AidaUtils.parseName(path); String objDir = AidaUtils.parseDirName(path); IManagedObject h = null; // Make sure all directories in the path exist. if(tree instanceof RemoteTree) ((RemoteTree) tree).addFolder(objDir); else tree.mkdirs(objDir); if (type.equals("RemoteUnavailableObject")) h = new RemoteUnavailableObject(name); else h = createObject(name, type); // Set X Axis Type if (h instanceof RemoteManagedObject) { RemoteManagedObject r = (RemoteManagedObject) h; r.setFillable(true); if ( h instanceof IBaseHistogram) { Annotation a = (Annotation) ((IBaseHistogram) h).annotation(); a.setFillable(true); try { a.setValue("xAxisType", xType); } catch (IllegalArgumentException e) { a.addItem("xAxisType", xType); } a.setFillable(false); } else if ( h instanceof IDataPointSet) { Annotation a = (Annotation) ((IDataPointSet) h).annotation(); a.setFillable(true); try { a.setValue("xAxisType", xType); } catch (IllegalArgumentException e) { a.addItem("xAxisType", xType); } a.setFillable(false); } r.setFillable(false); } if(tree instanceof RemoteTree) ((RemoteTree) tree).addObject(objDir, h); else tree.add(objDir, h); if (h instanceof RemoteManagedObject) { ((RemoteManagedObject) h).setTreeFolder(objDir); if (h instanceof RemoteUnavailableObject) ((RemoteManagedObject) h).setDataValid(true); else ((RemoteManagedObject) h).setDataValid(false); } else { handleDataUpdate(h, path, type); } } } catch (IllegalArgumentException ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: IllegalArgumentException while ADDING node: "+type+", "+path+", Skip this: \n\t"+ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } } else if (id == AidaUpdateEvent.FOLDER_IS_FILLED) { if (type.equalsIgnoreCase("dir")) tree.hasBeenFilled(path); } else if (id == AidaUpdateEvent.NODE_DELETED) { try { if (type.equalsIgnoreCase("dir")) { if(tree instanceof RemoteTree) ((RemoteTree) tree).removeFolder(path); else tree.rmdir(path); } else { if(tree instanceof RemoteTree) ((RemoteTree) tree).removeObject(path); else tree.rm(path); } } catch (IllegalArgumentException ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: IllegalArgumentException while DELETING node: "+type+", "+path+", Skip this.\n\t", ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } } else if (id == AidaUpdateEvent.NODE_UPDATED) { // Here we just mark RemoteManagedObject as not valid, do not actually get new data. try { IManagedObject h = null; if(tree instanceof RemoteTree) h = ((RemoteTree) tree).executeFind(path); else h = tree.find(path); if (h instanceof RemoteManagedObject) { ((RemoteManagedObject) h).setDataValid(false); } else { handleDataUpdate(h, path, type); } } catch (IllegalArgumentException ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: IllegalArgumentException while UPDATING node: "+type+", "+path+", Skip this.\n\t", ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } } else if (id == AidaUpdateEvent.DO_DATA_UPDATE_NOW) { updateData(path, type); } else if (id == AidaUpdateEvent.TREE_CLOSED) { remoteLogger.info("***** Got TREE_CLOSED event from Remote AIDA Server."); remoteLogger.info("***** Connection to the Remote AIDA Server is Lost"); acceptEvents = false; try { close(); } catch (Exception ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: Exception while closing tree", ex); remoteLogger.log(Level.FINE, "", ex); } } else if (id == AidaUpdateEvent.NODE_TEMPORARY_UNAVAILABLE) { remoteLogger.finest("RemoteMutableStore.executeStateChanged:: id="+id+", path="+path+", type="+type+", xAxisType="+xType+", lock="+lock+", Tree="+tree.storeName()); // Remove existing object first try { if (tree instanceof RemoteTree) { if (type.equalsIgnoreCase("dir")) { ((RemoteTree) tree).removeFolder(path); } else { //IManagedObject obj = tree.find(path); ((RemoteTree) tree).removeObject(path); } } else if (tree instanceof Tree) { ((Tree) tree).fireConnectionEvent(path, false); } } catch (IllegalArgumentException ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: IllegalArgumentException while NODE_TEMPORARY_UNAVAILABLE operation: "+type+", "+path+", Skip this.\n\t", ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } // Put in RemoteUnavailableObject if (tree instanceof RemoteTree) { String name = AidaUtils.parseName(path); String objDir = AidaUtils.parseDirName(path); RemoteUnavailableObject h = new RemoteUnavailableObject(name); ((RemoteTree) tree).addObject(objDir, h); h.setTreeFolder(objDir); h.setDataValid(true); reloadWebPage(); } } else if (id == AidaUpdateEvent.NODE_IS_AVAILABLE_AGAIN) { remoteLogger.finest("RemoteMutableStore.executeStateChanged:: id="+id+", path="+path+", type="+type+", xAxisType="+xType+", lock="+lock+", Tree="+tree.storeName()); // Remove existing RemoteUnavailableObject first try { IManagedObject obj = null; if (tree instanceof RemoteTree) { obj = ((RemoteTree) tree).executeFind(path); if (obj != null) { ((RemoteTree) tree).removeObject(path); } } else if (tree instanceof Tree) { obj = ((hep.aida.ref.tree.Tree) tree).findObject(path); if (obj.type().equalsIgnoreCase("RemoteUnavailableObject")) { tree.rm(path); } } } catch (IllegalArgumentException ex) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: IllegalArgumentException while NODE_IS_AVAILABLE_AGAIN operation: "+type+", "+path+", Skip this.\n\t", ex.getMessage()); remoteLogger.log(Level.FINE, "", ex); } try { if (tree instanceof RemoteTree) { if (type.equalsIgnoreCase("dir")) { ((RemoteTree) tree).addFolder(path); if (isRecursive()) try { read(tree, path); } catch (Exception ioe) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: Exception while updating PATH: "+type+", "+path, ioe); remoteLogger.log(Level.FINE, "", ioe.getStackTrace()); } } else { RemoteUpdateEvent evt = new RemoteUpdateEvent(AidaUpdateEvent.NODE_ADDED, path, type); RemoteUpdateEvent[] eventArray = new RemoteUpdateEvent[1]; eventArray[0] = evt; if (client != null) { client.stateChanged(eventArray); } } } else if (tree instanceof Tree) { if (type.equalsIgnoreCase("dir")) { tree.mkdirs(path); reloadFolder(path); ((Tree) tree).fireConnectionEvent(path, true); } else { RemoteUpdateEvent evt = new RemoteUpdateEvent(AidaUpdateEvent.NODE_ADDED, path, type); RemoteUpdateEvent[] eventArray = new RemoteUpdateEvent[1]; eventArray[0] = evt; if (client != null) { client.stateChanged(eventArray); } ((Tree) tree).fireConnectionEvent(path, true); } } } catch (Exception e) { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: IllegalArgumentException while NODE_IS_AVAILABLE_AGAIN operation: "+type+", "+path+", Skip this.\n\t", e.getMessage()); remoteLogger.log(Level.FINE, "", e); } } else if (id == AidaUpdateEvent.REMOTE_CONNECTION_EXCEPTION) { remoteLogger.info("***** Got REMOTE_CONNECTION_EXCEPTION event from Remote AIDA Server."); remoteLogger.info("***** Connection to the Remote AIDA Server is Lost"); acceptEvents = false; try { close(); } catch (Exception exc) { exc.printStackTrace(); } } else { remoteLogger.log(Level.INFO, "RemoteMutableStore.executeStateChanged: Wrong ID="+id+", path="+path+", type="+type); } } protected void reloadWebPage() { /* Studio app = (Studio) org.freehep.application.Application.getApplication(); if (app != null) { SimpleWebBrowser webBrowser = (SimpleWebBrowser) app.getLookup().lookup(WebBrowser.class); } */ } // Thread that does reading updates // Currently no used, as updates go through the client.stateChanged() public class ReadThread extends Thread { private String readPath; private long wait; public ReadThread(String readPath) { this(readPath, 0); } public ReadThread(String readPath, long wait) { this.readPath = readPath; this.wait = wait; } public void run() { try { if (wait > 0) Thread.sleep(wait); read(tree, readPath); } catch (InterruptedException e2) { remoteLogger.log(Level.INFO, "RemoteMutableStore InterruptedException: "+ e2); remoteLogger.log(Level.FINE, "RemoteMutableStore InterruptedException: ", e2); } catch (Exception e3) { remoteLogger.log(Level.INFO, "Exception in RemoteMutableStore: "+ e3); remoteLogger.log(Level.FINE, "Exception in RemoteMutableStore: ", e3); } } } }