/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.narayana.osgi.jta.internal;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.log.LogService;
public class Activator implements BundleActivator, ManagedService, Runnable {
public static final String PID = "org.jboss.narayana";
public static final String INTERN_PACKAGE = "org.jboss.narayana.osgi.jta.internal";
public static final String SERVER_CLASS = INTERN_PACKAGE + ".OsgiServer";
protected BundleContext bundleContext;
protected ExecutorService executor = new ThreadPoolExecutor(0, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
private AtomicBoolean scheduled = new AtomicBoolean();
private long schedulerStopTimeout = TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS);
private ServiceRegistration managedServiceRegistration;
private Dictionary<String, ?> configuration;
private Object service;
@Override
public void start(BundleContext context) throws Exception {
bundleContext = context;
scheduled.set(true);
Hashtable<String, Object> props = new Hashtable<>();
props.put(Constants.SERVICE_PID, PID);
managedServiceRegistration = bundleContext.registerService(ManagedService.class, this, props);
scheduled.set(false);
reconfigure();
}
@Override
public void stop(BundleContext context) throws Exception {
scheduled.set(true);
if (managedServiceRegistration != null) {
managedServiceRegistration.unregister();
}
executor.shutdown();
executor.awaitTermination(schedulerStopTimeout, TimeUnit.MILLISECONDS);
doStop();
}
ClassLoader createClassLoader() {
List<URL> urls = new ArrayList<>();
// Find our base url
String name = SERVER_CLASS.replace('.', '/') + ".class";
URL url = bundleContext.getBundle().getResource(name);
String strUrl = url.toExternalForm();
if (!strUrl.endsWith(name)) {
throw new IllegalStateException();
}
strUrl = strUrl.substring(0, strUrl.length() - name.length());
try {
urls.add(new URL(strUrl));
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
// Find all embedded jars
Collection<String> resources = bundleContext.getBundle().adapt(BundleWiring.class)
.listResources("/", "*.jar", BundleWiring.LISTRESOURCES_LOCAL);
for (String resource : resources) {
urls.add(bundleContext.getBundle().getResource(resource));
}
// Create the classloader
return new URLClassLoader(urls.toArray(new URL[urls.size()]),
new ClassLoader(getClass().getClassLoader()) {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// We forbid to load the server from the parent
if (name.startsWith(INTERN_PACKAGE)) {
throw new ClassNotFoundException(name);
}
return super.loadClass(name, resolve);
}
});
}
protected void doStart() throws Exception {
ClassLoader classLoader = createClassLoader();
Class<?> osgiServerClass = classLoader.loadClass(SERVER_CLASS);
service = osgiServerClass.getConstructor(BundleContext.class, Dictionary.class)
.newInstance(bundleContext, configuration);
service.getClass().getMethod("start").invoke(service);
}
protected void doStop() {
if (service != null) {
try {
service.getClass().getMethod("stop").invoke(service);
} catch (Throwable t) {
warn("Error stopping service", t);
} finally {
service = null;
}
}
}
public void updated(Dictionary<String, ?> properties) {
this.configuration = properties;
reconfigure();
}
protected void reconfigure() {
if (scheduled.compareAndSet(false, true)) {
executor.submit(this);
}
}
@Override
public void run() {
scheduled.set(false);
doStop();
try {
doStart();
} catch (Exception e) {
warn("Error starting service", e);
doStop();
}
}
protected void warn(String message, Throwable t) {
ServiceReference<LogService> ref = bundleContext.getServiceReference(LogService.class);
if (ref != null) {
LogService svc = bundleContext.getService(ref);
svc.log(LogService.LOG_WARNING, message, t);
bundleContext.ungetService(ref);
}
}
}