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 }