001    /*
002      Copyright (C) 2001 Renaud Pawlak <renaud@aopsys.com>
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 gnu.regexp.*;
021    import java.io.Serializable;
022    import java.lang.reflect.Method;
023    import java.util.Arrays;
024    import java.util.Hashtable;
025    import java.util.List;
026    import java.util.Vector;
027    import org.apache.log4j.Level;
028    import org.apache.log4j.Logger;
029    import org.objectweb.jac.core.ACConfiguration;
030    import org.objectweb.jac.core.ACManager;
031    import org.objectweb.jac.core.Application;
032    import org.objectweb.jac.core.ApplicationRepository;
033    import org.objectweb.jac.core.NameRepository;
034    import org.objectweb.jac.core.Wrappee;
035    import org.objectweb.jac.core.Wrapping;
036    import org.objectweb.jac.util.ExtArrays;
037    
038    /**
039     * This class defines a generic topology where nodes are a
040     * set of reachable containers.
041     *
042     * <p>It is used by the distribution aspect to define distribution,
043     * deployment, and replication schemes.<p>
044     *
045     * @see org.objectweb.jac.core.dist.RemoteContainer */
046    
047    public class Topology implements Serializable {
048        static Logger logger = Logger.getLogger("topology");
049        static Logger loggerDist = Logger.getLogger("dist");
050        static Logger loggerApp = Logger.getLogger("application");
051        static Logger loggerAspects = Logger.getLogger("aspects");
052    
053        public boolean bootstrapFlag = false;
054    
055        /** The name of the property that defines the initial global
056            topology in the org.objectweb.jac.prop file. */
057            //protected static String topologyProp = "org.objectweb.jac.topology";
058    
059        /** Store the gobal topology of the JAC distributed system. */
060        protected static Topology globalTopology = null;
061    
062        static Hashtable applicationsTopologies = new Hashtable();
063    
064        /**
065         * Returns the topology defined for the given application.
066         *
067         * @param application the application
068         * @return the corresponding topology 
069         */
070        public static Topology getTopology(Application application) {
071            if( application == null ) return null;
072            return (Topology)applicationsTopologies.get(application);
073        }
074    
075        /**
076         * Sets the topology for a given application.
077         *
078         * @param application the application
079         * @param topology the corresponding topology
080         */
081        public static void setTopology(Application application, 
082                                       Topology topology) {
083            if(application == null || topology == null) return;
084            loggerApp.debug("setting "+topology+" to "+application); 
085            applicationsTopologies.put(application,topology);
086        }
087    
088        /**
089         * This method returns the gobal topology of the JAC distributed
090         * middleware.<p>
091         *
092         * This instance of topology is set in strong consistency so that
093         * all the container of the distributed name/aspect space are
094         * notified when a container is added or removed.<p>
095         *
096         * @return the global topology
097         */
098        public static Topology get() {
099            if (globalTopology == null) {
100    
101                // creates the global topology 
102                new Topology();
103            }
104            return globalTopology;
105        }  
106    
107        /**
108         * Resets the global topology by re-resolving the different
109         * containers.  
110         */
111        public static void reset() {
112            globalTopology=null;
113            get();
114        }
115    
116        /**
117         * This method returns a topology with all the containers that
118         * match the regular expression within the global topology of org.objectweb.jac.
119         *
120         * @param regexp a regular expression on the container names
121         * @return a partial topology
122         * @see #getPartialTopology(String) 
123         */
124        public static Topology getPartialTopology(RE regexp) {
125            return new Topology(get().getContainers(regexp)); 
126        }
127    
128        /**
129         * This method returns the first container that matches the host
130         * expression. 
131         */
132        public RemoteContainer getFirstContainer(String sregexp) {
133            RE regexp = null;
134            try {
135                regexp = new RE(sregexp);
136            } catch(Exception e) {
137                return null;
138            }
139            for (int i=0; i<containers.size(); i++) {
140                if (regexp.isMatch(((RemoteContainer)containers.get(i)).getName())) {
141                    return (RemoteContainer)containers.get(i);
142                }
143            }
144            return null;
145        }
146    
147        /**
148         * This method returns a topology with all the containers that
149         * match the regular expression within the global topology of org.objectweb.jac.
150         *
151         * @param regexp a regular expression (as a string) on the
152         * container names
153         * @return a partial topology 
154         */
155        public static Topology getPartialTopology(String regexp) {
156            if (globalTopology == null) 
157                return null;
158            RE re = null;
159            try {
160                re = new RE(regexp);
161            } catch(Exception e) {
162                logger.error("getPartialTopology "+regexp,e);
163                return null;
164            }
165            //Log.trace("topology","get partial topology "+regexp+"(global topology="+globalTopology);
166            RemoteContainer[] containers = globalTopology.getContainers(re);
167            logger.debug("get partial topology found "+Arrays.asList(containers));
168            return new Topology(containers);
169        }
170    
171        /** Store the containers of the topology. */
172        public List containers = new Vector();
173    
174        /** Store the remote references on the name repository for
175            optimization matter. */
176        public List nameReps = new Vector();
177    
178        /**
179         * This constructor builds a topology and set it to global if a
180         * global topology does not exist yet.
181         *
182         * @see Topology#get() 
183         */
184        public Topology() {
185            logger.debug("Creating Topology...");
186            if (globalTopology == null) {
187                NameRepository.get().register("JAC_topology", this);
188                globalTopology = this;
189            }
190            createNameReps();
191            logger.debug("Topology created");
192        }
193    
194        /**
195         * Builds a topology with the given container identifiers. The
196         * identifier is protocol dependent.<p>
197         *
198         * This method transforms the names into remote containers
199         * references by calling <code>resolve</code>.
200         *
201         * @param someNames a set of container names
202         * @see org.objectweb.jac.core.dist.RemoteContainer#resolve(String) 
203         */
204        public Topology(String[] someNames) {
205            for(int i=0; i<someNames.length; i++) {
206                containers.add(RemoteContainer.resolve(someNames[i]));
207            }
208            createNameReps();
209        }
210    
211        /**
212         * Creates the remote references on the name repositories.<p>
213         *
214         * This method is used for optimization reasons. Indeed, it allows
215         * the topology to avoid the dynamic resolution of the containers
216         * when accessing them.<p>
217         *
218         * @see #getContainers()
219         * @see #getContainer(int)
220         * @see #getDistContainers() 
221         */
222        public void createNameReps() {
223            nameReps.clear();
224            for (int i=0; i<containers.size(); i++) {
225                RemoteContainer cc = (RemoteContainer) containers.get(i);
226                RemoteRef rr = null;
227                if (!cc.isLocal()) {
228                    rr = cc.bindTo ("JAC_name_repository");
229                } else {
230                    rr = RemoteRef.create("JAC_name_repository", 
231                                          (Wrappee)NameRepository.get());
232                }
233                nameReps.add(rr);
234            }
235        }
236    
237        /**
238         * Builds a topology from a set of containers.
239         *
240         * @param someContainers the container of the new topology
241         *
242         * @see #getContainers()
243         * @see #getContainer(int)
244         * @see #getDistContainers() 
245         */
246        public Topology(RemoteContainer[] someContainers) {
247            containers.addAll(Arrays.asList(someContainers));
248        }
249    
250        /**
251         * Returns all the containers of the topology.<p>
252         *
253         * @return an array of containers
254         * 
255         * @see #getContainer(int)
256         * @see #getDistContainers() 
257         */
258        public RemoteContainer[] getContainers() {
259            RemoteContainer[] res = new RemoteContainer[containers.size()];
260            System.arraycopy(containers.toArray(), 0, res, 0, res.length);
261            return res;
262        }
263    
264        /**
265         * Returns a given container.<p>
266         *
267         * @param index a valid index
268         * @return the container that corresponds to the index
269         *
270         * @see #getContainers()
271         * @see #getDistContainers()
272         */
273        public RemoteContainer getContainer(int index) {
274            return (RemoteContainer)containers.get(index);
275        }
276    
277        /**
278         * Returns the container of the topology minus the local one if
279         * any.<p>
280         *
281         * This method is typically used to deploy objects on a topology
282         * (when you do not want the object to be also depoyed on the local
283         * container).<p>
284         * 
285         * @return an array of containers
286         *
287         * @see org.objectweb.jac.core.dist.RemoteContainer#isLocal() 
288         * @see #getContainers()
289         * @see #getContainer(int)
290         */
291        public RemoteContainer[] getDistContainers() {
292            Vector ret = new Vector();
293            for ( int i=0; i<containers.size(); i++) {
294                RemoteContainer cc = (RemoteContainer)containers.get(i);
295                if (!cc.isLocal()) {
296                    ret.add (cc);
297                }
298            }
299            RemoteContainer[] rcs = new RemoteContainer[ret.size()];
300            System.arraycopy(ret.toArray(), 0, rcs, 0, ret.size());
301            return rcs;
302        }
303    
304        /**
305         * Tells if the current topology contains the local container.
306         *
307         * @return true if the local container is included in the topology
308         */
309        public boolean containsLocal() {
310            for (int i=0; i<containers.size(); i++) {
311                RemoteContainer cc = (RemoteContainer)containers.get(i);
312                if (cc.isLocal()) {
313                    return true;
314                }
315            }
316            return false;
317        }
318       
319        /**
320         * Gets a set of remote references on all the remote replicas of a
321         * local object for the current topology.
322         *
323         * @param localObject the local object of which the replicas are
324         * seeked
325         * @return the replicas references 
326         */
327        public Vector getReplicas(Wrappee localObject) {
328            String name = NameRepository.get().getName( localObject );
329            Vector vRes = new Vector();
330            for ( int i = 0; i < countContainers(); i++ ) {
331                logger.debug("try to find "+name+" on "+getContainer(i)+
332                          " -- local container is: "+Distd.getLocalContainerName());         
333                RemoteRef rr=null;
334                if(!getContainer(i).getName().equals(Distd.getLocalContainerName())) {
335                    rr=getContainer(i).bindTo(name);
336                }
337                if( rr != null ) {
338                    vRes.add( rr );
339                } else {
340                    logger.debug("remote object not found!");
341                }
342            }
343            return vRes;
344        }
345    
346        /**
347         * Says if an object exist on one of the container of the topology
348         * (excluding the local one).<p>
349         *
350         * @param name the name of the object to check
351         * @return true if the object has been found on one of the
352         * containers of the topology 
353         */
354        public boolean exist(String name) {
355            if ( nameReps == null || nameReps.size() == 0 ) {
356                createNameReps();
357            }
358            //      System.out.print ( " TEST THE EXISTANCE OF " + name + ".... "  );
359            try {
360                for ( int i = 0; i < nameReps.size(); i++ ) {
361                    RemoteRef nr = (RemoteRef) nameReps.get(i);
362                    if ( ! nr.getRemCont().isLocal() ) {
363                        if ( ((Boolean)nr.invoke ( "isRegistered", 
364                                                   new Object[] { name } ))
365                             . booleanValue() ) {
366                            return true;
367                        }
368                    }
369                }
370            } catch (Exception e) {
371                System.out.println(e);
372                return true;
373            }
374            return false;
375        }
376    
377        /**
378         * Get the index of a container.<p>
379         *
380         * @param container a container
381         * @return the index of the container, -1 if not part of this
382         * topology
383         * @see #getContainer(int)
384         * @see #getContainerIndex(String)
385         */
386        public int getContainerIndex(RemoteContainer container) {
387            return containers.indexOf(container);
388        }
389    
390        /**
391         * Get the index of a container from its name.<p>
392         *
393         * @param name a container name
394         * @return the index of the container, -1 if not part of this
395         * topology
396         * @see #getContainer(int)
397         * @see #getContainerIndex(RemoteContainer)
398         * @see #getContainerIndexes(RE)
399         * @see #getContainerIndexes(String[])
400         */
401        public int getContainerIndex(String name) {
402            for ( int i = 0; i < containers.size(); i++ ) {
403                if ( ((RemoteContainer)containers.get( i )).getName().equals ( name ) ) {
404                    return i;
405                }
406            }
407            return -1;
408        }
409    
410        /**
411         * Get the indexes of some container regarding a regular expression
412         * on the name.
413         *
414         * @param regexp a regular expression
415         * @return a set of matching containers indexes
416         *
417         * @see #getContainer(int)
418         * @see #getContainerIndex(String)
419         * @see #getContainerIndexes(String[])
420         */
421        public int[] getContainerIndexes(RE regexp) {
422            if ( regexp == null ) {
423                return null;
424            }
425            Vector temp = new Vector();
426            for ( int i = 0; i < containers.size(); i++ ) {
427                if ( regexp.isMatch( ((RemoteContainer)containers.get( i )).getName() ) ) {
428                    temp.add( new Integer( i ) );
429                }
430            }
431            int[] res = new int[temp.size()];
432            for ( int i = 0; i < res.length; i++ ) {
433                res[i] = ((Integer)temp.get(i)).intValue();
434            }
435            return res;
436        }
437    
438        /**
439         * Get some containers regarding a regular expression on the name.
440         *
441         * @param regexp a regular expression
442         * @return a set of matching containers
443         *
444         * @see #getContainer(int)
445         * @see #getContainerIndex(String)
446         * @see #getContainerIndexes(String[])
447         * @see #getContainerIndexes(RE) 
448         */
449        public RemoteContainer[] getContainers(RE regexp) {
450            if ( regexp == null ) {
451                return null;
452            }
453            Vector temp = new Vector();
454            for ( int i = 0; i < containers.size(); i++ ) {
455                if ( regexp.isMatch( ((RemoteContainer)containers.get(i)).getName() ) ) {
456                    temp.add(containers.get( i ));
457                }
458            }
459            RemoteContainer[] res = new RemoteContainer[temp.size()];
460            for ( int i = 0; i < res.length; i++ ) {
461                res[i] = (RemoteContainer)temp.get(i);
462            }
463            return res;
464        }
465    
466        /**
467         * Get the indexes of some container names.<p>
468         *
469         * @param names an array containing the names
470         * @return the array of the corresponding indexes
471         *
472         * @see #getContainer(int)
473         * @see #getContainerIndex(RemoteContainer)
474         * @see #getContainerIndexes(RE)
475         */
476        public int[] getContainerIndexes(String[] names) {
477            int[] res = null;
478            Vector vres = new Vector();
479            if ( names != null && names.length != 0 ) {
480                for ( int i = 0; i < names.length; i++ ) {
481                    int index = getContainerIndex( 
482                        RemoteContainer.resolve( names[i] ) );
483                    if ( index != -1 ) {
484                        vres.add( new Integer( index ) );
485                    }
486                }
487                res = new int[vres.size()];
488                for ( int i = 0; i < res.length; i++ ) {
489                    res[i] = ((Integer)vres.get(i)).intValue();
490                }
491            }
492            return res;
493        }
494    
495        /**
496         * Add a container to the topology if not already present.
497         *
498         * @param container the container to add
499         *
500         * @see #addContainers(RemoteContainer[])
501         */
502        public void addContainer(RemoteContainer container) {
503            if( containers == null ) return;
504            if( containers.contains( container ) ) return; 
505            containers.add(container);
506            fireTopologyChangeEvent();
507        }
508       
509        /**
510         * Adds a container from its name.
511         *
512         * @param container the container name 
513         */
514        public void addContainer(String container) {
515            RemoteContainer rc = RemoteContainer.resolve( container ); 
516            addContainer( rc );
517        }
518    
519        /**
520         * Add a set of containers to the topology.<p>
521         *
522         * @param someContainers a set of containers to add
523         *
524         * @see #addContainer(RemoteContainer)
525         */
526        public void addContainers(RemoteContainer[] someContainers) {
527            if( containers == null ) return;
528            for( int i=0; i < someContainers.length; i++ ) {
529                addContainer(someContainers[i]);
530            }
531        }
532    
533        /**
534         * Remove a container from the topology.<p>
535         *
536         * @param container the container to be removed 
537         */
538        public void removeContainer(RemoteContainer container) {
539            containers.remove(container);
540            fireTopologyChangeEvent();
541        }
542    
543        /**
544         * Check if the current topology contains the given container.
545         *
546         * @param container the container to check
547         * @return true if the topology contains this container, false
548         * otherwise 
549         */
550        public boolean isContainer(RemoteContainer container) {
551            return containers.contains(container);
552        }
553    
554        /**
555         * Replace a container at a given index.<p>
556         *
557         * @param index the index where the container has to be substituted
558         * @param newContainer the container to set
559         *
560         * @see #replaceContainer(RemoteContainer,RemoteContainer)
561         */
562        public void replaceContainer(int index, RemoteContainer newContainer) {
563            if( newContainer == null ) return;
564            if( index < 0 || index >= containers.size() ) return;
565            if( containers.contains(newContainer) ) {
566                containers.remove(index);
567                fireTopologyChangeEvent();
568                return;
569            }
570            containers.remove(index);
571            containers.add(index,newContainer);
572            fireTopologyChangeEvent();
573        }
574    
575        /**
576         * Replace a container by another container.<p>
577         *
578         * @param oldContainer the container to replace (must be in the topology)
579         * @param newContainer the replacing container
580         *
581         * @see #replaceContainer(int,RemoteContainer)
582         */
583        public void replaceContainer(RemoteContainer oldContainer,
584                                     RemoteContainer newContainer) {
585            replaceContainer(containers.indexOf(oldContainer),newContainer);
586        }
587    
588        /**
589         * Gets a container from its name.
590         *
591         * @param name the name to seek
592         * @return the container, null if not found in the topology 
593         */
594        public RemoteContainer getContainer(String name) {
595            RemoteContainer rc = RemoteContainer.resolve(name);
596            int index = containers.indexOf(rc);
597            if( index == -1 ) return null;
598            return (RemoteContainer)containers.get(index);
599        }
600    
601        /**
602         * Returns the number of containers in this topology.<p>
603         *
604         * @return the containers count 
605         */
606        public int countContainers() {
607            return containers.size();
608        }
609    
610        /**
611         * Notifies everybody that the topology changed.
612         */
613        protected void fireTopologyChangeEvent() {
614            if( bootstrapFlag == false && this == Topology.get() ) {
615                loggerDist.debug("topology changed");
616                // notify all the system objects
617                Wrapping.invokeRoleMethod((Wrappee)ApplicationRepository.get(),
618                                          /* org.objectweb.jac.aspects.distribution.consistency.ConsistencyWrapper, */
619                                          "invalidateTopology",ExtArrays.emptyObjectArray);
620                Wrapping.invokeRoleMethod((Wrappee)Topology.get(),
621                                          /* org.objectweb.jac.aspects.distribution.consistency.ConsistencyWrapper, */
622                                          "invalidateTopology",ExtArrays.emptyObjectArray);
623                // notify the aspects
624                ((ACManager)ACManager.get()).whenTopologyChanged();
625            }
626        }
627    
628    
629        /**
630         * Dump the containers of the topology.
631         */
632        public void dump () {
633            System.out.println ("Dumping the topology:");
634            System.out.println (containers);
635        }
636    
637        /**
638         * Launches the administration GUI on the local container. 
639         */
640        public void launchGUI(String programName) {
641            Object guiAC = ACManager.get().getObject("gui");
642            Object display = null;
643            try {
644                display = guiAC.getClass().getMethod("createSwingDisplay", 
645                                                     new Class[] { Class.class } )
646                    .invoke(guiAC, 
647                            new Object[] {Class.forName("org.objectweb.jac.aspects.gui.ProgramView")});
648                display.getClass().getMethod("setProgram",new Class[] {String.class})
649                    .invoke( display, new Object[] {programName});
650            } catch (Exception e) {
651                logger.error("launchGUI "+programName,e);
652            }
653        }
654    
655        /**
656         * Start the admin GUI
657         */
658        public void launchGUI() {
659            Object guiAC = ACManager.get().getObject("gui");
660            Object display = null;
661            try {
662                display = guiAC.getClass().getMethod("createSwingDisplays", 
663                                                     new Class[] { String[].class })
664                    .invoke(guiAC, new Object[] {new String[] {"admin"}});
665            } catch (Exception e) {
666                logger.error("launchGUI",e);
667            }
668        }
669    
670        /**
671         * Start some swing GUIs for an application.
672         *
673         * @param application the name of the application for which to
674         * start the GUIs
675         * @param guiNames the names of the GUI windows to start
676         */
677        public void startSwingGUI(String application, String[] guiNames) {
678            loggerAspects.debug(
679                "Start Swing GUI: "+application+" "+Arrays.asList(guiNames));
680            ACManager acm = (ACManager)ACManager.get();
681            Object guiAC = acm.getObject(application+".gui");
682            // We use reflection here because we do not want to depend on
683            // the GuiAC
684            try {
685                Class guiACClass = Class.forName("org.objectweb.jac.aspects.gui.GuiAC");
686                Method method = guiACClass.getMethod("createSwingDisplays",
687                                                     new Class[] {String[].class});
688                method.invoke(guiAC,new Object[] {guiNames});
689            } catch (Exception e) {
690                logger.error("startSwingGUI "+application+","+Arrays.asList(guiNames),e);
691            }
692        }
693    
694        /** 
695         * Reload the configuration of an aspect 
696         */ 
697        public void reloadAspect(String applicationName, String aspectName) {
698            ACManager.getACM().reloadAspect(applicationName,aspectName); 
699        }
700    
701            public void unweaveAspect(String applicationName, String aspectName) {
702                    //Application app=ApplicationRepository.get().getApplication(applicationName);
703                    ACManager.getACM().unregister(applicationName+"."+aspectName); 
704            }
705    
706            public void weaveAspect(String applicationName, String aspectClassName, String configPath) {
707                    Application app=ApplicationRepository.get().getApplication(applicationName);
708                    new ACConfiguration(app,aspectClassName,configPath,true).weave();
709            }
710    
711        /**
712         * Set a trace logging level
713         * @see org.objectweb.jac.util.Log#trace(String,int,String)
714         */
715        public void setTrace(String application, String category, int level) {
716            loggerAspects.debug("Setting trace "+category+"="+level); 
717            Logger.getLogger(category).setLevel(Level.toLevel(level));      
718        }
719    
720        /**
721         * Get a textual representation of the topology.
722         *
723         * @return a string describing the topology */
724    
725        public String toString() {
726            if( containers == null ) return "null";
727            return containers.toString();
728        }
729       
730    }