org.xerela.provider.scheduler.internal.SchedulerActivator.java Source code

Java tutorial

Introduction

Here is the source code for org.xerela.provider.scheduler.internal.SchedulerActivator.java

Source

/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is Ziptie Client Framework.
 * 
 * The Initial Developer of the Original Code is AlterPoint.
 * Portions created by AlterPoint are Copyright (C) 2006,
 * AlterPoint, Inc. All Rights Reserved.
 */

package org.xerela.provider.scheduler.internal;

import java.io.File;
import java.io.FileInputStream;
import java.net.URI;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.eclipse.core.runtime.Platform;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
import org.xerela.provider.scheduler.IScheduler;
import org.xerela.provider.scheduler.JobType;
import org.xerela.provider.scheduler.Scheduler;
import org.xerela.provider.scheduler.SchedulerException;
import org.xerela.zap.jta.TransactionElf;
import org.xerela.zap.security.ISecurityService;

/**
 * Activator.
 */
public class SchedulerActivator implements BundleActivator, IRegistryChangeListener {
    private static final String EXTENSION_NAMESPACE = "org.xerela.provider.scheduler";
    private static final String EXTENSION_POINT_ID = EXTENSION_NAMESPACE + ".quartzJob";
    private static Logger USER_LOG = Logger.getLogger(SchedulerActivator.class);

    private static BundleContext context;
    private static Scheduler scheduler;
    private static ServiceTracker dsTracker;
    private static ServiceTracker sessionTracker;
    private static ServiceTracker securityTracker;
    private static boolean ramStore;

    private ServiceTracker bundleTracker;
    private IExtensionRegistry extensionRegistry;
    private ServiceRegistration serviceRegistration;

    /**
     * Default constructor.
     */
    public SchedulerActivator() {
        extensionRegistry = Platform.getExtensionRegistry();
        extensionRegistry.addRegistryChangeListener(this, EXTENSION_NAMESPACE);
    }

    /** {@inheritDoc} */
    public void start(BundleContext ctx) throws Exception {
        USER_LOG.info("Scheduler starting...");

        try {
            context = ctx;

            sessionTracker = new ServiceTracker(context, SessionFactory.class.getName(), null);
            sessionTracker.open();

            dsTracker = new ServiceTracker(context, DataSource.class.getName(), null);
            dsTracker.open();

            securityTracker = new ServiceTracker(context, ISecurityService.class.getName(), null);
            securityTracker.open();

            Properties props = getSchedulerProperties();
            ramStore = props.getProperty("org.quartz.jobStore.class").indexOf("RAMJobStore") > -1;

            cleanupAbandonedJobs();

            createScheduler(props);

            USER_LOG.info("Scheduler started.");
        } catch (Exception e) {
            USER_LOG.fatal("Scheduler failed to start.", e);
            throw e;
        }
    }

    /** {@inheritDoc} */
    public void stop(BundleContext ctx) throws Exception {
        USER_LOG.info("Scheduler stopping...");
        extensionRegistry.removeRegistryChangeListener(this);

        if (bundleTracker != null) {
            bundleTracker.close();
            bundleTracker = null;
        }

        securityTracker.close();
        securityTracker = null;

        dsTracker.close();
        dsTracker = null;

        destroyScheduler();
        scheduler = null;

        sessionTracker.close();
        sessionTracker = null;

        USER_LOG.info("Scheduler stopped.");
    }

    /** {@inheritDoc} */
    public void registryChanged(IRegistryChangeEvent event) {
        IExtensionDelta[] extensionDeltas = event.getExtensionDeltas(EXTENSION_NAMESPACE);
        if (extensionDeltas.length > 0) {
            try {
                USER_LOG.info("Bundle change detected.  Restarting scheduler.");
                configureSchedulerJobTypes();
            } catch (SchedulerException se) {
                USER_LOG.fatal("Bundle change listener was unable to recreate scheduler service.", se);
            }
        }
    }

    private Properties getSchedulerProperties() throws Exception {
        String configArea = context.getProperty("osgi.configuration.area").replace(" ", "%20");
        File configDir = new File(URI.create(configArea));
        File configFile = new File(configDir,
                String.format("/quartz/quartz.%s.properties", System.getProperty("database", "derby")));
        if (!configFile.exists()) {
            configFile = new File(configDir, "/quartz/quartz.properties");
        }

        if (configFile.exists()) {
            USER_LOG.debug("Quartz configuration file using " + configFile);
            Properties props = new Properties();
            props.load(new FileInputStream(configFile));
            return props;
        } else {
            USER_LOG.error("No quartz configuration file");
            throw new Exception("Missing Quartz Configuration");
        }
    }

    /**
     * Create, register, and start the scheduler.
     *
     * @throws SchedulerException thrown if there is an error starting the scheduler.
     */
    private void createScheduler(Properties props) throws SchedulerException {
        scheduler = new Scheduler(props);
        configureSchedulerJobTypes();
        scheduler.start();
        serviceRegistration = context.registerService(IScheduler.class.getName(), scheduler, null);
        USER_LOG.info("Scheduler service registered.");
    }

    /**
     * Shutdown and deregister the scheduler.
     *
     * @throws SchedulerException thrown if there is an error shutting down the scheduler
     */
    private void destroyScheduler() throws SchedulerException {
        scheduler.shutdown();
        serviceRegistration.unregister();
        USER_LOG.info("Scheduler service unregistered.");
    }

