Hey,

JBoss world tour is over... back to home base, ready to go in project Rabbit
hole, first email is yours ...

1st... congratulations on the job, it seems we are in synch on what we want
to achieve, comments below,

|To test it, you should uncomment the freshly added line for the
|org.jboss.deployment.scope.J2eeGlobalScopeDeployer in jboss.jcml
|and disable
|the
|original J2eeDeployer entry, in turn.

There are so many problems that when we get this stabilized we should
default to your new and improved deployer, let's target 3.0.

|Personally, I have experimented with testbean.jar and
|testbean2.jar from the
|jbosstest:
|
|       - removed any occurences of org.jboss.testbean2.* in testbean.jar
|       - removed any occurences of org.jboss.testbean.* in testbean2.jar
|       - built two "trivial" earīs testbean.ear and teastbean2.ear that
|host the
|        above jars as ejb-modules (this is necessary to get the automatic
|dependency deployment    working, see later in this email).
|
|Deploying testbean.ear will succeed as before (normal
|deployer/complete jar)
|since links to the testbean2 classes will only be resolved at bean
|operation
|due to the lazy classloading approach of the Java VM.

Excellent, that is all we really need from a CL standpoint.  I was against
the externalization of the CL dependencies, I proposed the initial CL to do
lazy loading and linking.

|Deploying testbean2.ear afterwards will succeed, but that is not as usual!
|Links to testbean classes will be resolved already during deployment (If I
|remember right, the reflection done by ejb-verification will force
|the VM to
|load the referred classes) and here, the scoped deployer and its dedicated
|classloaders come into play:

Right, the verification will load the class in VM, good point.

|org.jboss.deployment.scope.ScopedURLClassLoader delegates any failed local
|resource loading call (and only those such that for usual ears, semantics
|and performance is not affected - additional cost is just a
|try/catch block)
|to the classloaders of other applications in the same scope (here, the
|testbean loader).

