Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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>&micro;</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>&micro;</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>&micro;</i>Server, by invoking the
33   * method {@link mucode.MuServer#createGroup(String, String)
34   * createGroup}. This ties a group to a specific <i>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</i>Server that is managing this group. If the group
244    * is not being unpacked after a transfer, the <i>&micro;</i>Server
245    * coincides with the one that created the group. Otherwise, it is the
246    * <i>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</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>&micro;</i>Server.
475    *
476    * @param destination the address of the target <i>&micro;</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