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 General 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.util.Arrays;
023    import org.apache.log4j.Logger;
024    import org.objectweb.jac.core.Collaboration;
025    import org.objectweb.jac.core.JacPropLoader;
026    import org.objectweb.jac.core.ObjectRepository;
027    import org.objectweb.jac.core.SerializedJacObject;
028    import org.objectweb.jac.util.WrappedThrowableException;
029    
030    /**
031     * <code>RemoteRef</code> stores the reference of a remote object.
032     * The way the remote object is accessed depends on
033     * the underlying communication protocol (eg CORBA or RMI).<p>
034     *
035     * Supporting a new communication protocol requires to subclass RemoteRef
036     * (eg RMIRemoteRef or CORBARemoteRef) and to implement the resolve and
037     * reresolve methods.<p>
038     *
039     * @see org.objectweb.jac.core.dist.RemoteRef#resolve(java.lang.String)
040     * @see org.objectweb.jac.core.dist.RemoteRef#reresolve()
041     * @see org.objectweb.jac.core.dist.rmi.RMIRemoteRef
042     *
043     * @author <a href="http://www-src.lip6.fr/homepages/Lionel.Seinturier/index-eng.html">Lionel Seinturier</a>
044     * @author <a href="http://cedric.cnam.fr/~pawlak/index-english.html">Renaud Pawlak</a>
045     */ 
046    public class RemoteRef implements Serializable {
047        static Logger logger = Logger.getLogger("dist");
048        static Logger loggerSerial = Logger.getLogger("serialization");
049    
050        /** The reference of the container that handles the remote object. */
051        protected RemoteContainer remCont;
052       
053        /** The index (see org.objectweb.jac.core.JacObject) of the remote object. */
054        protected int remIndex;
055    
056        /** The name of the remote object that is of will be associated to
057           the remote ref. */
058        protected String name = null;
059    
060        /** Property key for the remote reference class. */
061            //protected static final String remRefClassProp = "Jac.remoteRefClass";
062       
063        /** Default remote reference class. */
064            //protected static final String remRefDefaultClassName = "org.objectweb.jac.core.dist.rmi.RMIRemoteRef";
065    
066        /**
067         * This class method returns a new RemoteRef object.<p>
068         *
069         * Property org.objectweb.jac.dist.remoteRefClass defines the class of the actual
070         * RemoteRef returned (e.g. CORBARemoteRef). If the property is not
071         * defined or if the class does not exist, RMIRemoteRef is the
072         * default.<p>
073         *
074         * @param name the name to give to the remote ref (should be equal
075         * to the pointed object name)
076         * @return a new RemoteRef object
077         */
078        public static RemoteRef create(String name) {
079          
080            RemoteRef remoteRef = null;
081            String remoteRefClassName = JacPropLoader.remoteRefClassName;
082    
083            try {
084                Class remoteRefClass = Class.forName(remoteRefClassName);
085                remoteRef = (RemoteRef) remoteRefClass.newInstance();
086            } catch (Exception e) { 
087                logger.error("create "+name,e); 
088            }
089              
090            remoteRef.setName(name);
091    
092            return remoteRef;
093        }
094    
095        /**
096         * This class method returns a remote reference
097         * for an existing remote JAC object.<p>
098         *
099         * Property org.objectweb.jac.dist.remoteRefClass defines the class of the actual
100         * RemoteRef returned (e.g. CORBARemoteRef). If the property is not
101         * defined or if the class does not exist, RMIRemoteRef is the
102         * default.<p>
103         *
104         * @param name the remote object name (can be null if not needed)
105         * @param remCont the remote container where the JAC object is
106         * instantiated
107         * @param remIndex the index of the JAC object in the remote
108         * container
109         * @return the remote reference of the JAC object
110         */
111        public static RemoteRef create(String name,
112                                       RemoteContainer remCont,
113                                       int remIndex) 
114        {
115            logger.debug("creating remote ref " + name + ", " + 
116                         remCont + ", " + remIndex);
117    
118            RemoteRef remoteRef = null;
119            String remoteRefClassName = JacPropLoader.remoteRefClassName;
120            /*
121              String remoteRefClassName = null;
122    
123              if (JacObject.props != null) {
124              remoteRefClassName = JacObject.props.getProperty(remRefClassProp);
125              }
126          
127              if ( remoteRefClassName == null ) {
128              remoteRefClassName = "org.objectweb.jac.core.dist.rmi.RMIRemoteRef";
129              }
130            */
131            try {
132                Class remoteRefClass = Class.forName(remoteRefClassName);
133             
134                Constructor c =
135                    remoteRefClass.getConstructor( 
136                        new Class[] { RemoteContainer.class, int.class }
137                    );
138                remoteRef =
139                    (RemoteRef)c.newInstance(
140                        new Object[] { remCont, new Integer(remIndex) });
141            } catch(Exception e) { 
142                logger.error("create "+name+","+remCont+","+remIndex,e);
143            }
144          
145            remoteRef.setName(name);
146            logger.debug("returning remote ref " + remoteRef);
147          
148            return remoteRef;
149        }
150    
151        /**
152        * Create a remote reference from a local JAC object (in order, for
153        * example, to transmit it to a remote container).<p>
154        * 
155        * @param name the name to be given to the remote reference
156        * @param localObject the object to create the reference from
157        * @return the new remote reference */
158    
159        public static RemoteRef create(String name, Object localObject) {
160            logger.debug("creating remote ref "+name+" for "+localObject);
161            return RemoteRef.create(
162                name, RemoteContainer.resolve(Distd.getLocalContainerName()),
163                ObjectRepository.getMemoryObjectIndex(localObject));
164        }   
165       
166        /**
167        * This is a full constructor for RemoteRef.<p>
168        *
169        * @param remCont the ref of the container that handles the remote
170        * object.
171        * @param remIndex the index of the remote object */
172        
173        public RemoteRef(RemoteContainer remCont, int remIndex) {
174            this.remCont = remCont;
175            this.remIndex = remIndex;
176        }
177       
178       
179        /**
180        * This is a more friendly constructor for RemoteRef.<p>
181        *
182        * @param remCont the name of the container that handles the remote
183        * object.
184        * @param remIndex the index of the remote object.  */
185       
186        public RemoteRef(String remCont, int remIndex) {   
187            this.remCont = resolve(remCont);
188            this.remIndex = remIndex;
189        }
190    
191    
192        /**
193        * Empty default constructor for RemoteRef needed by the compiler whenever
194        * RemoteRef is subclasses (eg RMIRemoteRef or CORBARemoteRef).<p>
195        *
196        * This constructor should never be called in other cases (this is why it is
197        * protected).<p>
198        */
199       
200        protected RemoteRef() {}
201       
202       
203        /**
204        * The getter method for the remCont field.<p>
205        *
206        * It returns a the container that handles the remote object. If
207        * the remote container is local, then the object pointed by the
208        * remote reference is also local.<p>
209        *
210        * @return the remCont field value */
211        
212        public RemoteContainer getRemCont() { return remCont; }
213    
214    
215        /**
216        * The setter method for the name.<p>
217        *
218        * @param name the new name */
219    
220        public void setName(String name) {
221            this.name = name;
222        }
223       
224        /**
225        * The getter method for the name.
226        * 
227        * @return the reference name */
228       
229        public String getName() {
230            return name;
231        }
232    
233        /**
234        * The getter method for the <code>remIndex</code> field.<p>
235        *
236        * <code>remIndex</code> is the index (see org.objectweb.jac.core.JacObject) of
237        * the remote object.
238        *
239        * @return the remIndex field value */
240        
241        public int getRemIndex() { return remIndex; }
242       
243        /**
244        * This method resolves a container from a container name.<p>
245        *
246        * Its implementation is protocol dependent (eg RMI or CORBA).
247        * Most concrete implementations of this method (see RMIRemoteRef)
248        * simply delegate the resolution to a resolve() class method defined
249        * in a container class (see RMIRemoteContainer).<p>
250        *
251        * @param contName the name of the container
252        * @return the container
253        *
254        * @see org.objectweb.jac.core.dist.rmi.RMIRemoteRef#resolve(String)
255        */
256        public RemoteContainer resolve(String contName) { return null; }
257    
258        /**
259        * This method re-gets the reference of a remote container.<p>
260        *
261        * Its implementation is protocol dependent (eg RMI or CORBA).
262        * Some communication protocols (eg CORBA) do not linearalize
263        * remote references in a standard way. Thus a remote reference
264        * may need to be adapted whenever it is transmitted.<p>
265        *
266        * This method is called when a remote reference
267        * is recieved by a <code>RemoteContainer</code>.<p>
268        *
269        * @return the container reference
270        *
271        * @see org.objectweb.jac.core.dist.rmi.RMIRemoteRef#reresolve()
272        */   
273        public RemoteContainer reresolve() { return null; }
274       
275    
276        /** Following constants are property keys used by remoteNew(). */
277       
278        final protected static String toAdaptProp = "org.objectweb.jac.toAdapt";
279    
280    
281        /**
282        * Remotely instantiate a class.<p>
283        *
284        * Make the current <code>RemoteRef</code> instance reference the
285        * created object.<p>
286        *
287        * @param host the host machine
288        * @param clName the class to instantiate */
289       
290        public void remoteNew(String host, String clName) {
291            remoteNewWithCopy(host, clName, null, null, null);
292        }
293       
294        /**
295        * Remotely instantiate a class.<p>
296        *
297        * Make the current <code>RemoteRef</code> instance reference the
298        * created object.<p>
299        *
300        * @param host the host machine
301        * @param clName the class to instantiate
302        * @param args initialization arguments for the instantiation */
303       
304        public void remoteNew(String host, String clName, Object[] args) {
305            remoteNewWithCopy(host, clName, args, null, null);
306        }
307       
308        /**
309        * Remotely instantiate a class.<p>
310        *
311        * Make the current <code>RemoteRef</code> instance reference the
312        * created object and copy the state of the given object into the
313        * remote object.<p>
314        *
315        * All the fields of the object are copied.<p>
316        *
317        * @param host the host machine
318        * @param clName the class to instantiate
319        * @param src the source object containing the data to copy */
320       
321        public void remoteNewWithCopy(String host, String clName, Object src) {
322            remoteNewWithCopy(host, clName, null, src, null);
323        }
324       
325        /**
326        * Remotely instantiate a class.<p>
327        *
328        * Make the current <code>RemoteRef</code> instance reference the
329        * created object and copy the state of the given object into the
330        * remote object.<p>
331        * 
332        * All the fields of the object are copied.<p>
333        *
334        * @param host the host machine
335        * @param clName the class to instantiate
336        * @param args initialization arguments for the instantiation
337        * @param src the source object containing the data to copy */
338       
339        public void remoteNewWithCopy(String host,
340                                      String clName,
341                                      Object[] args,
342                                      Object src) {
343            remoteNewWithCopy(host, clName, args, src, null);
344        }
345    
346    
347        /**
348        * Remotely instantiate a class.<p>
349        *
350        * Make the current <code>RemoteRef</code> instance reference the
351        * created object and copy the state of the given object into the
352        * remote object.<p>
353        *
354        * Only specified fields are copied.<p>
355        *
356        * @param host the host machine
357        * @param clName the class to instantiate
358        * @param src the source object containing the data to copy
359        * @param fieldsName the fields name to copy */
360       
361        public void remoteNewWithCopy(String host,
362                                      String clName,
363                                      Object src,
364                                      String[] fieldsName) {
365            remoteNewWithCopy(host, clName, null, src, fieldsName);
366        }
367       
368       
369        /**
370        * Remotely instantiate a class.
371        *
372        * Make the current <code>RemoteRef</code> instance reference the
373        * created object and copy the state of the given object into the
374        * remote object.<p>
375        *
376        * Only specified fields are copied.<p>
377        *
378        * @param host the host machine
379        * @param clName the class to instantiate
380        * @param args initialization arguments for the instantiation
381        * @param src the source object containing the data to copy
382        * @param fieldsName the fields name to copy */
383       
384        public void remoteNewWithCopy(String host, 
385                                      String clName, 
386                                      Object[] args,
387                                      Object src,
388                                      String[] fieldsName) 
389        {
390            /**
391             * Resolving the host consists in getting the concrete remote reference
392             * (e.g. RMIRemoteRef or CORBARemoteRef) associated to the remote
393             * container where the remote instantiation is to be performed.
394             */
395          
396            remCont = resolve(host);
397    
398            /** Prepare the fields name and value */
399          
400            Object[] fieldsValue = null;
401          
402            if (src!=null) {
403                if ( fieldsName == null ) {
404                    Object[] state = ObjectState.getState(src);
405                    fieldsName = (String[])state[0];
406                    fieldsValue = (Object[])state[1];
407                }
408                else {
409                    Object[] state = ObjectState.getState(src,fieldsName );
410                    fieldsName = (String[])state[0];
411                    fieldsValue = (Object[])state[1];
412                }
413            }
414    
415            if (fieldsName!=null)
416                loggerSerial.debug(
417                    "serializing fields "+Arrays.asList(fieldsName)+
418                    " values = "+Arrays.asList(fieldsValue));
419    
420            byte[] sfieldsValue = SerializedJacObject.serialize(fieldsValue);
421            if (sfieldsValue!=null) 
422                Distd.outputCount += sfieldsValue.length;      
423    
424          /** Remotely create an instance of className */
425    
426            remIndex =
427                remCont.instantiates(
428                    name, clName, args, fieldsName,
429                    sfieldsValue, 
430                    SerializedJacObject.serialize(Collaboration.get())
431                );
432    
433        }
434    
435        /**
436         * Copy the state of a given object into the remote object
437         * referenced by the current reference.<p>
438         *
439         * All the fields of the object are copied.<p>
440         *
441         * @param src the source object containing the data to copy 
442         */
443        public void remoteCopy(Object src) {
444    
445            Object[] state = ObjectState.getState(src);
446            byte[] sstate = SerializedJacObject.serialize( (Object[]) state[1] );
447    
448            if ( sstate != null ) Distd.outputCount += sstate.length;
449    
450            /** Perform the remote copy */
451            remCont.copy(
452                name, remIndex,
453                (String[]) state[0],
454                sstate,
455                SerializedJacObject.serialize(Collaboration.get())
456            );
457        }
458       
459       
460        /**
461         * Copy the state of a given object into the remote object
462         * referenced by the current reference.<p>
463         *
464         * Only specified fields are copied.<p>
465         *
466         * @param src the source object containing the data to copy
467         * @param fieldsName the fields name to copy
468         */
469        public void remoteCopy(Object src, String[] fieldsName) {
470    
471            Object[] state = ObjectState.getState(src,fieldsName );
472            byte[] sstate = SerializedJacObject.serialize( (Object[]) state[1] );
473    
474            if ( sstate != null ) Distd.outputCount += sstate.length;
475    
476            /** Perform the remote copy */
477    
478            remCont.copy(
479                name,
480                remIndex,
481                (String[]) state[0],
482                sstate,
483                SerializedJacObject.serialize(Collaboration.get())
484            );
485        }
486       
487        /**
488         * Forward a call to the referenced object.<p>
489         *
490         * @param methodName the called method name
491         * @param methodArgs the called method arguments
492         * @return the result
493         */
494        public Object invoke(String methodName, Object[] methodArgs) {
495            return invoke(methodName,methodArgs,null);
496        }
497       
498        /**
499         * Forward a call to the referenced object.<p>
500         *
501         * @param methodName the called method name
502         * @param methodArgs the called method arguments
503         * @return the result
504         */
505        public Object invoke(String methodName, Object[] methodArgs, 
506                             Boolean[] refs) 
507        {
508            logger.debug("invoking "+methodName+" on "+this);
509    
510            byte[] ret = null;
511            byte[] args = SerializedJacObject.serializeArgs(methodArgs,refs);
512    
513            if (args != null) 
514                Distd.outputCount += args.length;
515    
516            //      System.out.println("Collab = "+Collaboration.get());
517            try {
518                ret = remCont.invoke(
519                    remIndex,
520                    methodName,
521                    args,
522                    SerializedJacObject.serialize(Collaboration.get())
523                );
524            } catch (Exception e) {
525                if (e instanceof WrappedThrowableException) {
526                    throw (RuntimeException) e;
527                } 
528                logger.error("Failed to remotely invoke "+methodName+": "+e);
529            }
530    
531            if ( ret != null ) Distd.inputCount += ret.length;
532    
533            return SerializedJacObject.deserialize( ret );
534        }
535    
536        /**
537         * Forward a role method call to the referenced object.<p>
538         *
539         * @param methodName the called role method name
540         * @param methodArgs the called role method arguments
541         * @return the result 
542         */
543        public Object invokeRoleMethod(String methodName,Object[] methodArgs) {
544    
545            logger.debug("invoking role method "+methodName+" on "+
546                         this+"-"+remCont);
547    
548            byte[] ret = null;
549            byte[] args = SerializedJacObject.serialize(methodArgs);
550    
551            if (args != null) Distd.outputCount += args.length;
552          
553            try {
554                ret = remCont.invokeRoleMethod(
555                    remIndex,
556                    methodName,
557                    args,
558                    SerializedJacObject.serialize(Collaboration.get())
559                );
560            } catch ( Exception e ) {
561                if ( e instanceof WrappedThrowableException ) {
562                    throw (RuntimeException) e;
563                } 
564                logger.error("Failed to remotely invoke "+methodName+": "+e);
565            }
566    
567            if ( ret != null ) Distd.inputCount += ret.length;
568    
569            return SerializedJacObject.deserialize(ret);
570        }
571    
572        /**
573         * Create a textual representation of the remote reference.
574         *
575         * @return the textual representation of the current reference 
576         */
577        public String toString() {
578            return ( "#" + getRemCont().getName() + "/" + 
579                     name + "[" + getRemIndex() + "]#" );
580        }
581    
582        /**
583         * Test the equality of 2 remote references.
584         *
585         * @param o the remote reference to check
586         * @return true if equals the current one 
587         */
588        public boolean equals(Object o) {
589            if ( ! (o instanceof RemoteRef) ) return false;
590            RemoteRef r = (RemoteRef) o;
591            return ( r.getRemIndex() == remIndex )
592                && ( r.getRemCont().equals (remCont) );
593        }
594       
595    }