You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ode.apache.org by va...@apache.org on 2011/01/06 18:44:08 UTC

svn commit: r1055956 - in /ode/branches/ode-1.3.5.x/jbi-bundle/src/main: java/org/apache/ode/jbi/osgi/ java/org/apache/ode/jbi/osgi/deployer/ resources/META-INF/spring/

Author: vanto
Date: Thu Jan  6 17:44:08 2011
New Revision: 1055956

URL: http://svn.apache.org/viewvc?rev=1055956&view=rev
Log:
ODE-901: Improved handling of SMX4 BPEL bundles. Big thanks to Kurt Westerfeld.

Added:
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeDeployedBundle.java
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtender.java
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderImpl.java
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderThreadFactory.java
Modified:
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/ServiceUnitActivator.java
    ode/branches/ode-1.3.5.x/jbi-bundle/src/main/resources/META-INF/spring/ode-jbi.xml

Modified: ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/ServiceUnitActivator.java
URL: http://svn.apache.org/viewvc/ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/ServiceUnitActivator.java?rev=1055956&r1=1055955&r2=1055956&view=diff
==============================================================================
--- ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/ServiceUnitActivator.java (original)
+++ ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/ServiceUnitActivator.java Thu Jan  6 17:44:08 2011
@@ -19,101 +19,27 @@
 
 package org.apache.ode.jbi.osgi;
 
-import java.io.File;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Enumeration;
+import java.util.logging.Logger;
 
-import javax.jbi.component.Component;
-import javax.jbi.component.ServiceUnitManager;
-
-import org.apache.commons.io.IOUtils;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
+
 
 /**
- * @author mproch
+ * This class is now deprecated in favor of using OdeExtender service, which watches
+ * deployment of bundles and manages start/stop/install/uninstall/update from OSGi
+ * to ODE.
  * 
+ * @deprecated
+ * @see OdeExtenderImpl
  */
 public class ServiceUnitActivator implements BundleActivator {
-
-    File rootDir;
-    String generatedName;
+    private static final Logger LOG = Logger.getLogger(ServiceUnitActivator.class.getName());
 
     public void start(BundleContext context) throws Exception {
-        generatedName = context.getBundle().getSymbolicName();
-        rootDir = context.getDataFile("bpelData");
-        rootDir.mkdirs();
-        Enumeration<?> en = context.getBundle().findEntries("/", "*", false);
-        while (en.hasMoreElements()) {
-            copyOne(rootDir, (URL) en.nextElement());
-        }
-        ServiceReference[] refs = context.getAllServiceReferences(
-                "javax.jbi.component.Component", "(&(NAME=OdeBpelEngine))");
-        if (refs == null || refs.length != 1) {
-            throw new RuntimeException("no appropriate service :(");
-        }
-        ServiceUnitManager suM = ((Component) context.getService(refs[0]))
-                .getServiceUnitManager();
-        ClassLoader l = Thread.currentThread().getContextClassLoader();
-        try {
-            ClassLoader suL = suM.getClass().getClassLoader();
-            Thread.currentThread().setContextClassLoader(new BundleClassLoader(suL, context.getBundle()));
-            suM.deploy(generatedName, rootDir.getAbsolutePath());
-            suM.init(generatedName, rootDir.getAbsolutePath());
-            suM.start(generatedName);
-        } finally {
-            Thread.currentThread().setContextClassLoader(l);
-        }
-
-    }
-
-    private void copyOne(File dest, URL url) throws Exception {
-        File d = new File(dest, url.getPath());
-        InputStream str = url.openStream();
-        if (str != null) {
-            FileWriter wr = new FileWriter(d);
-            try {
-                IOUtils.copy(str, wr);
-            } finally {
-                wr.flush();
-                wr.close();
-            }
-        }
+	LOG.warning("Use of " + this.getClass().getName() + " has been deprecated.  BPEL bundle deployment is now handled by the ODE extender service.  Remove Bundle-Activator from OSGi manifest for bundle: " + context.getBundle().getSymbolicName());
     }
 
     public void stop(BundleContext context) throws Exception {
-        ServiceReference[] refs = context.getAllServiceReferences(
-                "javax.jbi.component.Component", "(&(NAME=OdeBpelEngine))");
-        if (refs == null || refs.length != 1) {
-            throw new RuntimeException("no appropriate service :(");
-        }
-        ServiceUnitManager suM = ((Component) context.getService(refs[0]))
-                .getServiceUnitManager();
-        suM.shutDown(generatedName);
-        suM.undeploy(generatedName, rootDir.getAbsolutePath());
-
     }
-
-    public class BundleClassLoader extends ClassLoader {
-        private final Bundle delegate;
-
-        public BundleClassLoader(ClassLoader parent, Bundle delegate) {
-            super(parent);
-            this.delegate = delegate;
-        }
-
-        @Override
-        public Class<?> loadClass(String name) throws ClassNotFoundException {
-            try {
-                return getParent().loadClass(name);
-            } catch (Exception e) {
-                return delegate.loadClass(name);
-            }
-        }
-    }
-
 }