    /**
     * Use the extension point registry to discover job types and register them.
     */
    private void configureSchedulerJobTypes() throws SchedulerException {
        IConfigurationElement[] configElements = extensionRegistry.getConfigurationElementsFor(EXTENSION_POINT_ID);

        if (configElements.length == 0) {
            USER_LOG.warn("No jobs discovered by scheduler.");
        }

        Map<String, JobType> jobTypeCopy = new HashMap<String, JobType>(scheduler.getJobTypeMapping());

        for (IConfigurationElement element : configElements) {
            String name = element.getAttribute("name");
            String className = element.getAttribute("class");
            String cudPermission = element.getAttribute("cudPermission");
            String runPermission = element.getAttribute("runPermission");

            try {
                String targetBundle = element.getContributor().getName();
                Bundle bundle = getBundle(targetBundle);
                Class clazz = bundle.loadClass(className);

                JobType jobType = jobTypeCopy.get(name);
                if (jobType == null) {
                    USER_LOG.info(String.format("Discovered Job '%s'", name));
                    if (USER_LOG.isDebugEnabled()) {
                        USER_LOG.debug(String.format("  Job '%s' in bundle '%s'", name, targetBundle));
                    }
                    scheduler.registerJobType(name, clazz, cudPermission, runPermission);
                } else if (!jobType.getJobClass().equals(clazz)) {
                    scheduler.unregisterJobType(name, jobType.getJobClass());
                    USER_LOG.info(
                            String.format("Rediscovered Updated Job Type '%s' in bundle '%s'", name, targetBundle));
                    scheduler.registerJobType(name, clazz, cudPermission, runPermission);
                }
                // otherwise the job type mapping is already known by the scheduler

                // remove job types as we discover them, so that only unknown (removed)
                // job types are still hanging around at the end
                jobTypeCopy.remove(name);
            } catch (ClassNotFoundException cnfe) {
                USER_LOG.error(String.format("Scheduler bundle unable to load Job Type with class '%s'", className),
                        cnfe);
            }
        }

        // remove any job types that weren't rediscovered.
        for (Map.Entry<String, JobType> me : jobTypeCopy.entrySet()) {
            scheduler.unregisterJobType(me.getKey(), me.getValue().getJobClass());
        }
    }

    /**
     * Returns the resolved bundle with the specified symbolic name that has the
     * highest version.  If no resolved bundles are installed that have the 
     * specified symbolic name then null is returned.
     * <p>
     * @param symbolicName the symbolic name of the bundle to be returned.
     * @return the bundle that has the specified symbolic name with the 
     * highest version, or <tt>null</tt> if no bundle is found.
     */
    private Bundle getBundle(String symbolicName) {
        PackageAdmin packageAdmin = getBundleAdmin();
        if (packageAdmin == null) {
            return null;
        }

        Bundle[] bundles = packageAdmin.getBundles(symbolicName, null);
        if (bundles == null) {
            return null;
        }

        //Return the first bundle that is not installed or uninstalled
        for (int i = 0; i < bundles.length; i++) {
            if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
                return bundles[i];
            }
        }
        return null;
    }

    /**
     * Get the PackageAdmin service for this OSGi instance.
     *
     * @return the PackageAdmin service instance for this OSGi instance.
     */
    private PackageAdmin getBundleAdmin() {
        if (bundleTracker == null) {
            if (context == null) {
                return null;
            }
            bundleTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
            bundleTracker.open();
        }
        return (PackageAdmin) bundleTracker.getService();
    }

    /**
     * Set the status of any jobs that are marked as "still running" to canceled.  Of we're just
     * starting up, these jobs are NOT running anymore.  Of course, I don't know what this means
     * in a bundle restart scenario ... not sure what to do about that, as it's theoretically 
     * possible for a schedule to be running at the time we restart this bundle.  Ah well, an
     * obscure bug for another day.
     */
    private void cleanupAbandonedJobs() {
        if (!isRAMStore()) {
            TransactionElf.beginOrJoinTransaction();

            try {
                Session session = getSessionFactory().getCurrentSession();
                Query query = session.createQuery(
                        "UPDATE ExecutionData e SET e.endTime=:et, e.canceled=true WHERE e.endTime=null");
                query.setTimestamp("et", Calendar.getInstance().getTime());
                int updated = query.executeUpdate();
                TransactionElf.commit();

                if (updated > 0) {
                    USER_LOG.info(String.format("Set status of %d abandonded jobs to cancelled.", updated));
                }
            } catch (Exception e) {
                USER_LOG.warn("Exception resetting status of abandoned jobs.", e);
            }
        }
    }

    /**
     * Lookup the data source service.
     * @return The data source instance.
     */
    public static DataSource getDataSource() {
        return (DataSource) dsTracker.getService();
    }

    /**
     * Lookup the hibernate session factory service.
     * @return The session factory instance.
     */
    public static SessionFactory getSessionFactory() {
        return (SessionFactory) sessionTracker.getService();
    }

    /**
     * Lookup the security service.
     * @return the security service instance.
     */
    public static ISecurityService getSecurityService() {
        return (ISecurityService) securityTracker.getService();
    }

    /**
     * Get the singleton scheduler.
     * @return The scheduler instance.
     */
    public static Scheduler getScheduler() {
        return scheduler;
    }

    /**
     * Method to check whether the scheduler is using a RAM Store.
     *
     * @return true if the scheduler using a RAM Store
     */
    public static boolean isRAMStore() {
        return ramStore;
    }
}