How is that done? (didn't read the code) in my initial proposal this was
done with the static map.  How do you "scope" that map now? who defines the
application scope? where do you specify that? remember the less you specify
the better.

Also if someone fails to specify a scope, do you still use a default
(static) map?

|Until now, I have experimented with a single global scope, but the deployer
|implementation already supports the construction and deployment
|into several
|ones (we would need to expose these yet protected methods in an MBean if
|this is of interest).

again this is a great but what do you need to specify and where to get a
scoped deployment... what happens if you don't specify a scope.

|During the delegation process, the dependency testbean2-->testbean in the
|global scope is logged (class and resource locations are also
|cached) by the

well by definition we cache the class (that is the whole fucking point)
don't make it sound like it is an "extra" ;-)

|deployer such that if you now undeploy or redeploy testbean.ear, you will
|find out that indeed both ears are un/redeployed.

Ok,  testbean3-->testbean2-->testbean  what happens to testbean3 when we
cycle testbean? I assume this is there already?
in other words, you can build the graph of dependencies implicitely (lazy
loading and interception of load in the CL) and build a dependent set per
class and cycle that.

|Note that until this point, you have to care about the right sequence in
|which to deploy the ears (first testbean, then testbean2). As soon as the
|dependencies start to get non-trivial,  this is no longer feasible. If
|testbean.ear would need testbean2 classes at startup, too, manual
|deployment
|would be even impossible.

Here we could get fancy at the CL level...

so that deployments wait until the dependencies are cleared... a bit more
complex imho...

|For this purpose, I have extended the org.jboss.deployment.Deployment
|information a bit to also
|include the manifests of the ears. If you now specify "Class-Path:
|testbean.ear" in the testbean2.earīs META-INF/MANIFEST.MF you will find out
|that a deployment of testbean2.ear will automatically deploy testbean.ear,
|too (if its not already deployed in that scope).
|
|Marc seems to doubt the usefulness of this "dependency" annotation
|(which is
|why I tried to keep it as close to the already existing ear structure, as
|possible). I guess that this discrepancy maybe caused by a slightly

I doubt it to establish CL dependencies (we can induce them) it is useful
for specifying order of deployment though...

|different concept of how the local-->global resource dispatching should
|work. Marc seems to favourize to leave redundant classes within the ears,
|but only choose one representation at runtime (you do not run into
|deployment problems, but most certainly into versioning problems).

I don't remember saying that :) but I might have since yes it is a way to go
about it.  At least this way we ensure that we only have one version all the
time, however it is still up to the deployer to make sure that all the
packages have the right version which might be a maintainance problem.  I do
believe that your "explicit" solution to that problem is a bit more admin
friendly, but I do also believe that we could get fancy in the deployment
structures to allow for self sorting of the apps.  We would need to catch
the failed dependencies at the CL / Deployer level and put the ear/war in
the back of a list and try to deploy the others.  Circular dependencies
between jar need to be resolved by putting everyone in one jar, I don't see
a simple solution to that off-hand.

|For us, its important being able to deploy applications with
|lacking or even
|overlapping class versions. Since IMHO the first is just a special case of
|the latter (simply override loadClass and getResource of
|ScopedURLClassLoader, dont use earīs manifest), I thought that it would be
|more useful to implement our scheme as the basis.
|
|Iīm looking forward to hear your opinions, questions (, and bug reports ;-)
|CGJ

good work man, I hope to find time to review this code as part of Rabbit
hole...

marc

|
|-----Ursprüngliche Nachricht-----
|Von: marc fleury [mailto:[EMAIL PROTECTED]]
|Gesendet: Samstag, 19. Mai 2001 16:17
|An: [EMAIL PROTECTED]
|Betreff: RE: [JBoss-dev] CVS update:
|jboss/src/main/org/jboss/deployment/scope J2eeGlobalScopeDeployer.java
|Scope.java ScopedURLClassLoader.java package.html
|
|
||  Log:
||  First "official" try for a scoped application deployer that is
||able to build
||  "virtual" applications out of dependent earīs. Currently reads
||dependency information
||  from property file. setup of application management information
|
|hummmmm what is this "reads dependency information" from property file
|thing.  The URLClassLoader I posted in December works totally implicitely,
|we need an implicit design.  Care to explain what this design does with
|respect to finding the right class in the VM?
|
|marc
|
||is not yet
||  correct.
||
||  Revision  Changes    Path
||  1.1
||jboss/src/main/org/jboss/deployment/scope/J2eeGlobalScopeDeployer.java
||
||  Index: J2eeGlobalScopeDeployer.java
||  ===================================================================
||  /*
||   * JBoss, the OpenSource EJB server
||   *
||   * Distributable under LGPL license.
||   * See terms of license at gnu.org.
||   */
||
||  package org.jboss.deployment.scope;
||
||  import org.jboss.deployment.Deployment;
||  import org.jboss.mgt.Application;
||  import org.jboss.deployment.J2eeDeploymentException;
||  import java.net.URL;
||  import java.net.MalformedURLException;
||  import java.net.URLClassLoader;
||  import java.util.Iterator;
||  import java.util.List;
||  import java.util.Collection;
||  import java.util.Set;
||  import java.io.IOException;
||  import javax.management.ObjectName;
||  import javax.management.MBeanException;
||  import javax.management.RuntimeMBeanException;
||  import javax.management.RuntimeErrorException;
||  import javax.management.JMException;
||
||
||  /**
||   * This is an example deployer that uses the J2ee application
||scoping facility and
||   * implements a proper (re-)deployment procedure.
||   * In this case, we introduce a global scope.
||   * @author  cgjung
||   * @version 0.8
||   */
||
||  public class J2eeGlobalScopeDeployer extends
||org.jboss.deployment.J2eeDeployer {
||
||      /** the global scope */
||      protected Scope globalScope=null;
||
||      /** Creates new J2eeDependencyDeployer */
||      public J2eeGlobalScopeDeployer() {
||      }
||
||      /** starts the service by first creating
||       * a new scope
||       * @throws Exception to indicate
||       * that either superclass or scope creation
||       * went wrong.
||       */
||      public void startService() throws Exception {
||          globalScope=createScope();
||          super.startService();
||      }
||
||      /** factory method to create a new scope, May throw a
||general exception
||       * if this very basic enterprise fails.
||       * @throws Exception May throw any exception to indicate
||       * instantiation problems of the scope (probably
||       * because initial meta-data could not be found,
||       * is corrupt, etc.)
||       * @return a freshly instantiated and preconfigured scope.
||       *
||       */
||      protected Scope createScope() throws Exception {
||          return new Scope(log);
||      }
||
||      /** stops the service by freeing
||       * scope afterwards
||       */
||      public void stopService() {
||          super.stopService();
||          globalScope=null;
||      }
||
||
||      /**
||       * creates an application class loader for this deployment
||       * this class loader will be shared between jboss and
||       * tomcat via the contextclassloader
||       * we include all ejb and web-modules at this level
||       * to also be able to connect flat ejb-jars to each other
||       * @param deployment the deployment that is about to be made,
||       * but is already installed
||       *
||       * @throws J2eeDeploymentException to indicate
||       * problems with instantiating the classloader
||       * (maybe if reading some meta-data did not work properly)
||       */
||      protected void createContextClassLoader(Deployment
||deployment) throws J2eeDeploymentException {
||
||          try{
||              // get urls we want all classloaders of this
||application to share
||              Set allUrls=new java.util.HashSet();
||
||              // first we add the common urls (as does our parent)
||              Iterator
|allCommonUrls=deployment.getCommonUrls().iterator();
||
||              while(allCommonUrls.hasNext())
||                  allUrls.add(allCommonUrls.next());
||
||              // then the ejbmodules urls
||              Iterator
|allEjbModules=deployment.getEjbModules().iterator();
||
||              while(allEjbModules.hasNext()) {
||                  Iterator allLocalUrls=((Deployment.Module)
||allEjbModules.next()).getLocalUrls().iterator();
||                  while(allLocalUrls.hasNext())
||                      allUrls.add(allLocalUrls.next());
||              }
||
||              // then the web modules urls
||              Iterator
|allWebModules=deployment.getWebModules().iterator();
||
||              while(allWebModules.hasNext()) {
||                  Iterator allLocalUrls=((Deployment.Module)
||allWebModules.next()).getLocalUrls().iterator();
||                  while(allLocalUrls.hasNext())
||                      allUrls.add(allLocalUrls.next());
||              }
||
||              // create classloader with parent from context
||              ClassLoader parent =
||Thread.currentThread().getContextClassLoader();
||              // using the factory method
||              ScopedURLClassLoader appCl =
||createScopedContextClassLoader((URL[]) allUrls.toArray(new
||URL[allUrls.size()]),parent,deployment);
||
||              // set the result loader as the context class
||              // loader for the deployment thread
||              Thread.currentThread().setContextClassLoader(appCl);
||          } catch(Exception e) {
||              throw new J2eeDeploymentException("could not
||construct context classloader",e);
||          }
||      }
||
||      /** factory method for scoped url classloaders factored out.
||May throw a general
||       * exception if this enterprise fails.
||       * @param urls the urls for which the scoped
||       * classloader should be generated
||       *
||       * @param parent the parent loader
||       *
||       * @param deployment the deployment to which this classloader is
||       * associated.
||       *
||       * @throws Exception to indicate
||       * problems in constructing the
||       * classloader (maybe because
||       * meta-data was corrupt).
||       *
||       * @return a freshly instantiated and configured class loader
||       *
||       */
||      protected ScopedURLClassLoader
||createScopedContextClassLoader(URL[] urls,ClassLoader
||parent,Deployment deployment) throws Exception {
||          return new
||ScopedURLClassLoader(urls,parent,deployment,globalScope);
||      }
||
||      /** Overrides the normal (re-)deploy method in order to
||       * take dependent applications into account.
||       *
||       * @param _url the url (file or http) to the archiv to deploy
||       * @throws MalformedURLException in case of a malformed url
||       * @throws J2eeDeploymentException if something went wrong...
||       * @throws IOException if trouble while file download occurs
||       */
||      public void deploy(String _url) throws
||MalformedURLException, IOException, J2eeDeploymentException {
||          URL url = new URL(_url);
||
||          ObjectName lCollector = null;
||          try {
||              lCollector = new ObjectName( "Management",
||"service", "Collector" );
||          }
||          catch( Exception e ) {
||          }
||
||          // initialise teared down deployments just in case that nothing
||          // is teared down
||          List allTearedDown=new java.util.ArrayList();
||
||          // undeploy first if it is a redeploy
||          try {
||              // use modified undeploy in order to tear down
||              // dependent apps as well
||              undeployWithDependencies(_url,allTearedDown,url);
||              // Remove application data by its id
||              server.invoke(
||              lCollector,
||              "removeApplication",
||              new Object[] { _url },
||              new String[] { "java.lang.String" }
||              );
||          } catch (Exception _e) {
||              // fresh deployment case
||              allTearedDown.add(url);
||          }
||
||          // now we (re-)deploy the whole bunch that was teared down
||          // with us
||          Iterator allDeployments=allTearedDown.iterator();
||
||          while(allDeployments.hasNext()) {
||              URL nextUrl=(URL) allDeployments.next();
||              // maybe this deployment has already been made as
||              // a side effect
||              Deployment d = installer.findDeployment(nextUrl.toString());
||
||              if(d==null) {
||                  log.log("(Re-)Deploy J2EE application: " + nextUrl);
||                  d=installApplication(nextUrl);
||
||                  try {
||                      startApplication(d);
||                      log.log("J2EE application: " + nextUrl + "
||is (re-)deployed.");
||                      try {
||                          // Now the application is deployed add
||it to the server data collector
||                          Application lApplication =
||convert2Application( _url, d );
||                          server.invoke(
||                          lCollector,
||                          "saveApplication",
||                          new Object[] {
||                              _url,
||                              lApplication
||                          },
||                          new String[] {
||                              "java.lang.String",
||                              lApplication.getClass().getName()
||                          }
||                          );
||                      }
||                      catch( Exception e ) {
||                          log.log("Report of deployment of J2EE
||application: " + _url + " could not be reported.");
||                      }
||                  } catch (Exception _e) {
||                      try {
||                          stopApplication(d);
||                      }
||                      catch (Exception _e2) {
||                          log.error("unable to stop application
||"+d.getName()+": "+_e2);
||                      }
||                      finally {
||                          try {
||                              uninstallApplication(_url);
||                          }
||                          catch (Exception _e3) {
||                              log.error("unable to uninstall
||application "+d.getName()+": "+_e3);
||                          }
||                      }
||
||                      if (_e instanceof J2eeDeploymentException) {
||                          throw (J2eeDeploymentException)_e;
||                      }
||                      else {
||                          log.exception(_e);
||                          throw new J2eeDeploymentException("fatal
||error: "+_e);
||                      }
||                  }
||              }
||          }
||      }
||
||      /** A new stop method that stops a running deployment
||       * and its dependent applications and that logs their
||       * urls (where the current deployment will be redeployed
||under newUrl)
||       * in a set for redeployment.
||       *
||       * @param _d deployment to stop
||       *
||       * @param redeployUrls collects the sourceUrls of the
||       * undeployed apps
||       *
||       * @param newUrl the url under which the current deployment
||should be redeployed, if at all
||       *
||       * @throws J2eeDeploymentException  to
||       * indicate problems in undeployment.
||       */
||      protected void stopApplication(Deployment _d, List
||redeployUrls, URL newUrl) throws J2eeDeploymentException {
||          // synchronize on the scope
||          synchronized(globalScope.classLoaders) {
||
||              // find out the corresponding classloader
||              ScopedURLClassLoader source=(ScopedURLClassLoader)
||              globalScope.classLoaders.get(_d.getLocalUrl());
||
||              // its still here, so the thing is not already stopped
||              if(source!=null) {
||
||                  try{
||                      log.log("About to stop application "+_d.getName());
||
||                  // add it to the stopped list
||                  redeployUrls.add(newUrl);
||                  // get dependency information
||                  Iterator
||allDependencies=globalScope.getDependentClassLoaders(source).
||                  iterator();
||                  // deregister classloader
||                  globalScope.deRegisterClassLoader(source);
||
||                  // first stop the dependent stuff
||                  while(allDependencies.hasNext()) {
||                      ScopedURLClassLoader
||dependentLoader=(ScopedURLClassLoader) allDependencies.next();
||
||
||stopApplication(dependentLoader.deployment,redeployUrls,dependentLo
||ader.deployment.getSourceUrl());
||                  }
||
||                  } finally {
||
||                      try{
||                          // now we do the real stopping
||                          super.stopApplication(_d);
||
||                          // and leave a last message to the
|classloader to
||                          // tear down meta-data or such
||                          source.onUndeploy();
||                      } finally {
||                          try{
||                              uninstallApplication(_d);
||                          } catch(IOException e) {
||                              log.error("could not properly
||uninstall "+_d.getName());
||                          }
||                      }
||                  }
||
||              } // if
||          } // sync
||
||      }
||
||      /**  Overrides the proper stop in order to
||       *   be redirected to the dependency stopper
||       *   @param _d the deployment to stop
||       *   @throws J2eeDeploymentException if an error occures for
||one of these
||       *           modules
||       */
||      protected void stopApplication(Deployment _d) throws
||J2eeDeploymentException {
||          stopApplication(_d,new java.util.ArrayList(),_d.getSourceUrl());
||      }
||
||      /** Undeploys the given URL (if it is deployed) and returns an array
||       * of deployments that have been teared down
||       * Actually only the file name is of interest, so it dont has to be
||       * an URL to be undeployed, the file name is ok as well.
||       * @param _app the stirng spec of the app to tear down
||       *
||       * @param allTearedDown collection of deployments that have
||been teared down as a result.
||       *
||       * @param newUrl url under which the application is to be
||redeployed, if at all
||       *
||       * @throws J2eeDeploymentException if something went wrong
||(but should have removed all files)
||       * @throws IOException if file removement fails
||       */
||      public void undeployWithDependencies(String _app, List
||allTearedDown, URL newUrl) throws IOException, J2eeDeploymentException {
||          // find currect deployment
||          Deployment d = installer.findDeployment(_app);
||
||          if (d == null)
||              throw new J2eeDeploymentException("The application
||\""+name+"\" has not been deployed.");
||
||          try {
||              // use dependency stopper
||              stopApplication(d, allTearedDown, newUrl);
||          }
||          catch (J2eeDeploymentException _e) {
||              throw _e;
||          }
||
||      }
||
||      /** Starts the successful downloaded deployment. <br>
||       * Means the modules are deployed by the responsible
||container deployer
||       * This version of the method does indeed start necessary
||       * other applications as well.
||       * @param dep the deployment to start
||       *
||       * @throws J2eeDeploymentException if an error occures for
||one of these
||       *          modules
||       */
||      protected void startApplication(Deployment dep) throws
||J2eeDeploymentException {
||          // here we collect all the started deployments (not only dep)
||          // indexed by the sourceUrl
||          Collection deployments=new java.util.ArrayList();
||
||          startApplication(dep, deployments);
||
||          Iterator allDeployments=deployments.iterator();
||
||          while(allDeployments.hasNext()) {
||
||              Deployment _d=(Deployment) allDeployments.next();
||
||              // save the old classloader
||              ClassLoader oldCl = Thread.currentThread().
||              getContextClassLoader();
||
||              // find out the corresponding classloader
||              ScopedURLClassLoader source=(ScopedURLClassLoader)
||              globalScope.classLoaders.get(_d.getLocalUrl());
||
||              Thread.currentThread().setContextClassLoader(source);
||
||              // redirect all modules to the responsible deployers
||              Deployment.Module m = null;
||              String moduleName = null;
||              String message;
||              try {
||                  // Tomcat
||                  Iterator it = _d.getWebModules().iterator();
||                  if (it.hasNext() && !warDeployerAvailable())
||                      throw new
||J2eeDeploymentException("application contains war files but no web
||container available");
||
||
||                  while (it.hasNext()) {
||                      m = (Deployment.Module)it.next();
||                      moduleName = m.getName();
||                      log.log("Starting module " + moduleName);
||
||                      // Call the TomcatDeployer that is loaded in
||the JMX server
||                      server.invoke(warDeployer, "deploy",
||                      new Object[] { m.getWebContext(),
||m.getLocalUrls().firstElement().toString()}, new String[] {
||"java.lang.String", "java.lang.String" });
||
||                      // since tomcat changes the context classloader...
||
|Thread.currentThread().setContextClassLoader(source);
||                  }
||
||                  // JBoss
||                  // gather the ejb module urls and deploy the application
||                  moduleName = _d.getName();
||                  Collection tmp = new java.util.Vector();
||                  for( it = _d.getEjbModules().iterator();
||it.hasNext(); ) {
||                      m = (Deployment.Module) it.next();
||                      tmp.add(
||m.getLocalUrls().firstElement().toString() );
||                  }
||                  String[] jarUrls = new String[ tmp.size() ];
||                  tmp.toArray( jarUrls );
||                  // Call the ContainerFactory that is loaded in
||the JMX server
||                  server.invoke(jarDeployer, "deploy",
||                  new Object[]{ _d.getLocalUrl().toString(),
||jarUrls }, new String[]{ String.class.getName(),
||String[].class.getName() } );
||              }
||              catch (MBeanException _mbe) {
||                  log.error("Starting "+moduleName+" failed!");
||                  throw new J2eeDeploymentException("Error while
||starting "+moduleName+": " +
||_mbe.getTargetException().getMessage(), _mbe.getTargetException());
||              }
||              catch (RuntimeErrorException e) {
||                  log.error("Starting "+moduleName+" failed!");
||                  e.getTargetError().printStackTrace();
||                  throw new J2eeDeploymentException("Error while
||starting "+moduleName+": " + e.getTargetError().getMessage(),
||e.getTargetError());
||              }
||              catch (RuntimeMBeanException e) {
||                  log.error("Starting "+moduleName+" failed!");
||                  e.getTargetException().printStackTrace();
||                  throw new J2eeDeploymentException("Error while
||starting "+moduleName+": " + e.getTargetException().getMessage(),
||e.getTargetException());
||              }
||              catch (JMException _jme) {
||                  log.error("Starting failed!");
||                  throw new J2eeDeploymentException("Fatal error
||while interacting with deployer MBeans... " + _jme.getMessage());
||              }
||              finally {
||                  Thread.currentThread().setContextClassLoader(oldCl);
||              }
||          }
||
||      }
||
||      /** Starts the successful downloaded deployment. <br>
||       * Means the modules are deployed by the responsible
||container deployer
||       * <comment author="cgjung">better be protected for
||subclassing </comment>
||       * @param alreadyMarked the deployments that have already
||been installed and
||       * that  must be properly deployed afterwards.
||       * @param _d the deployment to start
||       * @throws J2eeDeploymentException if an error occures for
||one of these
||       *          modules
||       */
||      protected void startApplication(Deployment _d, Collection
||alreadyMarked) throws J2eeDeploymentException {
||
||          ClassLoader
||parent=Thread.currentThread().getContextClassLoader();
||
||          // set the context classloader for this application
||          createContextClassLoader(_d);
||
||          // save the application classloader for later
||          ScopedURLClassLoader appCl = (ScopedURLClassLoader)
||          Thread.currentThread().getContextClassLoader();
||
||          alreadyMarked.add(_d);
||
||          String[] dependentStuff=appCl.getDependingApplications();
||
||          for(int count=0;count<dependentStuff.length;count++) {
||
||              // reinstall parent
||              Thread.currentThread().setContextClassLoader(parent);
||
||              try{
||                  URL absoluteUrl=new
||URL(_d.getSourceUrl(),dependentStuff[count]);
||
||                  Deployment
||newD=installer.findDeployment(absoluteUrl.toString());
||
||                  if(newD==null) {
||                      newD = installApplication(absoluteUrl);
||
||                      startApplication(newD,alreadyMarked);
||                  }
||              } catch(MalformedURLException e) {
||                  throw new J2eeDeploymentException("could not
||construct url for dependent application "+dependentStuff[count],e);
||              } catch(IOException e) {
||                  throw new J2eeDeploymentException("io problem
||when trying to access dependent application "+dependentStuff[count],e);
||              }
||
||
||          } // for
||
||          // reinstall parent
||              Thread.currentThread().setContextClassLoader(parent);
||
||      }
||  }
||
||
||
||  1.1
|jboss/src/main/org/jboss/deployment/scope/Scope.java
||
||  Index: Scope.java
||  ===================================================================
||  /*
||   * JBoss, the OpenSource EJB server
||   *
||   * Distributable under LGPL license.
||   * See terms of license at gnu.org.
||   */
||
||  package org.jboss.deployment.scope;
||
||  import org.jboss.logging.Log;
||
||  import java.util.Set;
||  import java.util.Map;
||  import java.util.Iterator;
||
||  import java.net.URL;
||
||  /**
||   * Scope is a manager/mediator that connects several
||ScopedURLClassLoaders
||   * with each other and computes their dependencies. The locks
||used in the scope
||   * implementation are quite coarse-grained, maybe
||thread-unfriendly, but the
||   * rationale is that classloading a) happens not too often
||(hopefully) in the
||   * lifecycle of an application b) will dispatch only in special
||cases (where applications depliberately
||   * share classes) to this scope class and c) is optimized by
||caching the locations.
||   * @author  cgjung
||   * @version 0.8
||   */
||
||  public class Scope {
||
||      /** keeps a map of class loaders that participate in this scope */
||      final protected Map classLoaders=new java.util.HashMap();
||
||      /** keeps a hashtable of dependencies between the classLoaders */
||      final protected Map dependencies=new java.util.HashMap();
||
||      /** keeps a hashtable of class appearances */
||      final protected Map classLocations=new java.util.HashMap();
||
||      /** keeps a hashtable of resource appearances */
||      final protected Map resourceLocations=new java.util.HashMap();
||
||      /** keeps a reference to a logger which which to interact */
||      final protected Log log;
||
||      /** Creates new Scope */
||      public Scope(Log log) {
||          this.log=log;
||      }
||
||      /** registers a classloader in this scope */
||      public ScopedURLClassLoader
||registerClassLoader(ScopedURLClassLoader loader) {
||          // must synchronize not to collide with deregistrations and
||          // dependency logging
||          synchronized(classLoaders) {
||              return (ScopedURLClassLoader)
||classLoaders.put(loader.deployment.getLocalUrl(),loader);
||          }
||      }
||
||
||      /** deRegisters a classloader in this scope
||       *  removes all cached data related to this classloader
||       */
||      public ScopedURLClassLoader
||deRegisterClassLoader(ScopedURLClassLoader loader) {
||          // synchronized not to collide with registrations
||          // and dependency logging
||          synchronized(classLoaders) {
||              // remove class locations
||              clearByValue(classLocations,loader);
||              // remove resource locations
||              clearByValue(resourceLocations,loader);
||              // remove dependency annotations
||              dependencies.remove(loader);
||              // and remove the loader
||              return (ScopedURLClassLoader)
||classLoaders.remove(loader.deployment.getLocalUrl());
||          }
||      }
||
||      /** helper method that will clear all entries from a map
||       *  with a dedicated target value.
||       */
||      protected void clearByValue(Map map, Object value) {
||          Iterator values=map.values().iterator();
||          while(values.hasNext()) {
||              if(values.next().equals(value))
||                  // uses the very useful remove method of the
||value iterator!
||                  values.remove();
||          }
||      }
||
||      /** returns the classLoaders that a particular classLoader is
||       *  dependent on. Should be called after locking classLoaders
||       */
||      public Set getDependentClassLoaders(ScopedURLClassLoader loader) {
||          Set result=(Set) dependencies.get(loader);
||          if(result==null)
||              result=new java.util.HashSet();
||          return result;
||      }
||
||      /** adds a dependency between two classloaders. this can be called
||       *  from within application threads that require resource loading.
||       *  Should be called after locking classLoaders
||       */
||      protected boolean addDependency(ScopedURLClassLoader source,
||ScopedURLClassLoader target) {
||          // no rescursions necessary (but not volatile for the code)
||          if(!source.equals(target)) {
||
||              Set deps=(Set) dependencies.get(target);
||
||              if(deps==null) {
||                  deps=new java.util.HashSet();
||                  dependencies.put(target,deps);
||              }
||
||              log.debug("Adding dependency from deployment
||"+source+":"+source.deployment.getLocalUrl()+" to deployment "+
||                  target+":"+target.deployment.getLocalUrl());
||
||              return deps.add(source);
||          } else
||              return false;
||      }
||
||      /** loads a class on behalf of a given classloader */
||      public Class loadClass(String className,
||ScopedURLClassLoader source, boolean resolve)
||      throws ClassNotFoundException {
||
||          // short look into the class location cache, is synchronized in
||          // case that the relevant target is simultaneously teared down
||          synchronized(classLoaders) {
||              ScopedURLClassLoader target= (ScopedURLClassLoader)
||              classLocations.get(className);
||
||              // its there, so log and load it
||              if(target!=null) {
||                  addDependency(source,target);
||                  // we can be sure that the target loader
||                  // has the class already in its own cache
||                  // so this call should not cost much
||                  return target.loadClass(className,resolve);
||              }
||
||              // otherwise we do a big lookup
||              Iterator allLoaders=classLoaders.values().iterator();
||
||              while(allLoaders.hasNext()) {
||                  target=(ScopedURLClassLoader) allLoaders.next();
||
||              // no recursion, please
||              if(!target.equals(source)) {
||                  try{
||                      Class
||foundClass=target.loadClassProperly(className,resolve);
||                      classLocations.put(className,target);
||                      addDependency(source,target);
||                      return foundClass;
||                  } catch(ClassNotFoundException e) {
||                      // proceed with the next loaders in scope
||                  }
||              }
||          } // while
||
||          // no loader in the scope has been able to load the class
||          throw new ClassNotFoundException("could not resolve class "+
||          className+" in scope.");
||
||          } // sync
||      }
||
||      /** gets a URL on behalf of a given classloader */
||      public URL getResource(String name, ScopedURLClassLoader source) {
||
||          // short look into the resource location cache, is
||synchronized in
||          // case that the relevant target is simultaneously teared down
||          synchronized(classLoaders) {
||              ScopedURLClassLoader target= (ScopedURLClassLoader)
||                  resourceLocations.get(name);
||
||              // its there, so log and load it
||              if(target!=null)
||                  addDependency(source,target);
||
||             // the lock is released here, so that other threads
||could run too
||              if(target!=null)
||                  return target.getResource(name);
||
||              // otherwise we do a big lookup
||              Iterator allLoaders=classLoaders.values().iterator();
||
||              while(allLoaders.hasNext()) {
||                  target=(ScopedURLClassLoader) allLoaders.next();
||
||              // no recursion, please
||              if(!target.equals(source)) {
||                      URL foundResource=target.getResourceProperly(name);
||                      if(foundResource!=null) {
||                          resourceLocations.put(name,target);
||                          addDependency(source,target);
||                          return foundResource;
||                      }
||              }
||          } // while
||
||          // no loader in the scope has been able to load the resource
||          return null;
||
||          } // sync
||      }
||
||  }
||
||
||
||  1.1
||jboss/src/main/org/jboss/deployment/scope/ScopedURLClassLoader.java
||
||  Index: ScopedURLClassLoader.java
||  ===================================================================
||  /*
||  * JBoss, the OpenSource EJB server
||  *
||  * Distributable under LGPL license.
||  * See terms of license at gnu.org.
||  */
||
||  package org.jboss.deployment.scope;
||
||  import org.jboss.deployment.Deployment;
||  import java.net.URL;
||  import java.net.URLClassLoader;
||
||  import java.util.Collection;
||
||  /**
||   * A URLClassLoader that is tight to some J2EE deployment and that is
||   * able to share classes/resources under the associated scope.
||Hey, JDK-S**ckers
||   * why did you annotate getResources final?
||   * @author  cgjung
||   * @version 0.9
||   */
||  public class ScopedURLClassLoader extends URLClassLoader {
||
||      /** reference to the scope to which resource loading calls
||       *  can be delegated to.
||       */
||      final protected Scope scope;
||
||      /**
||       * reference to the deployment that is associated
||       * with this classloader
||       */
||      final protected Deployment deployment;
||
||
||      /** Creates new ScopedURLClassLoader given a set of urls and
||a parent,
||       *  representing a particular deployment */
||      public ScopedURLClassLoader(URL[] urls, ClassLoader parent,
||Deployment deployment, Scope scope) {
||          super(urls,parent);
||          this.scope=scope;
||          this.deployment=deployment;
||          scope.registerClassLoader(this);
||      }
||
||      /** exposes the proper loadClass call */
||      protected Class loadClassProperly(String name, boolean
||resolve) throws ClassNotFoundException {
||          return super.loadClass(name,resolve);
||      }
||
||      /** redirects loadClass in case that it could not be found
|locally */
||      protected Class loadClass(String name, boolean resolve)
||throws ClassNotFoundException {
||          try{
||              return super.loadClass(name,resolve);
||          } catch(ClassNotFoundException e) {
||              // redirect
||              return scope.loadClass(name,this,resolve);
||          }
||      }
||
||      /** exposes the proper getResource call */
||      protected URL getResourceProperly(String name) {
||          return super.getResource(name);
||      }
||
||      /** redirects getResource in case that it could not be found
||locally */
||      public URL getResource(String name) {
||          URL result=super.getResource(name);
||          if(result==null)
||              result=scope.getResource(name,this);
||          return result;
||      }
||
||      /** what happens on undeploy, could be overridden to tear
||down meta-data and such */
||      protected void onUndeploy() {
||          // nothing
||      }
||
||      /** returns a set of relative urls in string spec that
||       *  point to applications to which this application
||       *  is (most likely) dependent on
||       *  this feature is only prototypically implemented using a
||property file
||       */
||      public String[] getDependingApplications() {
||          try{
||              java.util.Properties localProps=new java.util.Properties();
||
||
||localProps.load(getResourceProperly("META-INF/org.jboss.deployment.
||scope.properties").openStream());
||
||              java.util.StringTokenizer tok=
||                  new
||java.util.StringTokenizer(localProps.getProperty("org.jboss.deploym
||ent.scope.dependencies",""),";");
||
||              Collection allDeps=new java.util.ArrayList();
||
||              while(tok.hasMoreTokens())
||                  allDeps.add(tok.nextToken());
||
||              return (String[]) allDeps.toArray(new
||String[allDeps.size()]);
||          } catch(Exception e) {
||              return new String[0];
||          }
||      }
||
||
||
||  }
||
||
||
||  1.1
||jboss/src/main/org/jboss/deployment/scope/package.html
||
||  Index: package.html
||  ===================================================================
||  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
||
||  <HTML>
||    <HEAD>
||      <TITLE></TITLE>
||    </HEAD>
||    <BODY>
||      This package hosts an extension to the J2eeDeployer that is able to
||      virtually merge J2EE applications into a so-called scope. These
||      scoped applications are able to share classes and resources.
||The dependencies
||      between the applications are logged and used to coherently
||undeploy parts
||      of the scope.
||    </BODY>
||  </HTML>
||
||
||
||
||_______________________________________________
||Jboss-development mailing list
||[EMAIL PROTECTED]
||http://lists.sourceforge.net/lists/listinfo/jboss-development
|
|
|
|_______________________________________________
|Jboss-development mailing list
|[EMAIL PROTECTED]
|http://lists.sourceforge.net/lists/listinfo/jboss-development
|
|_______________________________________________
|Jboss-development mailing list
|[EMAIL PROTECTED]
|http://lists.sourceforge.net/lists/listinfo/jboss-development



_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/jboss-development

Reply via email to