User: cgjung
Date: 01/05/18 08:14:19
Added: 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 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,dependentLoader.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.deployment.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