Source code: mucode/Group.java
1 /* mucode - A lightweight and flexible mobile code toolkit
2 * Copyright (C) 2000, Gian Pietro Picco
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 package mucode;
19
20 import java.io.*;
21 import java.net.*;
22 import java.util.*;
23 import java.util.zip.*;
24 import java.lang.reflect.*;
25
26 /** The <i>group</i> is the unit of mobility provided by <i>µ</i>Code.
27 * A group provides the programmer with a container that can be filled
28 * arbitrarily with classes and objects, and shipped altogether to another
29 * <i>µ</i>Server. <p>
30 *
31 * No constructor is provided for this class. Instead, the creation of a group
32 * must be requested to a specific <i>µ</i>Server, by invoking the
33 * method {@link mucode.MuServer#createGroup(String, String)
34 * createGroup}. This ties a group to a specific <i>µ</i>Server, which
35 * defines the context where classes are resolved. Thus, for instance, let us
36 * assume that a group <i>g</i> has been created by invoking
37 * <code>createGroup</code> on a <i>µ</i>Server <i>s</i>. If a thread
38 * <i>t</i> requests the insertion in <i>g</i> of a class that is not present
39 * in the private class space of <i>t</i>, then, according to the rules
40 * defined for class loading, the class must be searched in a shared class
41 * space, and more precisely in the shared class space associated with
42 * <i>s</i>. <p>
43 *
44 * Various methods are provided to insert classes in a group. Notably, the
45 * methods that specify classes with their class name (e.g., {@link
46 * #addClass(String)}) rely on the class loader associated to the calling
47 * thread, while methods that provide directly the class object (e.g., {@link
48 * #addClass(Class)}) rely on the class loader that has been originally used to
49 * retrieve the class object itself. <p>
50 *
51 * Objects inserted in a group must implement the <code>Serializable</code>
52 * interface, otherwise a <code>java.io.NotSerializableException</code> will
53 * be raised during the group transfer. The process of serializing an object
54 * <i>o</i> actually determines the copy of the whole object closure. In other
55 * words, <i>o</i>, all the object that are fields of <i>o</i>, the fields of
56 * these objects, and so forth are serialized. The programmer can prevent
57 * serialization of potentially serializable object fields by declaring them
58 * as <code>transient</code>, as explained in the Java Object Serialization
59 * specification (that comes with Sun's JDK). Thread objects can be inserted
60 * in a group, although their execution state is lost during migration and
61 * their execution restarted from scratch at the destination. <P>
62 *
63 * After the content of a group has been packaged, the group can be sent at
64 * destination by invoking the <code>ship</code> method. There, the classes
65 * contained in the group are then inserted in a private {@link
66 * mucode.ClassSpace class space} associated with the group. <p>
67 *
68 * Then, the actions taken by the destination <i>µ</i>Server are
69 * determined by two classes associated with the group: the handler and the
70 * root classes. <br>
71 *
72 * The <i>handler</i> class is used to instantiate an object responsible for
73 * "unpacking" the group and manipulating its contents. Programmers can define
74 * their own specialized group handlers (by implementing the {@link
75 * mucode.GroupHandler} interface), and thus define their own relocation
76 * primitives. (See for instance the implementation of {@link
77 * mucode.abstractions.RelocationHandler} and {@link
78 * mucode.abstractions.MuAgent}.) <br>
79 *
80 * The <i>root</i> class provides additional information for handling a group
81 * by specifying the class on which the relocation operation must be
82 * performed, e.g., to specify which class must be used to spawn a new thread
83 * in the destination <i>µ</i>Server. <p>
84 *
85 * The handler and the root classes can be any class. They can even be the
86 * same class, and do not need to be transmitted along with the group
87 * (although in this case it is responsibility of the programmer to ensure
88 * that they are found at destination). However, in order for the root and
89 * handler classes to be correctly instantiated in the target
90 * <i>µ</i>Server, they must:
91 *
92 * <OL>
93 * <LI> be declared as <code>public</code>, and
94 * <LI> implement a public, parameterless constructor.
95 * </ol>
96 *
97 * Finally, there are two more methods that affect the way the processing of a
98 * group. The method {@link #setDynamicLinkSource(String)} allows to specify a
99 * remote <i>µ</i>Server to be used for remote dynamic linking of
100 * classes. This way, a group can provide just a subset of the classes
101 * required for its handling. If and when additional classes are needed, they
102 * will be downloaded from the dynamic link source, and linked by the class
103 * loader. Note that classes will be downloaded from the source's shared class
104 * space, as the source is identified by a <i>µ</i>Server. The default
105 * value of the parameter is <code>null</code>, which disables remote dynamic
106 * linking altogether. <br>
107 *
108 * The method {@link #setSynchronousTransfer(boolean)} determines whether the
109 * group transfer is performed synchronously or asynchronously. In the first
110 * case, the operation is blocking for the caller, that is suspended until a
111 * return value is received after group handling on the remote
112 * <i>µ</i>Server. Otherwise, the method returns immediately after
113 * group transmission is completed.<P>
114 *
115 * @author <a href="mailto:picco@elet.polimi.it">Gian Pietro Picco</a>
116 * @version 1.0
117 *
118 * @see ClassSpace
119 * @see GroupHandler
120 * @see MuServer
121 * @see MuConstants */
122 public final class Group implements MuConstants {
123 /** Holds the code for the operation requested on the group. */
124 private int opCode = UNKNOWN;
125 /** Holds a parameter for the operation above. */
126 private Serializable[] opPars = null;
127 /** Holds the name of the root of the class hierarchy for to be instantiated
128 * in order to construct the thread on the destination site.
129 */
130 private String rootClassName = ""; // because of serialization
131 private String handlerClassName = null;
132 /** Holds the objects that will be reconstructed on the destination site. It
133 * may contain objects subclassing from <code>Thread</code> or any arbitrary
134 * object. The whole object graph of an object is serialized automatically,
135 * i.e., there is no need to insert explicitly objects contents of the
136 * objects are serialized TO COMPLETE!!!!
137 */
138 Hashtable classes = null;
139 Hashtable objects = null;
140 private String dynLink = null;
141 private boolean synch = false;
142 private String sender = null;
143
144 private transient MuServer server = null;
145
146 private transient Class rootClassObj = null;
147 private transient Class handlerClassObj = null;
148
149 /** Construct an empty group. */
150 Group(MuServer aServer) {
151 opCode = UNKNOWN;
152 opPars = null;
153 rootClassName = "";
154 handlerClassName = null;
155 objects = new Hashtable(10);
156 classes = new Hashtable(10);
157 dynLink = null;
158 synch = false;
159 sender = null;
160 rootClassObj = null;
161 handlerClassObj = null;
162 server = aServer;
163 }
164
165 /** Construct an empty group by assigning root and handler classes. */
166 Group(String root, String handler, MuServer aServer) {
167 this(aServer);
168 setRoot(root);
169 setHandler(handler);
170 }
171
172 /** Construct a group by reading its content from the specified socket. */
173 Group(Socket clientSocket, boolean compressionON, MuServer aServer)
174 throws DuplicateClassException, ClassNotFoundException, IOException {
175
176 server = aServer;
177 MuClassLoader l = server.createClassLoader();
178 server.incomingLoader = l;
179 MuObjectInputStream in;
180 GZIPInputStream gzos;
181 if (compressionON) {
182 gzos = new GZIPInputStream(clientSocket.getInputStream());
183 in = new MuObjectInputStream(gzos, l);
184 } else in = new MuObjectInputStream(clientSocket.getInputStream(), l);
185 server.D("Input stream created, compression: " + compressionON);
186 opCode = in.read();
187 rootClassName = in.readUTF();
188 handlerClassName = in.readUTF();
189 server.D("Group info: root class: " + rootClassName +
190 ", handler class: " + handlerClassName);
191 classes = (Hashtable) in.readObject();
192 server.D("Group info: #classes: " + classes.size());
193
194 // Put all the class bytecodes in the class space.
195 Enumeration keys = classes.keys();
196 while (keys.hasMoreElements()) {
197 String keyName = (String) keys.nextElement();
198 l.getClassSpace().putClassByteCode(keyName,
199 (byte[]) classes.get(keyName));
200 }
201 // Loads all the classes. The corresponding bytecode is already in the
202 // class space.
203 keys = classes.keys();
204 while (keys.hasMoreElements()) {
205 String keyName = (String) keys.nextElement();
206 l.loadClass(keyName);
207 }
208 server.D("All classes in the group have been loaded successfully.");
209 server.D("Contents of the private class space: " +
210 l.getClassSpace().toString());
211
212 if (server.isUbiquitous(rootClassName))
213 rootClassObj = Class.forName(rootClassName);
214 else rootClassObj = l.getClassSpace().getClass(rootClassName);
215
216 // Includes the case when handlerClassName.equals(HANDLER)
217 if (server.isUbiquitous(handlerClassName))
218 handlerClassObj = Class.forName(handlerClassName);
219 else handlerClassObj = l.getClassSpace().getClass(handlerClassName);
220 objects = (Hashtable) in.readObject();
221 opPars = (Serializable[]) in.readObject();
222
223 server.D("Group info: #objects: " + objects.size());
224 keys = objects.keys();
225 while (keys.hasMoreElements()) {
226 String keyName = (String) keys.nextElement();
227 server.D("Key: " + keyName);
228 }
229 dynLink = in.readUTF();
230 if (dynLink.equals(new String())) dynLink = null;
231 else l.dynLinkSource = dynLink;
232 synch = in.readBoolean();
233 sender = in.readUTF();
234 server.D("Group info: dynamic link: " +
235 ((dynLink == null) ? "disabled" : dynLink) +
236 ", synch: " + synch +
237 ", sender: " + sender);
238 server.D("Group successfully retrieved from the stream.");
239 in.close();
240 }
241
242 /**
243 * Return the <i>µ</i>Server that is managing this group. If the group
244 * is not being unpacked after a transfer, the <i>µ</i>Server
245 * coincides with the one that created the group. Otherwise, it is the
246 * <i>µ</i>Server that invoked the {@link GroupHandler#unpack(Group)
247 * unpack} method. */
248 public MuServer getServer() { return server; }
249
250 /** Return the root class object, or <code>null</code> if the group has not
251 yet been migrated. */
252 public Class getRootClass() { return rootClassObj; }
253 /** Return the handler class object, or <code>null</code> if the group has
254 not yet been migrated. */
255 public Class getHandlerClass() { return handlerClassObj; }
256
257 /** Return the name of the root class. */
258 public String getRootName() { return rootClassName; }
259 /** Return the name of the handler class. */
260 public String getHandlerName() { return handlerClassName; }
261
262
263 /**
264 * Retrieve the address of the <i>µ</i>Server from which classes not
265 * contained in the group can be retrieved through dynamic linking. If
266 * <code>null</code>, remote dynamic linking is not allowed.
267 */
268 public String getDynamicLinkSource() {
269 return dynLink;
270 }
271
272 /**
273 * Specifies the address of a <i>µ</i>Server from which classes not
274 * contained in the group can be retrieved through dynamic linking. If
275 * <code>null</code>, remote dynamic linking is not allowed.
276 */
277 public void setDynamicLinkSource(String muserver) {
278 this.dynLink = muserver;
279 }
280
281 /**
282 * Return <code>true</code> if, after the transfer of a group, the control
283 * returns to the caller only after the processing of the group at the
284 * destination <i>µ</i>Server is ended; <code>false</code> otherwise.
285 */
286 public boolean isSynchronousTransfer() { return synch; }
287
288 /**
289 * If the parameter is <code>true</code>, after the transfer of a group the
290 * control returns to the caller only after the processing of the group at
291 * the destination <i>µ</i>Server is ended; if it is
292 * <code>false</code>, the control is returned immediately after the
293 * transfer is complete.
294 */
295 public void setSynchronousTransfer(boolean isSynch) { this.synch = synch; }
296
297
298 /** Set the code of the operation that must be performed on the
299 group. Useful for programmers writing their own relocation
300 primitives. */
301 public void setOperation(int opCode) {
302 this.opCode = opCode;
303 }
304
305 /** Retrieve the code of the operation that must be performed on the
306 group. Useful for programmers writing their own relocation
307 primitives. */
308 public int getOperation() {
309 return opCode;
310 }
311
312 /** Retrieves the object in the group associated with the specified key.
313 * Useful in the implementation of a group handler.
314 *
315 * @param label the key used to retrieve the object.
316 *
317 * @return the object in the group, or <code>null</code> if no object with
318 * the specified key is found.
319 * */
320 public Object getObject(String key) {
321 return objects.get(key);
322 }
323
324 /** Add an object to the group, assigning a key to it for later
325 * retrieval. Neither the key nor the value can be <code>null</code>. If a
326 * key already exists, the old object is overwritten by the new one.
327 *
328 * @param key used for retrieving the object.
329 * @param obj the object to be stored in the group.
330 *
331 * @return the previous value of the specified key in the group, or
332 * <code>null</code> if it did not have one.
333 *
334 * @exception java.lang.NullPointerException if the key or the
335 * object is <code>null</code>.
336 */
337 public Object addObject(String key, Object obj) {
338 return objects.put(key,obj);
339 }
340
341 /** Add the class with the given name to the group. The byte code for the
342 * class is retrieved using the current class loader, i.e., the one used for
343 * the current thread. <br>
344 *
345 * Class loading follows the rules described in the documentation of {@link
346 * mucode.ClassSpace}. If the class definition relies on other classes, it
347 * is responsibility of the programmer to ensure that either these classes
348 * are sent along within the group, or are available on the destination
349 * site, or can be retrieved through remote dynamic linking.
350 *
351 * @param className the name of the class to be inserted in the group. Fully
352 * specified names can be used to resolve name clashes across packages.
353 *
354 * @exception ClassNotFoundException if the class cannot be resolved. */
355 public void addClass(String className) throws ClassNotFoundException {
356 if (!server.isUbiquitous(className)) {
357 try {
358 addClass(className,
359 MuClassLoader.getCurrent().getClassSpace().getClassByteCode(className));
360 } catch(IOException e) {
361 throw new ClassNotFoundException(MuServer.ERRMSG + className);
362 }
363 } else server.D("Class not added to group (ubiquitous): " + className);
364 }
365
366 /**
367 * Add the given class object to the group. The byte code for the class is
368 * retrieved using the class loader that originally loaded it. <br>
369 *
370 * Class loading follows the rules described in the documentation of <a
371 * href="mucode.ClassSpace.html">ClassSpace</a>. If the class definition
372 * relies on other classes, it is responsibility of the programmer to
373 * ensure that either these classes are sent along within the group, or are
374 * available on the destination site, or can be retrieved through remote
375 * dynamic linking.
376 *
377 * @param c the <code>Class</code> object to be inserted in the group.
378 * @exception if the class cannot be resolved. */
379 public void addClass(Class c) throws ClassNotFoundException {
380 String className = c.getName();
381
382 if (!server.isUbiquitous(className)) {
383 try {
384 addClass(className,
385 MuClassLoader.getClassLoader(c).getClassSpace().getClassByteCode(className));
386 } catch(IOException e) {
387 throw new ClassNotFoundException(MuServer.ERRMSG + className);
388 }
389 } else server.D("Class not added to group (ubiquitous): " + className);
390 }
391
392 private void addClass(String className, byte[] bytecode) {
393 server.D("Class added to group: " + className +
394 " (" + bytecode.length + " bytes)");
395 classes.put(className, bytecode);
396 }
397
398 /** Add the classes with the given names to the group. The byte code for the
399 * classes is retrieved using the current class loader, i.e., the one used
400 * for the current thread. <br>
401 *
402 * Class loading follows the rules described in the documentation of <a
403 * href="mucode.ClassSpace.html">ClassSpace</a>. If the class definition
404 * relies on other classes, it is responsibility of the programmer to
405 * ensure that either these classes are sent along within the group, or are
406 * available on the destination site, or can be retrieved through remote
407 * dynamic linking.
408 *
409 * @param className the name of the class to be inserted in the group. Fully
410 * specified names can be used to resolve name clashes across packages.
411 *
412 * @exception ClassNotFoundException if the class cannot be resolved. */
413 public void addClasses(String[] classNames) throws ClassNotFoundException {
414
415 for(int i = 0; i < classNames.length; i++)
416 addClass(classNames[i]);
417 }
418
419 /**
420 * Add the given class objects to the group. The byte code for the classes is
421 * retrieved using the class loader that originally loaded them. <br>
422 *
423 * Class loading follows the rules described in the documentation of <a
424 * href="mucode.ClassSpace.html">ClassSpace</a>. If the class definition
425 * relies on other classes, it is responsibility of the programmer to
426 * ensure that either these classes are sent along within the group, or are
427 * available on the destination site, or can be retrieved through remote
428 * dynamic linking.
429 *
430 * @param c the <code>Class</code> object to be inserted in the group.
431 * @exception ClassNotFoundException if the class cannot be resolved. */
432 public void addClasses(Class[] classes) throws ClassNotFoundException {
433
434 for(int i = 0; i < classes.length; i++)
435 addClass(classes[i]);
436 }
437
438
439 /**
440 * Return the names of the classes contained in the group.
441 */
442 public String[] getClasses() {
443 String[] ret = new String[classes.size()];
444 Enumeration keys = classes.keys();
445 for (int i = 0; i < ret.length; i++)
446 ret[i] = (String) keys.nextElement();
447 return ret;
448 }
449
450 /**
451 * Retrieve the specialized class loader that is handling this group. If
452 * such class loader is the system class loader, this is wrapped into (and
453 * accessible through) a <code>MuClassLoader</code> object.
454 * */
455 public MuClassLoader getClassLoader() {
456 MuClassLoader ret = server.incomingLoader;
457 if (ret == null) ret = MuServer.defaultLoader;
458 return ret;
459 }
460
461 /**
462 * Retrieve the (private) class space associated to this group, or
463 * <code>null</code> if the group has not yet been migrated. */
464 public ClassSpace getClassSpace() {
465 return getClassLoader().getClassSpace();
466 }
467
468 /**
469 * Returns the address (<code>host:port</code>) of the <i>µ</i>Server
470 * that shipped the group, or <code>null</code> if the group has never been
471 * migrated. */
472 public String getSource() { return sender; }
473
474 /** Sends a copy of the group to a destination <i>µ</i>Server.
475 *
476 * @param destination the address of the target <i>µ</i>Server. It is
477 * specified in the format <code><hostname>:<port></code>.
478 *
479 * @exception IOException if the symbolic name given as
480 * destination cannot be resolved in a proper IP address, or some problem
481 * occurs with connection or serialization.
482 * @exception ClassNotFoundException if some of the classes referenced
483 * by the group cannot be resolved.
484 * @return a code describing the outcome of the operation */
485 public int ship(String destination)
486 throws IOException, ClassNotFoundException {
487 Socket socket = null;
488 ServerSocket servSocket = null;
489 ObjectOutputStream os = null;
490 MuObjectInputStream is = null;
491 InetAddress dest = null;
492 int port = -1;
493
494 dest = MuServer.parseHost(destination);
495 port = MuServer.parsePort(destination);
496 if (port == -1) port = SERVER_PORT;
497
498 server.D("Shipping a group to: " + destination);
499 this.dynLink = dynLink;
500 this.synch = synch;
501
502 // There used to be a call to addHandler, essentially doing
503 // addClass(handlerClassName);
504 // I believe is no longer necessary...
505
506 socket = new Socket(dest, port);
507 new Header(GROUP, new String(), server.isCompressionOn()).ship(socket);
508 server.D("Header transmitted.");
509 if (server.isCompressionOn()) {
510 server.D("Compression enabled.");
511 os = new ObjectOutputStream
512 (new GZIPOutputStream(socket.getOutputStream()));
513 } else os = new ObjectOutputStream(socket.getOutputStream());
514 if (socket != null && os != null) {
515 os.write(opCode);
516 os.writeUTF(rootClassName);
517 os.writeUTF(handlerClassName);
518 os.writeObject(classes);
519 os.writeObject(objects);
520 os.writeObject(opPars);
521 if (dynLink == null) os.writeUTF(new String());
522 else os.writeUTF(dynLink);
523 os.writeBoolean(synch);
524 sender = socket.getLocalAddress().getHostAddress() +
525 ":" + server.getPort();
526 os.writeUTF(sender);
527 os.flush();
528 os.close();
529 if (!synch) socket.close();
530 if (synch) {
531 // int ack = is.readInt();
532 // should return an int value instead...
533 // if (ack != OK) throw new MuCodeException();
534 }
535 }
536 // Da sostituire con i valori per synch, ecc.ecc.
537 return 0;
538 }
539
540
541 /** Sets the root class. */
542 void setRoot(String className) {
543 if (className == null) rootClassName = "";
544 else rootClassName = className;
545 }
546
547 /** Sets the handler class. */
548 void setHandler(String className) { handlerClassName = className; }
549
550 }
551