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

Quick Search    Search Deep

Source code: mucode/MuServer.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.lang.reflect.*;
24  
25  /** Provides an abstraction of the run-time support needed to receive and/or
26   * execute mobile code.
27   * 
28   * A <code>MuServer</code> object does not enable per se the handling of
29   * incoming mobile code, rather they provide only the capability to ship
30   * mobile code to other <i>&micro;</i>Servers. In order to accept incoming
31   * mobile code, a thread listening for incoming groups must be explicitly
32   * started in the <i>&micro;</i>Server by calling the {@link #boot}
33   * method. This choice minimizes the overhead introduced by the run-time
34   * support, wasting a thread only when really needed. Multiple servers can be
35   * started on the same Java VM or on different Java VMs on the same host, as
36   * long as their are given different port numbers. However, the name spaces
37   * defined by their (shared) {@link ClassSpace class spaces} are disjoint, and
38   * their sets of ubiquitous classes (see {@link #isUbiquitous} and {@link
39   * MuClassLoader}) may be different.<p>
40   * 
41   * @author <a href="mailto:picco@elet.polimi.it">Gian Pietro Picco</a>
42   * @version 1.0
43   *
44   * @see ClassSpace
45   * @see Group
46   * @see GroupHandler */
47  public class MuServer extends Thread implements MuConstants {
48    static final String ERRMSG = "Impossible to retrieve the bytecode for ";
49    // The following must be declared here to avoid forward reference.
50  
51    private static final String builtinUbiPackages = 
52      "java.* javax.* " + PACKAGE_NAME + ".*";
53  
54    /** The default properties. */
55    protected static Properties defaultProperties = new Properties();
56    static {
57      defaultProperties.put(MESSAGESkey, String.valueOf(true));
58      defaultProperties.put(ERRORSkey, String.valueOf(true));
59      defaultProperties.put(DEBUGkey, String.valueOf(false));
60      defaultProperties.put(COMPRESSIONkey, String.valueOf(false));
61  
62      defaultProperties.put(PORTkey, String.valueOf(SERVER_PORT));
63      defaultProperties.put(TIMEOUTkey, String.valueOf(TIMEOUT));
64      defaultProperties.put(UBICLASSESkey, "");
65      defaultProperties.put(UBIPACKAGESkey, builtinUbiPackages);
66    }
67  
68    /** The properties of this <i>&micro;</i>Server. Properties are stored as
69     * pairs <code><key,value></code>. The set of legal keys (here, the
70     * constantsare shown, rather than the actual key string), their
71     * corresponding default value, and their meaning is: <P>
72     *
73     * <TABLE NOSAVE >
74     * <TR>
75     * <TD><B>Key</B></TD>
76     * <TD><B>Default value</B></TD>
77     * <TD><B>Description</B></TD>
78     * </TR>
79     * <TR>
80     * <TD><code>MESSAGESkey</code></TD>
81     * <TD><code>true</code></TD>
82     * <TD>Activates server messages on the standard output stream.</TD>
83     * </TR>
84     * <TR>
85     * <TD><code>ERRORSkey</code></TD>
86     * <TD><code>true</code></TD>
87     * <TD>Activates errors messages on the standard error stream.</TD>
88     * </TR>
89     * <TR>
90     * <TD><code>DEBUGkey</code></TD>
91     * <TD><code>false</code></TD>
92     * <TD>Activates debugging messages on the standard output stream.</TD>
93     * </TR>
94     * <TR>
95     * <TD><code>COMPRESSIONkey</code></TD>
96     * <TD><code>true</code></TD>
97     * <TD>Activates compression of the serialized stream containing outgoing mobile code.</TD>
98     * </TR>
99     * <TR>
100    * <TD><code>PORTkey</code></TD>
101    * <TD><code>1968</code></TD>
102    * <TD>The port number for incoming connections.</TD>
103    * </TR>
104    * <TR>
105    * <TD><code>TIMEOUTkey</code></TD>
106    * <TD><code>30000</code></TD>
107    * <TD>The timeout for network connections.</TD>
108    * </TR>
109    * </TABLE> <P>
110    *
111    * Properties can be set using the method {@link #setProperty setProperty},
112    * and retrieved using the methods of {@link java.util.Properties}.*/
113   public Properties properties = new Properties(defaultProperties);
114 
115 
116   private int port = SERVER_PORT;
117   private long timeout = TIMEOUT;
118   private boolean compressionON = false;
119   private boolean errorsON = true;
120   private boolean debugON = false;
121   private boolean messagesON = true;
122 
123   /** The class loader used to handle incoming groups. */
124   protected MuClassLoader incomingLoader = null;
125   
126   /** The class loader for all non-mobile threads. There's only one per VM
127    * and doesn't have any server associated with it. */
128   protected static MuClassLoader defaultLoader = new MuClassLoader(null);
129   /** The class space visible outside the server. */
130 
131   // It used to be like this: ClassSpace sharedSpace = new ClassSpace();
132   // However, effectively the "private" class space for the default loader
133   // includes the shared class space, as it is host-wide.
134   protected ClassSpace sharedSpace = defaultLoader.getClassSpace();
135 
136   /** The "persistency root" for class loaders. 
137       Key: Thread object. Value: class loader. */
138   protected static Hashtable loaders = new Hashtable(100);
139 
140   /** Needed to perform the shutdown of the server. */
141   private Thread serverThread = null;
142 
143   /** Holds the list of packages that are ubiquitous, and whose subpackages
144       are considered ubiquitous as well. */
145   private Vector ubiquitousPackagePrefixes = new Vector(11,1);
146   /** Holds the list of ubiquitous packages. */
147   private Vector ubiquitousPackages = new Vector(11,1);
148   /** Holds the list of ubiquitous classes that are not in ubiquitous
149       packages. */
150   private Vector ubiquitousClasses = new Vector(11,1);
151 
152   /** Contains all the directories listed in the <code>CLASSPATH</code> system
153    *  variable. Used for locating a class in the system. */
154   static String[] classpath;
155   /** The static initializer for codeBase. */
156   static {
157     Vector cp = new Vector();
158     StringTokenizer st = 
159       new StringTokenizer(System.getProperty("java.class.path"), 
160                           System.getProperty("path.separator"));
161     while (st.hasMoreTokens()) 
162       cp.addElement(st.nextToken() + File.separatorChar);
163     classpath = new String[cp.size()];
164     cp.copyInto(classpath);
165   }
166  
167   /** The <i>&micro;</i>Server constructor. */
168   public MuServer() {
169     ubiquitousPackagePrefixes.addElement("java.");
170     ubiquitousPackagePrefixes.addElement("javax.");
171     ubiquitousPackagePrefixes.addElement(PACKAGE_NAME + ".");
172     loadPropertyVars();
173   }
174   
175   /**
176    * Set the value of a property. See {@link #properties} for a list of key,
177    * default values, and their meaning. */
178   public void setProperty(String key, String value) {
179     properties.put(key, value);
180     loadPropertyVars();
181   }
182 
183   /**
184    * Loads into the server the properties read from a file.  The format is the
185    * same used by Java, i.e., <code>key=value</code> on separate lines.
186    * The following would be a property file for the default values:
187    * <PRE>
188    * messages=true
189    * errors=true
190    * debug=false
191    * compression=true
192    * port=1968
193    * timeout=30000
194    * ubiclasses=
195    * ubipackages=java.* javax.* mucode.*
196    * </pre>
197    * @param filename the name of the property file. 
198    */
199   public void loadProperties(String filename) 
200     throws FileNotFoundException, IOException {
201     properties.load(new FileInputStream(new File(filename)));
202     properties.list(System.out);
203     loadPropertyVars();
204   }
205 
206   private void loadPropertyVars() {
207     debugON =
208       Boolean.valueOf(properties.getProperty(DEBUGkey)).booleanValue();     
209     messagesON =
210       Boolean.valueOf(properties.getProperty(MESSAGESkey)).booleanValue();
211     errorsON =
212       Boolean.valueOf(properties.getProperty(ERRORSkey)).booleanValue();
213     compressionON = 
214       Boolean.valueOf(properties.getProperty(COMPRESSIONkey)).booleanValue();
215 
216     port =       
217       Integer.valueOf(properties.getProperty(PORTkey)).intValue();
218     timeout = 
219       Long.valueOf(properties.getProperty(TIMEOUTkey)).intValue();
220 
221     StringTokenizer st = 
222       new StringTokenizer(properties.getProperty(UBICLASSESkey), " ");
223     while (st.hasMoreTokens()) 
224       ubiquitousClasses.addElement(st.nextToken());
225 
226     st = new StringTokenizer(properties.getProperty(UBIPACKAGESkey), " ");
227     
228     String packagename = null;
229     while (st.hasMoreTokens()) {
230       packagename = st.nextToken();
231       if (packagename.endsWith(".*"))
232         ubiquitousPackagePrefixes.addElement(packagename.substring(0,packagename.length() - 1));
233       else ubiquitousPackages.addElement(packagename);
234     }
235   }
236   
237   /** Retrieve the socket port the <i>&micro;</i>Server is listening to. */
238   public int getPort() { return port; }
239   /** Retrieve the timeout (in milliseconds) for synchronous transfers. */
240   public long getTimeout() { return timeout; }
241   /** Return <code>true</code>, if GZIP compression is used during outgoing
242       communication, <code>false</code> otherwise. */
243   public boolean isCompressionOn() { return compressionON; }
244   /** Return <code>true</code>, if debug messages are shown on the standard
245       output stream, <code>false</code> otherwise. */
246   public boolean isDebugOn() { return debugON; }
247   /** Return <code>true</code>, if error messages are shown on the standard
248       error stream, <code>false</code> otherwise. */
249   public boolean isErrorsOn() { return errorsON; }
250   /** Return <code>true</code>, if messages are shown on the standard
251       output stream, <code>false</code> otherwise. */
252   public boolean isMessagesOn() { return messagesON; }
253 
254 
255    /** Specify the packages that are considered to be ubiquitous, i.e., whose
256    * classes are assumed to be present on every node. Ubiquitous classes are
257    * the first one to be searched by the class loader. If
258    * <code>packageName</code> ends with <code>.*</code>, all the subpackages
259    * are made ubiquitous as well. Thus, for instance, calling this method by
260    * passing <code>("mypackage")</code> results in considering
261    * ubiquitous only all the classes in <code>mypackage</code>, but not for
262    * instance those in <code>mypackage.mysubpackage</code>. By default, system
263    * classes (<code>java.*</code> and subpackages), Java extensions
264    * (<code>javax.*</code> and subpackages), and <i>&micro;</i>Code classes
265    * (<code>mucode.*</code> and subpackages) are ubiquitous.
266    *
267    * @param package The package name to be added. It is specified using the
268    * standard Java convention for naming packages, using the dot notation.
269    * @param subpackages If <code>true</code>, all subpackages of
270    * <code>packageName</code> are set to ubiquitous as well.  */
271   public final void addUbiquitousPackage(String packageName) {
272     setProperty(UBIPACKAGESkey, 
273                 properties.getProperty(UBIPACKAGESkey) + " " + packageName);
274     insertUbiquitousPackage(packageName);
275   }
276 
277   private final void insertUbiquitousPackage(String packageName) {
278     if (packageName.endsWith(".*")) 
279       ubiquitousPackagePrefixes.addElement(packageName.substring(0,packageName.length() - 1));
280     else ubiquitousPackages.addElement(packageName + ".");
281   }
282 
283   /** Add all the classes contained in a package to the set of ubiquitous
284    * classes, i.e., the classes present on every node. Ubiquitous classes are
285    * the first one to be searched by the class loader. System classes
286    * (<code>java.*</code>, and the primitive types) and &micro;Code classes
287    * (<code>mucode.*</code>) are ubiquitous by default.
288    *
289    * @param className The (fully specified) name of the class to be added. It
290    * is specified using the standard Java convention for naming packages,
291    * using the dot notation.  */
292   public final void addUbiquitousClass(String className) {
293     setProperty(UBICLASSESkey, 
294                 properties.getProperty(UBICLASSESkey) + " " + className);
295     ubiquitousClasses.addElement(className);
296   }
297 
298   /** Return <code>true</code> if a class is ubiquitous, (i.e., it is assumed
299    *  to be present on every node), <code>false</code> otherwise.
300    *
301    * @param className The (fully specified) name of the class to be
302    * searched. It is specified using the standard Java convention for naming
303    * packages, using the dot notation (e.g., <code>java.lang.Thread</code>.  */
304   public final boolean isUbiquitous(String className) {
305     boolean inUPackagePrefixes = false;
306     boolean inUPackages = false;
307     boolean inUClasses = false;
308     Enumeration e = null;
309     String root = null;
310 
311     e = ubiquitousPackagePrefixes.elements();
312     while (!inUPackagePrefixes && e.hasMoreElements()) {
313       root = (String) e.nextElement();
314       inUPackagePrefixes = className.startsWith(root);
315     } 
316     if (!inUPackagePrefixes) {
317       e = ubiquitousPackages.elements();
318       while (!inUPackages && e.hasMoreElements()) {
319         root = (String) e.nextElement();
320         inUPackages = (className.startsWith(root) && 
321                        className.indexOf(".", root.length()) == -1);
322       }
323     }
324     if (!inUPackagePrefixes && !inUPackages) {
325       e = ubiquitousClasses.elements();
326       while (!inUClasses && e.hasMoreElements()) {
327         root = (String) e.nextElement();
328         inUClasses = className.equals(root);
329       } 
330     }
331     return (inUPackagePrefixes || inUPackages || inUClasses);
332   }
333 
334   /** Create a new <code>Group</code> object. This operation must be requested
335    * to a specific <i>&micro;</i>Server rather than being performed by
336    * application code, because resolution of the group classes depend on the
337    * contents of the class spaces associated to a particular
338    * <i>&micro;</i>Server.
339    * 
340    * @param rootClass the root class for the group. 
341    * @param handlerClass the handler for the group. It cannot be
342    * <code>null</code>.
343    * @return the group created by the <i>&micro;</i>Server.  */
344   public final Group createGroup(String root, String handler) {
345     if (handler == null) 
346       throw new IllegalArgumentException("The handler " + 
347                                          "class of a group cannot be null");
348     return new Group(root, handler, this);
349   }
350 
351   /** Retrieve the shared class space associated with the <i>&micro;</i>Server
352    * containing the thread object invoking the method. 
353    */
354   public ClassSpace getSharedClassSpace() {
355     return sharedSpace;
356   }
357 
358   /** Retrieves the private class space associated with the thread object
359    * invoking the method.
360    */
361   public final ClassSpace getPrivateClassSpace() {
362     return ClassSpace.getCurrent();
363   }
364 
365   /** Start the run-time support listening for incoming groups.  */
366   public final void boot() {
367     setName("MuServer");
368     start();
369   }
370 
371   /** Stop the run-time support listening for incoming groups. */
372   public final void shutDown() {
373     stop();
374   }
375 
376   /** Retrieve the <i>&micro;</i>Server managing the thread object invoking
377    * this method. If the thread object is a stationary one, i.e. it has not
378    * been created as a consequence of a migration, the method returns
379    * <code>null</code>.
380    * 
381    * @return the <i>&micro;</i>Server managing the invoking thread. */
382   public final static MuServer getServer() {
383     MuServer r = null;
384     MuClassLoader l = MuClassLoader.getCurrent();
385    
386     if (l != null) r = l.getServer();
387     return r;
388   }
389 
390 
391   /** Retrieve the <i>&micro;</i>Server managing the thread object passed as a
392    * parameter. If the thread object is a stationary one, i.e. it has not
393    * been created as a consequence of a migration, the method returns
394    * <code>null</code>. 
395    * @return the <i>&micro;</i>Server managing the thread object passed as a parameter. */
396   public final static MuServer getServer(Thread t) {
397     MuServer r = null;
398     MuClassLoader l = (MuClassLoader) loaders.get(t);
399    
400     if (l != null) r = l.getServer();
401     return r;
402   }
403 
404 
405   /** Retrieve the <i>&micro;</i>Server associated with the given object. If
406    * the object has not been inside a <i>&micro;</i>Server, the method returns
407    * <code>null</code>.
408    * 
409    * @return the <i>&micro;</i>Server associated to the given object. */
410   public final static MuServer getServer(Object obj) {
411     MuServer ret = null;
412     ClassLoader classLoader = obj.getClass().getClassLoader();
413 
414     if (classLoader instanceof MuClassLoader)
415       ret = ((MuClassLoader) classLoader).getServer();
416     return ret;
417   }    
418 
419   /** Implements the <code>Runnable</code> interface, and contains the
420    * behavior of the server. */
421   public void run() {
422     Socket clientSocket = null;
423     ServerSocket servSocket = null;
424     Group group = null;
425     boolean problem = false;
426     GroupHandler handler = null;
427     Thread t = null;
428     Header header = null;
429 
430     long start = 0;
431     
432     // Accept connections.
433     try {
434       servSocket = new ServerSocket(port);
435     } catch (IOException e) {
436       Err("Cannot listen on port " + port + ". Halting.");
437       System.exit(0);
438     }
439     M("MuServer activated on port "+port);
440     // Continuously process requests.
441     for(;;) {
442       try {
443   clientSocket = servSocket.accept();
444         D("Connection accepted. Reconstructing the header...");
445         start = System.currentTimeMillis();
446         // The server is passed to enable messages.
447         header = new Header(clientSocket);
448       } catch(IOException e) { 
449         Err("Accept failed on port " + port, e);
450       }  
451       // Reads the header to determine whether is a request for dynamic linking
452       // or a normal operation.
453       switch (header.dataType) {
454       case DYN_LINK:
455         D("The connection contains a request for dynamic linking of class: " + 
456           header.className);
457         DataOutputStream os = null;
458         try {
459           os = new DataOutputStream(clientSocket.getOutputStream());
460           D("Contents of shared class space: " 
461             + getSharedClassSpace().toString()); 
462           byte[] requestedBC = 
463             getSharedClassSpace().getClassByteCode(header.className); 
464           if (requestedBC == null) {
465             D("Bytecode for " + header.className + " doesn't exist");
466             os.write(REMOTE_ERROR);
467           } else {
468             os.writeInt(requestedBC.length);
469             os.write(requestedBC);
470             D("Bytecode for " + header.className + 
471               " (" + requestedBC.length+ " bytes) sent."); 
472           }
473         } catch (IOException e) {
474           Err("I/O errors during remote dynamic linking.", e);
475         } finally {
476           try {
477             os.flush();
478             os.close();
479           } catch (IOException e) {
480             Err("I/O errors during remote dynamic linking.", e);
481           }
482         }
483         break;
484       case GROUP:
485         try {
486           D("The connection contains a group. Reconstructing ... " + 
487           header.className);
488           group = new Group(clientSocket, header.compressedStream, this);
489           // If we're not requested to send back error messages, we can close
490           // the connection.
491           if (!group.isSynchronousTransfer()) clientSocket.close();
492         } catch (IOException e) { 
493           Err("I/O errors during group deserialization.", e);
494           problem = true;
495         } catch (ClassNotFoundException e) {
496           Err("Problems during class loading.", e);
497           problem = true;
498         } catch (DuplicateClassException e) {
499           Err("The group is not allowed to overwrite a class "+
500               "in the class space.", e);
501           problem = true;
502         }
503         // If some problem occurs, we print the error on the console and simply
504         // skip the request processing. 
505         if (!problem) {
506           // Can we allow a handler to be loaded dynamically? Why not...
507           // But if it has many classes, should be a separate namespace? Boh.
508           // @@@ Qui c'e' bisogno di mettere dell'altra roba.
509           try {
510             handler = (GroupHandler) group.getHandlerClass().newInstance(); 
511             D("Handler created.");
512           } catch (IllegalAccessException e) { 
513             Err("The handler class must public!.", e); 
514           } catch (InstantiationException e) {
515             Err("The handler class cannot be an interface " +
516                 "or an abstract class.", e); 
517           } 
518           try {
519             D("Unpacking the group.");
520             t = handler.unpack(group);
521             D("Group unpacked.");
522           } catch (MuCodeException e) { 
523             Err("Problems during the unpack() of a group: " + e, e); 
524           }
525           if (t != null) {
526             if(!loaders.containsKey(t))
527               loaders.put(t, incomingLoader);
528             t.start();
529             D("Time to activate the group: " +
530               (System.currentTimeMillis() - start));
531             D("Group thread ("+ t.getName()+ "spawned.");
532           } 
533         } 
534         break;
535       default: Err("Illegal data in group header:" + header.dataType);
536       }
537       incomingLoader = null;
538       cleanup();
539       Thread.yield();
540     }
541   }
542   
543   protected void cleanup() {
544     Enumeration keys = loaders.keys();
545     while (keys.hasMoreElements()) {
546       Thread keyObj = (Thread) keys.nextElement();
547       if (!keyObj.isAlive()) {
548   loaders.remove(keyObj);
549   D("Garbage collected a class loader: " + loaders.size() + " left.");
550       }
551     }
552   }
553   
554   /******* Output Message handling ********/
555 
556   void M(String msg) { 
557     if (messagesON) System.out.println(PACKAGE_NAME + ": " + msg); 
558   }
559   void D(String msg) { if (debugON) M(msg); }
560   void Err(String msg) { 
561     if (errorsON) System.err.println(PACKAGE_NAME + ": " + msg); }
562 
563   void D(String msg, Throwable e) {
564     D(msg);
565     e.printStackTrace();
566   }
567 
568   void Err(String msg, Throwable e) {
569     Err(msg);
570     if (debugON) e.printStackTrace();
571   }
572 
573   /******* NetUtil ********/
574 
575   /** Extracts the host component from an address specified as
576    *  <code>host:port</code>. 
577    *
578    * @param destination The address to be parsed.
579    * @return The host address.
580    *
581    * @exception java.net.UnknownHostException if the result correspond to an
582    * invalid host.*/
583   static InetAddress parseHost(String destination) 
584     throws UnknownHostException {
585     StringTokenizer st = new StringTokenizer(destination, ":");
586     String dest = null;
587     
588     if (st.countTokens() == 1) dest = destination;
589     else dest = (String) st.nextElement();
590     return InetAddress.getByName(dest);
591   }
592 
593   /** Extracts the port component from an address specified as
594    *  <code>host:port</code>. If the port is not specified, the default value
595    *  as specified in the class <code>Properties</code> is returned.
596    *
597    * @param destination The address to be parsed.
598    * @return The port number.
599    *
600    * @exception java.net.UnknownHostException if the result correspond to an
601    * invalid host.*/
602   static int parsePort(String destination) throws UnknownHostException {
603     StringTokenizer st = new StringTokenizer(destination, ":");
604     int port = -1;
605 
606     if (st.countTokens() > 1) {
607         String dummy = (String) st.nextElement();
608         port = Integer.valueOf((String) st.nextElement()).intValue();
609     }
610     return port;
611   }
612 
613   protected MuClassLoader createClassLoader() {
614     incomingLoader = new MuClassLoader(this);
615     return incomingLoader;
616   }
617 }
618 
619 
620 
621 //           try {
622 //             if (group.synch) {
623 //               new ObjectOutputStream(clientSocket.getOutputStream()).writeInt(OK);
624 //               clientSocket.close();
625 //             }
626 //           } catch (IOException e) {
627 //             Err("Problems with socket communication");
628 //             e.printStackTrace();
629 //           }
630 //           group = null;
631 
632 //   e.printStackTrace();
633 //             try {
634 //               if (group.synch) new ObjectOutputStream(clientSocket.getOutputStream()).writeInt(REMOTE_ERROR); 
635 //             } catch (IOException e2) {
636 //               Err("Problems with socket communication");
637 //               e.printStackTrace();
638 //             }
639 //           } catch (IllegalAccessException e) {
640 //             Err("Problems in restarting the thread while executing rSpawnClassClosure()");
641 //             e.printStackTrace();
642 //             try {
643 //               if (group.synch) new ObjectOutputStream(clientSocket.getOutputStream()).writeInt(REMOTE_ERROR); 
644 //             } catch (IOException e2) {
645 //               Err("Problems with socket communication");
646 //               e.printStackTrace();
647 //             }
648 //           } catch (InstantiationException e) {
649 //             Err("Problems in instantiating the thread while executing rSpawnClassClosure()");
650 //             e.printStackTrace();
651 //             try {
652 //               if (group.synch) new ObjectOutputStream(clientSocket.getOutputStream()).writeInt(REMOTE_ERROR); 
653 //             } catch (IOException e2) {
654 //               Err("Problems with socket communication");
655 //               e.printStackTrace();
656 //             }