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 }