Added: ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeDeployedBundle.java
URL: http://svn.apache.org/viewvc/ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeDeployedBundle.java?rev=1055956&view=auto
==============================================================================
--- ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeDeployedBundle.java (added)
+++ ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeDeployedBundle.java Thu Jan  6 17:44:08 2011
@@ -0,0 +1,304 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ode.jbi.osgi.deployer;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jbi.component.Component;
+import javax.jbi.component.ComponentLifeCycle;
+import javax.jbi.component.ServiceUnitManager;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.xml.namespace.QName;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.ode.bpel.iapi.ProcessStore;
+import org.apache.ode.jbi.OdeContext;
+import org.osgi.framework.Bundle;
+
+public class OdeDeployedBundle {
+    private static final Logger LOG = Logger.getLogger(OdeDeployedBundle.class.getName());
+    private boolean updated;
+    private Bundle bundle;
+    private OdeExtenderImpl odeRegistrationService;
+    private File rootDir;
+    private String duName;
+    private String name;
+
+    OdeDeployedBundle(Bundle bundle, OdeExtenderImpl odeRegistrationService) {
+	if (LOG.isLoggable(Level.FINE))
+	    LOG.fine("Initialized ODE service unit deployer for bundle: " + bundle.getSymbolicName());
+	this.bundle = bundle;
+	this.odeRegistrationService = odeRegistrationService;
+    }
+
+    String getName() {
+	if (this.name == null)
+	    this.name = this.bundle.getSymbolicName();
+	return this.name;
+    }
+
+    private String getDUName() {
+	if (this.duName == null)
+	    this.duName = getName() + "-" + bundle.getVersion().getMicro();
+	return this.duName;
+    }
+
+    private File getRootDir() {
+	if (this.rootDir == null && this.bundle.getBundleContext() != null)
+	    this.rootDir = this.bundle.getBundleContext().getDataFile("bpelData/" + getDUName());
+	return this.rootDir;
+    }
+
+    void doStart() throws Exception {
+	// If we are already started, don't bother starting again.
+	LOG.info("Starting ODE service unit: " + getName());
+
+	// Wait until ODE is available before starting.
+	waitAvailable();
+
+	// Do we need to undeploy first?
+	boolean needUpdate = updated;
+	boolean forceDeploy = needUpdate;
+	this.updated = false;
+
+	// Do deploy.
+	this.deploy(this.bundle, this.odeRegistrationService.getOdeComponent().getServiceUnitManager(), forceDeploy, needUpdate);
+    }
+
+    void doStop() throws Exception {
+	LOG.info("Stopping ODE service unit: " + getName());
+	this.shutdown(this.bundle, this.odeRegistrationService.getOdeComponent().getServiceUnitManager());
+    }
+
+    void doInstall() throws Exception {
+	LOG.info("Installing ODE service unit: " + getName());
+    }
+
+    void doUninstall() throws Exception {
+	LOG.info("Uninstalling ODE service unit: " + getName());
+	this.undeploy(this.bundle, this.odeRegistrationService.getOdeComponent().getServiceUnitManager());
+    }
+
+    void doUpdate() throws Exception {
+	LOG.info("Updating ODE service unit: " + getName());
+	
+	// We simply mark the update state of this bundle so that on next start, we do a redeploy
+	updated = true;
+    }
+
+    private void deploy(Bundle bundle, ServiceUnitManager suM, boolean forceDeploy, boolean undeploy) throws Exception {
+	// Do deployment.
+	File rootDir = getRootDir();
+	String duName = getDUName();
+	boolean needDeploy = rootDir.mkdirs() || forceDeploy;
+	if (LOG.isLoggable(Level.FINE))
+	    LOG.fine("Exploding content to " + rootDir + " for " + duName);
+	Enumeration<?> en = bundle.findEntries("/", "*", false);
+	while (en.hasMoreElements())
+	    needDeploy |= copyOne(rootDir, (URL) en.nextElement());
+
+	// Now start it.
+	ClassLoader l = Thread.currentThread().getContextClassLoader();
+	try {
+	    ClassLoader suL = suM.getClass().getClassLoader();
+	    Thread.currentThread().setContextClassLoader(new BundleClassLoader(suL, bundle));
+	    try {
+		// Try first an init/start, which will fail if the process isn't
+		// here.
+		if (needDeploy) {
+		    // If deployed, undeploy first.
+		    if (undeploy && isDeployed(duName)) {
+			// Do the undeploy to service unit manager.
+			LOG.info("Performing undeploy " + duName + " from dir " + rootDir);
+			suM.undeploy(duName, rootDir.getAbsolutePath());
+
+			// Now, remove any .cbp files.
+			File[] cbpFiles = rootDir.listFiles(new FilenameFilter() {
+			    @Override
+			    public boolean accept(File dir, String name) {
+				return name.endsWith(".cbp");
+			    }
+			});
+			for (File cbpFile : cbpFiles) {
+			    LOG.info("Removing compiled bpel process: " + cbpFile);
+			    cbpFile.delete();
+			}
+		    }
+
+		    LOG.info("Deploying " + duName + " to dir " + rootDir);
+		    suM.deploy(duName, rootDir.getAbsolutePath());
+		}
+		suM.init(duName, rootDir.getAbsolutePath());
+		suM.start(duName);
+	    } catch (javax.jbi.management.DeploymentException e) {
+		LOG.log(Level.WARNING, "Deploy failed; could not deploy/start this bundle: " + this.getName(), e);
+		throw e;
+	    }
+	} finally {
+	    Thread.currentThread().setContextClassLoader(l);
+	}
+    }
+
+    private boolean isDeployed(String processName) {
+	boolean Result = true;
+	try {
+	    // Get the "ProcessStore" interface by grabbing the internal field
+	    // of OdeContext and querying for the processes. Could also use PMAPI here, 
+	    // but in any case we just need to know if the process exists in a deployed state.
+	    //
+	    // TODO: add a OdeContext.getStore() method to clean this up.
+	    OdeContext inst = OdeContext.getInstance();
+	    Field _store = inst.getClass().getDeclaredField("_store");
+	    _store.setAccessible(true);
+	    ProcessStore ps = (ProcessStore) _store.get(inst);
+	    List<QName> processes = ps.listProcesses(processName);
+	    Result = processes != null && !processes.isEmpty();
+	} catch (Exception e) {
+	    LOG.log(Level.WARNING, "Could not determine deployment state for process: " + processName, e);
+	}
+	return Result;
+    }
+
+    private boolean copyOne(File dest, URL url) throws Exception {
+	File d = new File(dest, url.getPath());
+	boolean needDeploy = !d.exists();
+	long length = d.exists() ? d.length() : 0L;
+	InputStream str = url.openStream();
+	if (str != null) {
+	    FileWriter wr = new FileWriter(d);
+	    try {
+		IOUtils.copy(str, wr);
+	    } finally {
+		wr.flush();
+		wr.close();
+	    }
+
+	    // If file is zero-length (which is the case handling a directory),
+	    // just remove it.
+	    if (d.exists() && d.length() == 0) {
+		d.delete();
+		needDeploy = false;
+	    } else
+		needDeploy |= length != d.length();
+	}
+	return needDeploy;
+    }
+
+    private void shutdown(Bundle bundle, ServiceUnitManager suM) throws Exception {
+	String duName = getDUName();
+	if (suM != null) {
+	    suM.stop(duName);
+	    suM.shutDown(duName);
+	} else {
+	    LOG.warning("Could not shutdown this process (" + duName + ") because ode component was never located");
+	}
+    }
+
+    private void undeploy(Bundle bundle, ServiceUnitManager suM) throws Exception {
+	String duName = getDUName();
+	if (suM != null) {
+	    if (isDeployed(duName)) {
+		// Use ODE's classloader to avoid class loading from the bundle
+		// being undeployed.
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		try {
+		    Thread.currentThread().setContextClassLoader(suM.getClass().getClassLoader());
+		    File rootDir = getRootDir();
+		    LOG.info("Performing undeploy " + duName + " from dir " + rootDir);
+		    suM.undeploy(duName, rootDir.getAbsolutePath());
+		} finally {
+		    Thread.currentThread().setContextClassLoader(classLoader);
+		}
+	    }
+	} else {
+	    LOG.warning("Could not shutdown this process (" + duName + ") because ode component was never located");
+	}
+    }
+
+    public class BundleClassLoader extends ClassLoader {
+	private final Bundle delegate;
+
+	public BundleClassLoader(ClassLoader parent, Bundle delegate) {
+	    super(parent);
+	    this.delegate = delegate;
+	}
+
+	@Override
+	public Class<?> loadClass(String name) throws ClassNotFoundException {
+	    try {
+		return getParent().loadClass(name);
+	    } catch (Exception e) {
+		return delegate.loadClass(name);
+	    }
+	}
+    }
+
+    private void waitAvailable() throws InterruptedException {
+	/**
+	 * We need to wait until the service unit manager is available before 
+	 * proceeding.  Also, since the ode component itself does an asynchronous
+	 * start with respect to this bundle, we need to wait until it is done
+	 * initializing.  This would be much cleaner if we could simply
+	 * call "isStarted" on OdeLifeCycle, which maintains a started state.
+	 * 
+	 * If we do not wait until the ode component is started, deployments
+	 * will fail sporadically because of asynchronous start race conditions.
+	 */
+	boolean showedWait = false;
+	while (this.odeRegistrationService.getOdeComponent().getServiceUnitManager() == null || !isStarted(this.odeRegistrationService.getOdeComponent())) {
+
+	    // Do a wait.
+	    if (!showedWait) {
+		LOG.info("Waiting for ODE to arrive (" + getName() + ")...");
+		showedWait = true;
+	    }
+	    Thread.sleep(500L);
+	}
+    }
+
+    private boolean isStarted(Component odeComponent) {
+	
+	boolean Result = true;
+	try {
+	    // Get the ODE component started state by grabbing the internal field
+	    // of OdeLifeCycle.  We cannot rely on the started state of the ODE
+	    // component bundle.
+	    //
+	    // TODO: add OdeLifeCycle.isStarted() and do a cast of odeComponent.getLifeCycle() accordingly.
+	    ComponentLifeCycle inst = odeComponent.getLifeCycle();
+	    Field _started = inst.getClass().getDeclaredField("_started");
+	    _started.setAccessible(true);
+	    Result = Boolean.TRUE.equals(_started.get(inst));
+	} catch (Exception e) {
+	    LOG.log(Level.WARNING, "Could not determine started state for ode component", e);
+	}
+	return Result;
+    }
+}

