001    /*
002      Copyright (C) 2001-2003 Lionel Seinturier, Renaud Pawlak.
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser Generaly Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
017    
018    package org.objectweb.jac.core.dist;
019    
020    import java.io.Serializable;
021    import java.lang.reflect.Constructor;
022    import java.lang.reflect.InvocationTargetException;
023    import java.lang.reflect.Method;
024    import java.util.Arrays;
025    import org.apache.log4j.Logger;
026    import org.objectweb.jac.core.ACManager;
027    import org.objectweb.jac.core.Collaboration;
028    import org.objectweb.jac.core.JacLoader;
029    import org.objectweb.jac.core.JacPropLoader;
030    import org.objectweb.jac.core.NameRepository;
031    import org.objectweb.jac.core.ObjectRepository;
032    import org.objectweb.jac.core.SerializedJacObject;
033    import org.objectweb.jac.core.Wrappee;
034    import org.objectweb.jac.core.Wrapping;
035    import org.objectweb.jac.core.rtti.ClassRepository;
036    import org.objectweb.jac.util.ExtArrays;
037    import org.objectweb.jac.util.WrappedThrowableException;
038    
039    /**
040     * RemoteContainer is used as a delegate by daemons (Distd) that hold
041     * remote objects.<p>
042     *
043     * A remote container enables JAC objects to access and manipulate
044     * objects located on other Java VMs.
045     *
046     * @see org.objectweb.jac.core.dist.Distd
047     *
048     * @author <a href="http://www-src.lip6.fr/homepages/Lionel.Seinturier/index-eng.html">Lionel Seinturier</a>
049     * @author <a href="http://cedric.cnam.fr/~pawlak/index-english.html">Renaud Pawlak</a> */
050     
051    public class RemoteContainer implements Serializable {
052        static Logger logger = Logger.getLogger("dist");
053        static Logger loggerSerial = Logger.getLogger("serialization");
054    
055        /** Verbose tells whether information message should be printed or
056            not. */
057        public static boolean verbose = false;
058    
059        /** The name of the container. */
060        public String name = "theContainerWithNoName";
061    
062        /** Property key for the class providing a naming service. */
063            //protected static final String namingClassProp = "org.objectweb.jac.core.dist.namingClass";
064    
065        /** Default class providing a naming service. */
066            //protected static final String namingClassDefaultName = "org.objectweb.jac.core.dist.rmi.RMINaming";
067    
068        /**
069         * This method dynamically binds the local container to a new
070         * remote container.
071         *
072         * @param name the remote container to bind to */
073    
074        public static RemoteContainer bindNewContainer(String name) {
075    
076            logger.debug("binding to a new container "+name);
077            RemoteContainer result = null;
078            // check is the container already bounded
079            result=Topology.get().getContainer(name);
080            if (result != null) {
081                logger.debug("container already bound");
082                return result;
083            } else {
084                result = RemoteContainer.resolve(name);
085                if (result == null) {
086                    throw new RuntimeException("container "+name+" does not exist");
087                }
088            }
089    
090            // get the remote topology      
091            RemoteRef remoteTopology = result.bindTo("JAC_topology");
092    
093            // add the local known containers to the remote topology 
094            RemoteContainer[] rcs = Topology.get().getContainers();
095            remoteTopology.invoke("addContainers", new Object[] {rcs});
096    
097            // add all the remote containers to the local topology
098            rcs = (RemoteContainer[])remoteTopology.invoke("getContainers",ExtArrays.emptyObjectArray);
099            Topology.get().addContainers(rcs);
100            return result;
101        }
102    
103        /**
104         * Create a new container.
105         *
106         * @param verbose  true if information messages are to be printed
107         */
108        public RemoteContainer(boolean verbose) { 
109            RemoteContainer.verbose = verbose; 
110        }
111    
112        /**
113         * Create a new container and instantiates an object.
114         *
115         * @param className the name of the class to instantiate
116         * @param verbose  true if information messages are to be printed
117         */
118        public RemoteContainer(String className, boolean verbose) {
119            try {
120                Class.forName(className).newInstance();
121            } catch (Exception e) {
122                logger.error("RemoteContainer "+className,e);
123            }
124            RemoteContainer.verbose = verbose;
125        }
126       
127        /**
128         * Create a new empty container.
129         * This constructor is needed by the compiler when one tries
130         * to subclass RemoteContainer.
131         *
132         * @see org.objectweb.jac.core.dist.rmi.RMIRemoteContainerStub
133         */   
134        public RemoteContainer() {}
135    
136        /**
137         * Getter method for field name.<p>
138         *
139         * @return  the container name
140         */
141        public String getName() { 
142            return name; 
143        }
144       
145    
146        /**
147         * Setter method for field name.<p>
148         *
149         * Note that this method does not bind the container. It simply
150         * sets the value of field name. Use resolve(name) to bind a
151         * container to a name.<p>
152         *
153         * The name can be an incomplete name since it is completed by the
154         * <code>getFullHostName</code> method.
155         *
156         * @param name the name of the container
157         * @see #resolve(String)
158         * @see Distd#getFullHostName(String) */
159    
160        public void setName(String name) {
161            this.name = Distd.getFullHostName(name);
162        }
163    
164        /**
165         * Return true if the container is local.<p>
166         *
167         * @param name the container name to resolve
168         * @return true if the container is local.
169         * @see #resolve(String)
170         * @see #isLocal() 
171         */
172        public static boolean isLocal(String name) {
173            RemoteContainer rc = RemoteContainer.resolve(name);
174            if (rc==null) 
175                return false;
176            return rc.isLocal();
177        }
178       
179        /**
180         * This class method resolve a container from its name.
181         * The way the resolution is performed is protocol dependent.<p>
182         *
183         * Property org.objectweb.jac.dist.remoteContainerClass defines the class of
184         * the actual RemoteContainer returned (e.g. CORBARemoteContainer).
185         * If the property is not defined or
186         * if the class does not exist, RMIRemoteContainer is the default.
187         *
188         * @param name  the name of the container
189         * @return      the container reference    
190         */   
191        public static RemoteContainer resolve(String name) 
192        {
193            logger.debug("resolving remote container "+name);
194    
195            RemoteContainer remoteContainer = null;
196          
197            /** Lookup the actual remote container class name from the properties. */
198    
199            String namingClassName = JacPropLoader.namingClassName;
200            /*
201              String namingClassName =  null;
202              if (JacObject.props != null) {
203              namingClassName = JacObject.props.getProperty(namingClassProp);
204              }
205    
206            */
207            if (namingClassName == null) {
208                namingClassName = JacPropLoader.namingClassDefaultName;
209            }
210              
211            /** Invoke the class method resolve on the above mentioned class. */
212          
213            try {
214                Class namingClass = Class.forName(namingClassName);
215    
216                Method meth =
217                    namingClass.getMethod( "resolve", new Class[]{String.class} );
218    
219                remoteContainer =
220                    (RemoteContainer) meth.invoke(null, new Object[]{name});
221          
222            } catch (Exception e) {
223                logger.error("resolve "+name,e);
224                throw new RuntimeException(e.toString());
225            }
226    
227            logger.debug("resolve is returning " + remoteContainer);
228          
229            return remoteContainer;
230        }
231    
232    
233        /**
234         * Return true if the container is local.<p>
235         *
236         * RemoteContainer objects are always locals. RemoteContainer
237         * stubs objects (see RMIRemoteContainerStub) may not always be
238         * local.
239         *
240         * @return true if the container is local.
241         * @see Distd#containsContainer(RemoteContainer) */
242    
243        public boolean isLocal() {
244            return Distd.containsContainer( this );
245        }
246    
247        /**
248         * This method instantiates a className object.<p>
249         *
250         * Clients call it to remotely instantiate an object.
251         * <code>instantiates</code> creates an object and returns its
252         * local JAC index.<p>
253         *
254         * The object's name can be null. If not, the object is
255         * registered into the local repository.<p>
256         *
257         * The Aspect Component Manager is upcalled to notify the system
258         * from a remote instantiation.<p>
259         *
260         * @param name       the name of the object
261         * @param className  the class name to instantiate
262         * @param args       initialization arguments for the instantiation
263         * @param fields     the object fields that are part of the state
264         * @param state      the state to copy
265         * @return the index of the instantiated object
266         *
267         * @see org.objectweb.jac.core.ACManager#whenRemoteInstantiation(Wrappee,String) 
268         */
269        public int instantiates(String name, String className, Object[] args,
270                                String[] fields, byte[] state, byte[] collaboration) 
271        {
272            /** Set the local interaction */
273            Collaboration.set(
274                (Collaboration)SerializedJacObject.deserialize(collaboration));
275    
276            logger.debug("remote instantiation of "+className);
277    
278            Object substance = null;
279          
280            try {
281    
282                /** Instantiate the base object */
283             
284                Class substanceClass = null;
285             
286                try {
287                    /** Begin critical section */
288                    // WAS THIS REALLY USEFUL ????
289                    //JacObject.remoteInstantiation = true;            
290                
291                    substanceClass = Class.forName(className);
292    
293                    if (args == null) {
294                        Constructor c = substanceClass.getConstructor ( 
295                            new Class[] {});
296                        substance = c.newInstance(new Object[] {});
297                    } else {
298                        throw new InstantiationException(
299                            "No arguments allowed when remote instantiation");
300                    }
301    
302                    /** End critical section */
303                    // USEFULL ????
304                    ///JacObject.remoteInstantiation = false;
305                
306                } catch(Exception e) {
307                    // USEFUL????
308                    //JacObject.remoteInstantiation = false;
309                    logger.error("Instantiates "+name+" "+className+Arrays.asList(args),e);
310                    return -1;
311                }         
312    
313                /** Copy the transmitted state if needed */
314                
315                if (fields != null && state != null) {
316                    Object[] dstate = (Object[])
317                        SerializedJacObject.deserialize(state);
318                
319                    ObjectState.setState(
320                        substance,new Object[] { fields, dstate }
321                    );
322                    loggerSerial.debug("deserialized fields="+
323                              Arrays.asList(fields)+"; state="+Arrays.asList(dstate));
324                } else {
325                    loggerSerial.debug("deserialize: transmitted state is empty");
326                }
327             
328                if (verbose) {
329                    System.out.println(
330                        "--- A " + className + " instance has been created (" + 
331                        ObjectRepository.getMemoryObjectIndex(substance) + ") ---"
332                    );
333                }
334             
335            } catch (Exception e) { 
336                logger.error("Instantiates "+name+" "+className+Arrays.asList(args),e);
337                return -1; 
338            }
339    
340            /** Upcall the ACManager to notify it from a remote instatiation. */
341            ((ACManager) ACManager.get()).whenRemoteInstantiation( (Wrappee) substance, name );
342          
343            return ObjectRepository.getMemoryObjectIndex(substance);      
344        }
345    
346        /**
347         * Copy a state into a base object.
348         *
349         * @param index   the base object index (see <code>org.objectweb.jac.core.JacObject</code>)
350         * @param fields  the object fields that are part of the state
351         * @param state   the state to copy
352         */
353        
354        public void copy(String name, int index, String[] fields, byte[] state,
355                         byte[] collaboration) {
356    
357            /** Set the local interaction */
358            Collaboration.set((Collaboration)SerializedJacObject.deserialize(collaboration));
359            ObjectState.setState(ObjectRepository.getMemoryObject(index), new Object[] {
360                fields, (Object[]) SerializedJacObject.deserialize(state) } );
361    
362            /** upcall the acmanager ??? */
363        }
364       
365       
366        /**
367         * Invoke a method on a base object.<p>
368         *
369         * The base object is the remote counterpart of a local object that
370         * has been remotely instantiated by the <code>Distd</code> daemon.
371         *
372         * @param index       the callee index (see org.objectweb.jac.core.JacObject)
373         * @param methodName  the called method name
374         * @param args        the called method arguments
375         * @param collaboration the collaboration coming from the calling host
376         * @return the result as an array of bytes (to be deserialized) */
377       
378        public byte[] invoke(int index, String methodName, byte[] args,
379                             byte[] collaboration) {
380    
381            if (args != null) 
382                Distd.inputCount += args.length;
383    
384            Object[] methodArgs =
385                (Object[])SerializedJacObject.deserializeArgs(args);
386          
387            Object ret = null;
388    
389            try {
390                Object substance = ObjectRepository.getMemoryObject(index);
391                Class substanceClass = substance.getClass();
392             
393                /** Set the local interaction */
394                Collaboration.set((Collaboration)SerializedJacObject.
395                                  deserialize(collaboration));
396    
397                logger.debug("remote invocation of " + methodName + " on "+
398                             substance + "("+
399                             NameRepository.get().getName(substance)+")");
400             
401                ret = ClassRepository.invokeDirect(substanceClass, methodName,
402                                                   substance, methodArgs);
403             
404            } catch (InvocationTargetException e) {
405                if (e.getTargetException() instanceof WrappedThrowableException) {
406                    throw (RuntimeException)e.getTargetException();
407                } else {
408                    logger.error("invoke "+index+"."+methodName+
409                                 Arrays.asList(methodArgs),e);
410                }
411            } catch (Exception e) {
412                logger.error("invoke "+index+"."+methodName+
413                             Arrays.asList(methodArgs),e);
414            }
415            /*catch( IllegalAccessException e ) {
416              e.printStackTrace();
417              return null;
418              }
419              catch ( NoSuchMethodException e ) {
420              System.out.println ( "Error: invoke '" + methodName + 
421              "' with " + Arrays.asList(methodArgs) + "." );
422              }*/
423    
424            byte[] sret =  SerializedJacObject.serialize(ret);
425    
426            if (sret != null) 
427                Distd.outputCount += sret.length;
428            logger.debug("remote invocation ok, returning "+ret);
429            return sret;
430        }
431    
432        public byte[] invokeRoleMethod(int index, String methodName, byte[] args,
433                                       byte[] collaboration) {
434    
435            if (args != null) 
436                Distd.inputCount += args.length;
437    
438            Object[] methodArgs =
439                (Object[])
440                SerializedJacObject.deserialize(args);
441          
442            Object ret = null;
443    
444            try {
445             
446                Object substance = ObjectRepository.getMemoryObject(index);
447                Class substanceClass = substance.getClass();
448             
449                /** Set the local interaction */
450    
451                logger.debug(Collaboration.get().toString());
452    
453                Collaboration.set((Collaboration)SerializedJacObject
454                                  .deserialize(collaboration));
455             
456             
457                logger.debug("remote invocation of role method " + methodName + " on "+
458                          substance + "("+
459                          NameRepository.get().getName(substance)+")"+
460                          " - "+this);
461                logger.debug(Collaboration.get().toString());
462             
463                ret=Wrapping.invokeRoleMethod((Wrappee)substance,methodName,methodArgs);
464             
465                logger.debug(Collaboration.get().toString());
466            } catch(Exception e) {
467                if (e instanceof WrappedThrowableException) {
468                    throw (RuntimeException)e;
469                } else {
470                    logger.error("invokeRoleMethod "+index+"."+methodName+
471                                 Arrays.asList(methodArgs),e);
472                }
473            }
474    
475            byte[] sret = SerializedJacObject.serialize(ret);
476    
477            if (sret != null) 
478                Distd.outputCount += sret.length;
479    
480            logger.debug("remote invocation ok, returning "+ret);
481    
482            return sret;
483        }
484    
485    
486        /**
487         * Gets the bytecode for the given class by using the current
488         * loader.<p>
489         *
490         * This method is used by distributed loaders to fetch classes
491         * bytecodes from JAC remote containers that are classes
492         * repositories.<p>
493         *
494         * @param className the class name
495         * @return the corresponding bytecode
496         */
497    
498        public byte[] getByteCodeFor(String className) {
499            ClassLoader cl = getClass().getClassLoader();
500            if (cl instanceof JacLoader) {
501                logger.debug("getByteCodeFor "+className+" bootstraping");
502                /** we are a bootstraping site */
503                try {
504                    if (Wrappee.class.isAssignableFrom(Class.forName(className))) {
505                        //( ((JacLoader)cl).getClassPath().readClassfile( className ) );
506                        return null; 
507                    }
508                } catch ( Exception e ) { 
509                    logger.error("Failed to fetch bytecode for "+className,e);
510                }
511            } else if (cl instanceof DistdClassLoader) {
512                logger.debug("getByteCodeFor "+className+" distd");
513                ((DistdClassLoader)cl).getByteCode(className);
514            }
515            logger.debug("getByteCodeFor "+className+" -> null");
516            return null;
517        }
518    
519        /**
520         * Returns a remote reference on the object corresponding to the
521         * given name.<p>
522         *
523         * Note: This method has been added for practical reason but
524         * introduces a implicit dependency towards the naming aspect. If
525         * the naming aspect is not woven, then this method does not mean
526         * anything and the returned remote reference is null.
527         *
528         * @param name the name of the object to bind to
529         * @return the corresponding remote reference */
530    
531        public RemoteRef bindTo(String name) {
532            Class nrc = null;
533            Method m = null;
534            Object nr = null;
535            Object o = null;
536    
537            logger.debug("client is binding to "+name);
538    
539            o = NameRepository.get().getObject(name);
540            if (o == null) {
541                logger.debug("object "+name+" not found on this container");
542                return null;
543            }
544           
545            RemoteContainer rc = RemoteContainer.resolve(this.name);
546            int index = ObjectRepository.getMemoryObjectIndex(o);
547    
548            RemoteRef rr = RemoteRef.create(name, rc, index);
549          
550            logger.debug("returning remote reference "+rr);
551    
552            return rr;
553        }
554    
555        /**
556         * Check the equality of two containers (on the name).<p>
557         *
558         * @param container the container to check
559         * @return true if the given container equals to the current container
560         */
561    
562        public boolean equals(Object container) {
563            if (!(container instanceof RemoteContainer)) 
564                return false;
565            RemoteContainer c = (RemoteContainer)container;
566          
567            return name.equals(c.getName());
568        }
569       
570        /**
571         * Return a textual representation of a container (its name).
572         *
573         * @return a textual representation of the container */
574    
575        public String toString() {
576            return "Container " + getName();
577        }
578    
579        /**
580         * Launches a administration GUI on a remote container.
581         */
582    
583        public void launchRemoteGUI() {
584            RemoteContainer rc = RemoteContainer.resolve( name );
585            logger.debug("launch remote GUI: container = " + rc );
586            RemoteRef remoteTopology = rc.bindTo( "JAC_topology" );
587            logger.debug("remote topology = " + remoteTopology );
588    
589            /*Object guiAC = ACManager.get().getObject("gui");
590              Object display = null;
591              String programName = null;
592              try {
593              display = guiAC.getClass().getMethod( "getDisplay", new Class[] { String.class } )
594              .invoke( guiAC, new Object[] { "admin" } );
595              programName = (String)display.getClass().getMethod("getProgram",ExtArrays.emptyClassArray)
596              .invoke( display, ExtArrays.emptyObjectArray);
597              } catch (Exception e) { e.printStackTrace(); return; }*/
598            remoteTopology.invoke( "launchGUI", ExtArrays.emptyObjectArray );
599        }
600       
601    }