package org.apache.karaf.diagnostic.common;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.karaf.features.FeaturesService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Activator implements BundleActivator {

    private static final Logger LOGGER = LoggerFactory.getLogger(Activator.class);

    private Runnable featuresServiceSupport;


    /**
     * Called when this bundle is started so the Framework can perform the
     * bundle-specific activities necessary to start this bundle. This method
     * can be used to register services or to allocate any resources that this
     * bundle needs.
     * <p/>
     * <p/>
     * This method must complete and return to its caller in a timely manner.
     *
     * @param context The execution context of the bundle being started.
     * @throws Exception If this method throws an exception, this
     *                   bundle is marked as stopped and the Framework will remove this
     *                   bundle's listeners, unregister all services registered by this
     *                   bundle, and release all services used by this bundle.
     */
    public void start(BundleContext context) throws Exception {
        try {
        featuresServiceSupport = new FeaturesServiceSupport(context);
        }catch (NoClassDefFoundError ex) {
           LOGGER.warn("Feature service is not present, Feature dump provider will be disabled");
        }
    }

    /**
     * Called when this bundle is stopped so the Framework can perform the
     * bundle-specific activities necessary to stop the bundle. In general, this
     * method should undo the work that the <code>BundleActivator.start</code>
     * method started. There should be no active threads that were started by
     * this bundle when this bundle returns. A stopped bundle must not call any
     * Framework objects.
     * <p/>
     * <p/>
     * This method must complete and return to its caller in a timely manner.
     *
     * @param context The execution context of the bundle being stopped.
     * @throws Exception If this method throws an exception, the
     *                   bundle is still marked as stopped, and the Framework will remove
     *                   the bundle's listeners, unregister all services registered by the
     *                   bundle, and release all services used by the bundle.
     */
    public void stop(BundleContext context) throws Exception {
        if (featuresServiceSupport != null) {
            featuresServiceSupport.run();
        }
    }

    private static class FeaturesServiceSupport implements Runnable {

        private BundleContext context;
        private ServiceRegistration registration;
        private ServiceTracker serviceTracker;

        private FeaturesServiceSupport(BundleContext context) {
            this.context = context;
            InvocationHandler invocationHandler = new InvocationHandler() {
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                    return method.invoke(getFeaturesService(), objects);
                }
            };

            FeaturesService proxy = (FeaturesService) Proxy.newProxyInstance(FeaturesService.class.getClassLoader(), new Class[]{FeaturesService.class}, invocationHandler);
            FeaturesDumpProvider featuresDumpProvider = new FeaturesDumpProvider(proxy);
            registration = context.registerService("org.apache.karaf.diagnostic.core.DumpProvider", featuresDumpProvider, null);
        }

        public void run() {
           if (registration != null) {
               registration.unregister();
           }
        }

        private FeaturesService getFeaturesService() {
            serviceTracker = new ServiceTracker(context, FeaturesService.class.getName(), null);
            serviceTracker.open();
            return (FeaturesService) serviceTracker.getService();
        }
    }
}
