1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package uk.ac.rdg.resc.jstyx.gridservice.client;
30
31 import java.util.Vector;
32
33 import org.apache.mina.common.ByteBuffer;
34
35 import uk.ac.rdg.resc.jstyx.messages.TreadMessage;
36 import uk.ac.rdg.resc.jstyx.messages.TwriteMessage;
37 import uk.ac.rdg.resc.jstyx.messages.StyxBuffer;
38 import uk.ac.rdg.resc.jstyx.client.StyxConnection;
39 import uk.ac.rdg.resc.jstyx.client.CStyxFile;
40 import uk.ac.rdg.resc.jstyx.client.CStyxFileChangeAdapter;
41 import uk.ac.rdg.resc.jstyx.types.DirEntry;
42 import uk.ac.rdg.resc.jstyx.StyxException;
43 import uk.ac.rdg.resc.jstyx.StyxUtils;
44 import uk.ac.rdg.resc.jstyx.gridservice.config.SGSConfig;
45 import uk.ac.rdg.resc.jstyx.gridservice.config.SGSConfigException;
46
47 /***
48 * Client for a Styx Grid Service.
49 *
50 * @author Jon Blower
51 * $Revision: 588 $
52 * $Date: 2006-02-20 17:34:27 +0000 (Mon, 20 Feb 2006) $
53 * $Log$
54 * Revision 1.16 2006/02/20 17:34:27 jonblower
55 * Added getConnection() method
56 *
57 * Revision 1.15 2006/01/05 16:06:34 jonblower
58 * SGS clients now deal with possibility that client could be created on a different server
59 *
60 * Revision 1.14 2005/12/07 17:50:48 jonblower
61 * Fixed bug and comments for getConfig()
62 *
63 * Revision 1.13 2005/12/07 08:53:08 jonblower
64 * Improved getConfig() and changed in line with change to SGSInstanceClient constructor
65 *
66 * Revision 1.12 2005/12/01 08:29:47 jonblower
67 * Refactored XML config handling to simplify clients
68 *
69 * Revision 1.11 2005/11/07 21:03:22 jonblower
70 * Added getConfigXML() method
71 *
72 * Revision 1.9 2005/09/11 18:51:52 jonblower
73 * Added error() method and changed name from getInstances() to getInstancesAsync()
74 *
75 * Revision 1.8 2005/08/12 08:08:39 jonblower
76 * Developments to support web interface
77 *
78 * Revision 1.7 2005/07/06 17:53:43 jonblower
79 * Implementing automatic update of SGS instances in SGS Explorer
80 *
81 * Revision 1.6 2005/05/17 15:10:40 jonblower
82 * Changed structure of SGS to put instances in a directory of their own
83 *
84 * Revision 1.5 2005/05/11 18:24:59 jonblower
85 * Implementing automatic detection of service data elements
86 *
87 * Revision 1.4 2005/03/22 17:44:17 jonblower
88 * Changed to use CStyxFileChangeAdapter instead of Listener and removed empty methods
89 *
90 * Revision 1.3 2005/03/19 21:47:02 jonblower
91 * Further fixes relating to releasing ByteBuffers
92 *
93 * Revision 1.2 2005/03/18 13:55:59 jonblower
94 * Improved freeing of ByteBuffers, and bug fixes
95 *
96 * Revision 1.1 2005/03/16 22:16:44 jonblower
97 * Added Styx Grid Service classes to core module
98 *
99 * Revision 1.3 2005/03/16 17:59:35 jonblower
100 * Changed following changes to core JStyx library (replacement of java.nio.ByteBuffers with MINA's ByteBuffers)
101 *
102 * Revision 1.2 2005/02/21 18:12:09 jonblower
103 * Following changes to core JStyx library
104 *
105 * Revision 1.1 2005/02/16 19:22:29 jonblower
106 * Commit adding of SGS files to CVS
107 *
108 */
109 public class SGSClient extends CStyxFileChangeAdapter
110 {
111
112 private CStyxFile sgsRoot;
113 private CStyxFile cloneFile;
114 private CStyxFile instancesFile;
115
116
117 private String description;
118 private SGSConfig config;
119 private Vector instances;
120
121 private boolean gettingInstances;
122 private Vector changeListeners;
123
124 /***
125 * Creates a new instance of SGSClient.
126 * @param sgsRoot The CStyxFile representing the root of the SGS
127 */
128 public SGSClient(CStyxFile sgsRoot)
129 {
130 this.sgsRoot = sgsRoot;
131 this.cloneFile = this.sgsRoot.getFile("clone");
132 this.instancesFile = this.sgsRoot.getFile(".instances");
133 this.cloneFile.addChangeListener(this);
134 this.instancesFile.addChangeListener(this);
135 this.gettingInstances = false;
136 this.changeListeners = new Vector();
137 this.instances = new Vector();
138 }
139
140 /***
141 * @return the connection object
142 */
143 public StyxConnection getConnection()
144 {
145 return this.sgsRoot.getConnection();
146 }
147
148 /***
149 * @return the name of this SGS (i.e. the name of the root CStyxFile)
150 */
151 public String getName()
152 {
153 return this.sgsRoot.getName();
154 }
155
156 /***
157 * Gets the short description of this Styx Grid Service. If the description
158 * has not previously been set, this method will sent a message to get the
159 * new description and this method will block until the reply arrives.
160 * @throws StyxException if an error occurs when getting the description from
161 * the server. Will never be thrown if the description has already been set.
162 */
163 public String getDescription() throws StyxException
164 {
165 if (this.description == null)
166 {
167 CStyxFile descFile = this.sgsRoot.getFile("docs/description");
168 this.description = descFile.getContents();
169 }
170 return this.description;
171 }
172
173 /***
174 * <p>Reads the configuration file from the server so that we know how to parse
175 * parameters, deal with input files etc. Some of this information cannot be
176 * gleaned simply from interpreting the namespace itself.</p>
177 * <p>The first time this is called, the server will be queried for the
178 * config information. This information will be cached so that further
179 * calls to this method will not lead to the server being queried again,
180 * but will return the cached object.</p>
181 * @throws StyxException if there was an error reading the configuration
182 * from the server
183 */
184 public synchronized SGSConfig getConfig() throws StyxException
185 {
186 try
187 {
188 if (this.config == null)
189 {
190 this.config = new SGSConfig(this.sgsRoot.getFile("config").getContents());
191 }
192 return this.config;
193 }
194 catch (SGSConfigException sce)
195 {
196
197
198 throw new StyxException(sce.getMessage());
199 }
200 }
201
202 /***
203 * Requests creation of a new instance of the SGS on the server. This method
204 * blocks until the instance has been created.
205 * @return The full URL to the root of the new instance, e.g.
206 * <code>styx://thehost.com:9092/mySGS/instances/1234567890abcde</code>.
207 * Note that the new instance may be created on a different server (for
208 * load balancing purposes, for example).
209 */
210 public String createNewInstance() throws StyxException
211 {
212 return this.cloneFile.getContents();
213 }
214
215 /***
216 * Gets a file that represents the root of the SGS instance with the given
217 * ID. Makes a blocking read to the server to check to see if the instance
218 * exists.
219 * @return A CStyxFile representing the root of the instance
220 * @throws StyxException if the instance does not exist
221 */
222 public CStyxFile getInstanceFile(String instanceID) throws StyxException
223 {
224 CStyxFile instanceFile = this.sgsRoot.getFile("instances/" + instanceID);
225 if (!instanceFile.exists())
226 {
227 throw new StyxException("There is no instance with ID " + instanceID
228 + " on this server.");
229 }
230 return instanceFile;
231 }
232
233 /***
234 * Sends message to get all the instances of this SGS. When the instances
235 * arrive, the gotInstances() event will be fired on all registered
236 * change listeners. This method does not block.
237 */
238 public void getInstancesAsync()
239 {
240
241
242 if (!this.gettingInstances)
243 {
244 this.gettingInstances = true;
245 this.instancesFile.readAsync(0);
246 }
247 }
248
249 /***
250 * Required by the CStyxFileChangeListener interface
251 */
252 public synchronized void dataArrived(CStyxFile file, TreadMessage tReadMsg,
253 ByteBuffer data)
254 {
255 if (file == this.instancesFile)
256 {
257
258
259
260
261
262
263
264 if (tReadMsg.getOffset().asLong() == 0)
265 {
266 this.instances.clear();
267 }
268
269
270 long offset = tReadMsg.getOffset().asLong() + data.remaining();
271 if (data.remaining() > 0)
272 {
273
274 StyxBuffer styxBuf = new StyxBuffer(data);
275
276 while(data.hasRemaining())
277 {
278 DirEntry dirEntry = styxBuf.getDirEntry();
279 CStyxFile instance = this.sgsRoot.getFile("instances/" +
280 dirEntry.getFileName());
281 instance.setDirEntry(dirEntry);
282 this.instances.add(instance);
283 }
284
285 this.instancesFile.readAsync(offset);
286 }
287 else
288 {
289
290 CStyxFile[] instancesRoots =
291 (CStyxFile[])this.instances.toArray(new CStyxFile[0]);
292 this.fireGotInstances(instancesRoots);
293
294 this.instancesFile.readAsync(0);
295 }
296 }
297 }
298
299 /***
300 * This callback is called if there has been an error with one of the
301 * asynchronous methods
302 */
303 public void error(CStyxFile file, String message)
304 {
305 if (file == this.cloneFile)
306 {
307 this.fireError("Error creating new instance: " + message);
308 }
309 else if (file == this.instancesFile)
310 {
311 this.fireError("Error reading instances: " + message);
312 }
313 }
314
315 /***
316 * Adds a new SGSChangeListener to the list of registered change listeners.
317 * If the given listener has already been registered, this method does nothing.
318 */
319 public void addChangeListener(SGSChangeListener listener)
320 {
321 synchronized(this.changeListeners)
322 {
323 if (!this.changeListeners.contains(listener))
324 {
325 this.changeListeners.add(listener);
326 }
327 }
328 }
329
330 /***
331 * Removes a SGSChangeListener to the list of registered change listeners.
332 * If the given listener has not been registered, this method does nothing.
333 */
334 public void removeChangeListener(SGSChangeListener listener)
335 {
336 synchronized(this.changeListeners)
337 {
338 this.changeListeners.remove(listener);
339 }
340 }
341
342 /***
343 * Fires the gotInstances() event in all registered change listeners
344 * @param instances The instances belonging to this SGS
345 */
346 private void fireGotInstances(CStyxFile[] instances)
347 {
348 synchronized(this.changeListeners)
349 {
350 for (int i = 0; i < this.changeListeners.size(); i++)
351 {
352 SGSChangeListener listener = (SGSChangeListener)this.changeListeners.get(i);
353 listener.gotInstances(instances);
354 }
355 }
356 }
357
358 /***
359 * Fires the error() event in all registered change listeners
360 * @param message the error message
361 */
362 private void fireError(String message)
363 {
364 synchronized(this.changeListeners)
365 {
366 for (int i = 0; i < this.changeListeners.size(); i++)
367 {
368 SGSChangeListener listener = (SGSChangeListener)this.changeListeners.get(i);
369 listener.error(message);
370 }
371 }
372 }
373 }