Added: ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtender.java
URL: http://svn.apache.org/viewvc/ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtender.java?rev=1055956&view=auto
==============================================================================
--- ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtender.java (added)
+++ ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtender.java Thu Jan  6 17:44:08 2011
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ode.jbi.osgi.deployer;
+
+import org.osgi.framework.Bundle;
+
+public interface OdeExtender {
+    public Bundle[] getBundles();
+}

Added: ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderImpl.java
URL: http://svn.apache.org/viewvc/ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderImpl.java?rev=1055956&view=auto
==============================================================================
--- ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderImpl.java (added)
+++ ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderImpl.java Thu Jan  6 17:44:08 2011
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ode.jbi.osgi.deployer;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jbi.component.Component;
+
+import org.apache.ode.jbi.osgi.ServiceUnitActivator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class OdeExtenderImpl implements OdeExtender, InitializingBean, DisposableBean, BundleContextAware {
+    private static final Logger LOG = Logger.getLogger(OdeExtenderImpl.class.getName());
+    private static final String SU_ACTIVATOR_DEPRECATED = ServiceUnitActivator.class.getName();
+    private BundleContext bundleContext;
+    private BundleTracker tracker;
+    private Component odeComponent;
+    private Executor executor;
+
+    public OdeExtenderImpl() {
+    }
+
+    @Override
+    public void setBundleContext(BundleContext bundleContext) {
+	this.bundleContext = bundleContext;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+	// Create our executor.
+	executor = Executors.newSingleThreadExecutor(new OdeExtenderThreadFactory("ODE Extender"));
+
+	// Start tracking bundles. We are looking for /deploy.xml as a signature
+	// to deploy the ODE BPEL bundle, if the bundle has at least one .bpel
+	// file.
+	int stateMask = Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING | Bundle.INSTALLED | Bundle.UNINSTALLED;
+	this.tracker = new BundleTracker(this.bundleContext, stateMask, new ODEBundleTrackerCustomizer());
+	this.tracker.open();
+    }
+
+    @Override
+    public void destroy() throws Exception {
+	// Close the tracker.
+	BundleTracker tracker = this.tracker;
+	this.tracker = null;
+	if (tracker != null)
+	    tracker.close();
+
+	// Drop our thread pool.
+	this.executor = null;
+    }
+
+    @Override
+    public Bundle[] getBundles() {
+	return this.tracker.getBundles();
+    }
+
+    public void setOdeComponent(Component odeComponent) {
+	this.odeComponent = odeComponent;
+    }
+
+    public Component getOdeComponent() {
+	return odeComponent;
+    }
+
+    private boolean isBPELBundle(Bundle bundle) {
+	boolean Result = false;
+
+	// First see if there is a deploy.xml.
+	URL entry = bundle.getEntry("deploy.xml");
+	if (entry != null) {
+	    // Next, check if there's at least one BPEL file.
+	    @SuppressWarnings("rawtypes")
+	    Enumeration bpelFiles = bundle.findEntries("/", "*.bpel", false);
+	    if (bpelFiles != null && bpelFiles.hasMoreElements()) {
+		// Make sure there's a symbolic name.
+		if (bundle.getSymbolicName() != null) {
+		    // Make sure there's no bundle activator.
+		    // NOTE: if the "ServiceUnitActivator" is found, we hijack those bundles as well and manage them.
+		    Dictionary<?, ?> headers = bundle.getHeaders();
+		    Object activator = null;
+		    if (headers == null || (activator = headers.get(Constants.BUNDLE_ACTIVATOR)) == null || SU_ACTIVATOR_DEPRECATED.equals(activator)) {
+			if (LOG.isLoggable(Level.FINE))
+			    LOG.fine("Recognized ODE deployment bundle: " + bundle.getSymbolicName());
+			Result = true;
+		    } else
+			LOG.warning("Ignoring ODE bundle " + bundle.getSymbolicName() + " which has custom activator: " + activator);
+		} else
+		    LOG.warning("Ignoring ODE bundle " + bundle.getBundleId() + " which has no OSGi symbolic name");
+	    }
+	}
+
+	return Result;
+    }
+
+    private class ODEBundleTrackerCustomizer implements BundleTrackerCustomizer {
+	private Map<Long, OdeDeployedBundle> bundles = new ConcurrentHashMap<Long, OdeDeployedBundle>();
+
+	@Override
+	public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+	}
+
+	@Override
+	public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+	    if (event != null) {
+		if (LOG.isLoggable(Level.FINE))
+		    LOG.fine("Received " + getEventType(event.getType()) + " event for bundle: " + bundle.getSymbolicName());
+		switch (event.getType()) {
+		case BundleEvent.STARTED:
+		    executor.execute(new Start((OdeDeployedBundle) object));
+		    break;
+		case BundleEvent.STOPPING:
+		    executor.execute(new Stop((OdeDeployedBundle) object));
+		    break;
+		case BundleEvent.INSTALLED:
+		    executor.execute(new Install((OdeDeployedBundle) object));
+		    break;
+		case BundleEvent.UNINSTALLED:
+		    executor.execute(new Uninstall((OdeDeployedBundle) object));
+		    break;
+		case BundleEvent.UPDATED:
+		    executor.execute(new Update((OdeDeployedBundle) object));
+		    break;
+		}
+
+		// Do this outside the try/catch above. Last chance to drop a
+		// bundle.
+		if (event.getType() == BundleEvent.UNINSTALLED)
+		    bundles.remove(bundle.getBundleId());
+	    }
+	}
+
+	private String getEventType(int type) {
+	    switch (type) {
+	    case BundleEvent.INSTALLED:
+		return "installed";
+	    case BundleEvent.LAZY_ACTIVATION:
+		return "lazy activation";
+	    case BundleEvent.RESOLVED:
+		return "resolved";
+	    case BundleEvent.STARTED:
+		return "started";
+	    case BundleEvent.STARTING:
+		return "starting";
+	    case BundleEvent.STOPPED:
+		return "stopped";
+	    case BundleEvent.STOPPING:
+		return "stopping";
+	    case BundleEvent.UNINSTALLED:
+		return "uninstalled";
+	    case BundleEvent.UNRESOLVED:
+		return "unresolved";
+	    case BundleEvent.UPDATED:
+		return "updated";
+	    }
+	    return "(unknown: " + type + ")";
+	}
+
+	@Override
+	public Object addingBundle(Bundle bundle, BundleEvent event) {
+	    Object Result = null;
+
+	    // Is this a BPEL bundle?
+	    if (isBPELBundle(bundle)) {
+		// Found BPEL signature; setup deployer.
+		OdeDeployedBundle deployer = bundles.get(bundle.getBundleId());
+		if (deployer == null)
+		    bundles.put(bundle.getBundleId(), deployer = new OdeDeployedBundle(bundle, OdeExtenderImpl.this));
+		Result = deployer;
+
+		// Is this an initial bundle?
+		if (event == null) {
+		    // If the bundle is active, then we didn't start it. So
+		    // start it now.
+		    if (bundle.getState() == Bundle.ACTIVE)
+			executor.execute(new Start(deployer));
+		} else
+		    modifiedBundle(bundle, event, deployer);
+	    }
+
+	    return Result;
+	}
+    }
+
+    private static abstract class OperationClosure implements Runnable {
+	protected OdeDeployedBundle target;
+
+	OperationClosure(OdeDeployedBundle target) {
+	    this.target = target;
+	}
+
+	@Override
+	public final void run() {
+	    String name = Thread.currentThread().getName();
+	    try {
+		Thread.currentThread().setName(name + ":" + getClass().getSimpleName() + ":" + target.getName());
+		perform();
+	    } catch (Exception e) {
+		LOG.log(Level.WARNING, "Could not perform '" + getClass().getSimpleName() + "' operation on bundle: " + target.getName(), e);
+	    } finally {
+		Thread.currentThread().setName(name);
+	    }
+	}
+
+	protected abstract void perform() throws Exception;
+    }
+
+    private static class Start extends OperationClosure {
+	Start(OdeDeployedBundle target) {
+	    super(target);
+	}
+
+	@Override
+	protected void perform() throws Exception {
+	    target.doStart();
+	}
+    }
+
+    private static class Stop extends OperationClosure {
+	Stop(OdeDeployedBundle target) {
+	    super(target);
+	}
+
+	@Override
+	protected void perform() throws Exception {
+	    target.doStop();
+	}
+    }
+
+    private static class Install extends OperationClosure {
+	Install(OdeDeployedBundle target) {
+	    super(target);
+	}
+
+	@Override
+	protected void perform() throws Exception {
+	    target.doInstall();
+	}
+    }
+
+    private static class Uninstall extends OperationClosure {
+	Uninstall(OdeDeployedBundle target) {
+	    super(target);
+	}
+
+	@Override
+	protected void perform() throws Exception {
+	    target.doUninstall();
+	}
+    }
+
+    private static class Update extends OperationClosure {
+	Update(OdeDeployedBundle target) {
+	    super(target);
+	}
+
+	@Override
+	protected void perform() throws Exception {
+	    target.doUpdate();
+	}
+    }
+}

