View Javadoc

1   /*
2    * Copyright (c) 2005 The University of Reading
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   * 2. Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in the
12   *    documentation and/or other materials provided with the distribution.
13   * 3. Neither the name of the University of Reading, nor the names of the
14   *    authors or contributors may be used to endorse or promote products
15   *    derived from this software without specific prior written permission.
16   * 
17   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   */
28  
29  package uk.ac.rdg.resc.jstyx.gridservice.client;
30  
31  import javax.swing.tree.DefaultMutableTreeNode;
32  import java.util.Vector;
33  
34  import org.apache.log4j.Logger;
35  
36  import uk.ac.rdg.resc.jstyx.client.CStyxFileChangeListener;
37  import uk.ac.rdg.resc.jstyx.client.CStyxFile;
38  import uk.ac.rdg.resc.jstyx.StyxUtils;
39  import uk.ac.rdg.resc.jstyx.StyxException;
40  
41  // These imports are only needed for the signatures of the (empty) methods
42  // of the CStyxFileChangeListener
43  import org.apache.mina.common.ByteBuffer;
44  import uk.ac.rdg.resc.jstyx.types.DirEntry;
45  import uk.ac.rdg.resc.jstyx.messages.TwriteMessage;
46  import uk.ac.rdg.resc.jstyx.messages.TreadMessage;
47  
48  /***
49   * Node in the model that represents a CStyxFile.  This allows the file to
50   * be represented with a different name in the GUI.
51   *
52   * @todo How much of this functionality can be shared with the SGSClient or
53   * SGSInstanceClient?
54   *
55   * @todo Would it be better to create subclasses of this for the different node
56   * types?
57   *
58   * @author Jon Blower
59   * $Revision: 569 $
60   * $Date: 2006-01-05 16:06:35 +0000 (Thu, 05 Jan 2006) $
61   * $Log$
62   * Revision 1.16  2006/01/05 16:06:34  jonblower
63   * SGS clients now deal with possibility that client could be created on a different server
64   *
65   * Revision 1.15  2005/12/07 17:49:05  jonblower
66   * Added getInstanceClient() method to CStyxFileNode
67   *
68   * Revision 1.14  2005/12/07 08:51:48  jonblower
69   * Added getSGSClient() method
70   *
71   * Revision 1.13  2005/09/14 07:26:46  jonblower
72   * Added error() method (from SGSChangeListener interface)
73   *
74   * Revision 1.12  2005/09/11 19:29:51  jonblower
75   * Changed call to getInstances() to getInstancesAsync()
76   *
77   * Revision 1.11  2005/08/12 08:08:39  jonblower
78   * Developments to support web interface
79   *
80   * Revision 1.10  2005/07/29 11:19:29  jonblower
81   * Implemented automatic update of SGS instances in SGS Explorer (and logging)
82   *
83   * Revision 1.6  2005/06/20 17:20:48  jonblower
84   * Added download() and downloadAsync() to CStyxFile
85   *
86   * Revision 1.5  2005/05/25 16:58:41  jonblower
87   * Added fileCreated()
88   *
89   * Revision 1.3  2005/05/18 17:13:51  jonblower
90   * Created SGSInstanceGUI
91   *
92   * Revision 1.2  2005/05/18 08:03:24  jonblower
93   * Implemented creation of new service instances
94   *
95   * Revision 1.1  2005/05/17 18:20:50  jonblower
96   * Separated CStyxFileNode from SGSExplorerTreeModel
97   *
98   */
99  class CStyxFileNode extends DefaultMutableTreeNode implements CStyxFileChangeListener,
100     SGSChangeListener
101 {
102 
103     private static final Logger log = Logger.getLogger(CStyxFileNode.class);
104     
105     public static final int ROOT = 0;
106     public static final int SERVER = 1;
107     public static final int SERVICE = 2;
108     public static final int INSTANCE = 3;
109     
110     private CStyxFile file; // The CStyxFile at this node
111     private CStyxFile instances; // The CStyxFile representing the instances of this SGS
112     private String name; // The name as it will appear in the tree
113     private SGSExplorerTreeModel dataModel;
114 
115     private SGSClient sgsClient; // For Service nodes only, the client class
116     private SGSInstanceClient instanceClient; // For Instance nodes only, the client class
117     
118     public CStyxFileNode(SGSExplorerTreeModel dataModel, CStyxFile file, String name)
119     {
120         this.dataModel = dataModel;
121         this.file = file;
122         this.file.addChangeListener(this);
123         this.name = name;
124     }
125 
126     public CStyxFileNode(SGSExplorerTreeModel dataModel, CStyxFile file)
127     {
128         this(dataModel, file, file.getName());
129     }
130     
131     /***
132      * @return the CStyxFile that this node represents
133      */
134     public CStyxFile getFile()
135     {
136         return this.file;
137     }
138 
139     /***
140      * @return the String that will be used to identify this node in the
141      * SGSExplorer
142      */
143     public String toString()
144     {
145         return this.name;
146     }
147     
148     /***
149      * @return the SGSClient object associated with this node.  Only relevant
150      * for SERVICE nodes (will return null otherwise)
151      */
152     public SGSClient getSGSClient()
153     {
154         return this.sgsClient;
155     }
156     
157     /***
158      * @return the type of this node (ROOT, SERVER, SERVICE or INSTANCE) as an
159      * integer, corresponding to the constants defined in this class
160      */
161     public int getType()
162     {
163         // The type of the node is given by the depth in the hierarchy.  This
164         // is one less than the path length to this node.
165         return this.getPath().length - 1;
166     }
167 
168     /***
169      * All nodes can have children except those representing service
170      * instances
171      */
172     public boolean getAllowsChildren()
173     {
174         return (this.getType() != INSTANCE);
175     }
176 
177     /***
178      * Sends a message to get all the children of this node.  When the reply
179      * arrives, the childrenFound method of this class will be called.  Does 
180      * nothing if the children have already been found.
181      */
182     public void findChildren()
183     {
184         if (this.children == null)
185         {
186             if (this.getType() == SERVICE)
187             {
188                 // If this has already been called, this method will do nothing
189                 // When we have got the instances of this service, the
190                 // gotInstances() method will be called
191                 this.sgsClient.getInstancesAsync();
192             }
193             else
194             {
195                 // When we have got the children of this file, the
196                 // childrenFound() method will be called
197                 this.file.getChildrenAsync();
198             }
199         }
200     }
201 
202     /***
203      * Called when the children have been found. Adds them to this node's
204      * Vector of children
205      */
206     public void childrenFound(CStyxFile file, CStyxFile[] files)
207     {
208         // Remove all previous children of this file
209         this.removeAllChildren();
210         synchronized (this)
211         {
212             for (int i = 0; i < files.length; i++)
213             {
214                 this.add(new CStyxFileNode(this.dataModel, files[i]));
215             }
216         }
217         this.dataModel.nodeStructureChanged(this);
218     }
219     
220     /***
221      * Intercepts calls to add a child to this structure, creating SGSclient 
222      * objects if the new child represents a SGS
223      */
224     private void add(CStyxFileNode newChild)
225     {
226         super.add(newChild);
227         if (newChild.getType() == SERVICE)
228         {
229             // If this is a service, we can get a client for the SGS
230             newChild.sgsClient = new SGSClient(newChild.getFile());
231             newChild.sgsClient.addChangeListener(newChild);
232         }
233     }
234     
235     /*** 
236      * Sends a message to create a new instance (reads the clone file). This
237      * method will only be called if this node represents a Service.
238      */
239     public void createNewInstance()
240     {
241         if (this.getType() == SERVICE)
242         {
243             // When the new instance has been created, the gotInstances() method
244             // will be called
245             //this.sgsClient.createNewInstanceAsync();
246         }
247         else
248         {
249             log.error("Can't create new instance from this type of node");
250         }
251     }
252     
253     /***
254      * Gets an SGSInstanceClient for this node.
255      * @throws IllegalStateException if this node does not represent a service
256      * instance
257      * @throws StyxException if there was an error creating the client
258      */
259     public SGSInstanceClient getInstanceClient() throws StyxException
260     {
261         if (this.instanceClient == null)
262         {
263             if (this.getType() != INSTANCE)
264             {
265                 throw new IllegalStateException("Can only call getInstanceClient "
266                     + "on a service instance");
267             }
268             // Get the parent of this node: this will be a SERVICE
269             CStyxFileNode parent = (CStyxFileNode)this.getParent();
270             this.instanceClient = new SGSInstanceClient(parent.getSGSClient(), this.file);
271         }
272         return this.instanceClient;
273     }
274     
275     /***
276      * Required by SGSChangeListener interface: called when this is a Service
277      * and we have a new list of instances
278      */
279     public void gotInstances(CStyxFile[] newInstances)
280     {
281         if (this.children == null)
282         {
283             // Just add the children of this node
284             this.childrenFound(null, newInstances);
285         }
286         else
287         {
288             // We must work out which of the instance(s) that we have found are
289             // new, and which instance(s) have been destroyed
290             synchronized (this.children)
291             {
292                 log.debug("Got " + newInstances.length + " instances");
293                 // We will construct a new Vector of children.  There will be 
294                 // more efficient algorithms based on in-place modification of the
295                 // existing Vector of children (TODO), and the fact that the
296                 // children arrive in a predictable order
297                 Vector newChildren = new Vector();
298                 // Search through all of the new instances
299                 try
300                 {
301                     for (int i = 0; i < newInstances.length; i++)
302                     {
303                         log.debug("Examining new instance " + newInstances[i].getName());
304                         boolean instanceFound = false;
305                         // Look to see if this instance already exists
306                         for (int j = 0; j < this.children.size(); j++)
307                         {
308                             log.debug("Instance " + j + " is a "
309                                 + this.children.get(j).getClass());
310                             CStyxFileNode existingInstanceNode =
311                                 (CStyxFileNode)this.children.get(j);
312                             CStyxFile existingInstanceFile =
313                                 existingInstanceNode.getFile();
314                             log.debug("Comparing new instance " +
315                                 newInstances[i].getName() + " with existing instance "
316                                 + existingInstanceFile.getName());
317                             // Check to see if they are the same file.  The 
318                             // isSameFile checks the Qids of the file and *might*
319                             // block, but it shouldn't since the dirEntrys of the 
320                             // two files should already have been set.
321                             if (newInstances[i].isSameFile(existingInstanceFile))
322                             {
323                                 log.debug("They are the same file");
324                                 newChildren.add(existingInstanceNode);
325                                 instanceFound = true;
326                                 break;
327                             }
328                             else
329                             {
330                                 log.debug("They are not the same file");
331                             }
332                         }
333                         if (!instanceFound)
334                         {
335                             log.debug("Adding new instance " + newInstances[i].getName()
336                                 + " to the list of children");
337                             CStyxFileNode newNode = new CStyxFileNode(this.dataModel, newInstances[i]);
338                             newNode.setParent(this);
339                             newChildren.add(newNode);
340                         }
341                     }
342                     this.children = newChildren;
343                     this.dataModel.nodeStructureChanged(this);
344                 }
345                 catch (Exception e)
346                 {
347                     // This shouldn't happen (isSameFile() can throw a StyxException
348                     // but should not in this case: see comments above.
349                     // TODO: log error properly
350                     e.printStackTrace();
351                 }
352             }
353         }
354     }
355     
356     /***
357      * Overrides method in SGSChangeListener: this is called when an error occurs
358      * creating a new instance of the service or reading the instances
359      */
360     public void error(String message)
361     {
362         log.error("Error creating or getting instances: " + message);
363     }
364 
365     /***
366      * Overrides method in CStyxFileChangeListener
367      */
368     public void error(CStyxFile file, String message)
369     {
370         log.error("Error with " + file.getName() + ": " + message);
371     }
372 
373     // These methods are required by the CStyxFileChangeListener interface
374     public void dataArrived(CStyxFile theFile, TreadMessage tReadMsg, ByteBuffer data) {}
375     public void fileOpen(CStyxFile file, int mode){}
376     public void fileCreated(CStyxFile file, int mode){}
377     public void dataWritten(CStyxFile file, TwriteMessage tWriteMsg){}
378     public void statChanged(CStyxFile file, DirEntry newDirEntry){}
379     public void uploadComplete(CStyxFile targetFile){}
380     public void downloadComplete(CStyxFile sourceFile){}
381 }