/*
* 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() {
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
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();
}
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;
}
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>();
public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
}
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 + ")";
}
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;
}
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();
}
}
}