Added: ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderThreadFactory.java
URL: http://svn.apache.org/viewvc/ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderThreadFactory.java?rev=1055956&view=auto
==============================================================================
--- ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderThreadFactory.java (added)
+++ ode/branches/ode-1.3.5.x/jbi-bundle/src/main/java/org/apache/ode/jbi/osgi/deployer/OdeExtenderThreadFactory.java Thu Jan  6 17:44:08 2011
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ode.jbi.osgi.deployer;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+public class OdeExtenderThreadFactory implements ThreadFactory {
+    private final ThreadFactory factory = Executors.defaultThreadFactory();
+    private final String name;
+
+    public OdeExtenderThreadFactory(String name) {
+	this.name = name;
+    }
+
+    public Thread newThread(Runnable r) {
+	final Thread t = factory.newThread(r);
+	t.setName(name);
+	t.setDaemon(true);
+	return t;
+    }
+}

Modified: ode/branches/ode-1.3.5.x/jbi-bundle/src/main/resources/META-INF/spring/ode-jbi.xml
URL: http://svn.apache.org/viewvc/ode/branches/ode-1.3.5.x/jbi-bundle/src/main/resources/META-INF/spring/ode-jbi.xml?rev=1055956&r1=1055955&r2=1055956&view=diff
==============================================================================
--- ode/branches/ode-1.3.5.x/jbi-bundle/src/main/resources/META-INF/spring/ode-jbi.xml (original)
+++ ode/branches/ode-1.3.5.x/jbi-bundle/src/main/resources/META-INF/spring/ode-jbi.xml Thu Jan  6 17:44:08 2011
@@ -63,5 +63,18 @@
         <prop key="ode-jbi.jca.port">0</prop>
         <prop key="javax.persistence.provider">org.apache.openjpa.persistence.PersistenceProviderImpl</prop>
     </osgix:cm-properties>
-    
+
+    <!-- ODE Extender Service -->
+    <bean id="ode-extender-service" class="org.apache.ode.jbi.osgi.deployer.OdeExtenderImpl">
+      <property name="odeComponent">
+        <ref bean="ode-jbi-component" />
+      </property>
+    </bean>
+
+    <osgi:service ref="ode-extender-service">
+        <osgi:interfaces>
+            <value>org.apache.ode.jbi.osgi.deployer.OdeExtender</value>
+        </osgi:interfaces>
+    </osgi:service>
+
 </beans>