Package org.apache.sling.commons.scheduler.impl

Source Code of org.apache.sling.commons.scheduler.impl.QuartzScheduler$QuartzThreadPool

/*
* 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.sling.commons.scheduler.impl;

import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;

import org.apache.sling.commons.scheduler.Job;
import org.apache.sling.commons.scheduler.Scheduler;
import org.apache.sling.commons.threads.ThreadPool;
import org.apache.sling.commons.threads.ThreadPoolManager;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.impl.DirectSchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.simpl.RAMJobStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The quartz based implementation of the scheduler.
*
* @scr.component metatype="no" immediate="true"
* @scr.service interface="org.apache.sling.commons.scheduler.Scheduler"
* @scr.reference name="job" interface="org.apache.sling.commons.scheduler.Job" cardinality="0..n" policy="dynamic"
* @scr.reference name="task" interface="java.lang.Runnable" cardinality="0..n" policy="dynamic"
*/
public class QuartzScheduler implements Scheduler {

    /** Default log. */
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    protected static final String DEFAULT_QUARTZ_JOB_GROUP = "Sling";

    protected static final String THREAD_POOL_NAME = "SLING_SCHEDULER";

    /** Map key for the job object */
    static final String DATA_MAP_OBJECT = "QuartzJobScheduler.Object";

    /** Map key for the job name */
    static final String DATA_MAP_NAME = "QuartzJobScheduler.JobName";

    /** Map key for the concurrent run property */
    static final String DATA_MAP_RUN_CONCURRENT = "QuartzJobScheduler.RunConcurrently";

    /** Map key for the run status */
    static final String DATA_MAP_KEY_ISRUNNING = "QuartzJobExecutor.isRunning";

    /** Map key for the configuration. */
    static final String DATA_MAP_CONFIGURATION = "QuartzJobScheduler.Configuration";

    /** Map key for the logger. */
    static final String DATA_MAP_LOGGER = "QuartzJobScheduler.Logger";

    protected org.quartz.Scheduler scheduler;

    protected final List<Object[]> registeredJobs = new ArrayList<Object[]>();

    protected ComponentContext context;

    /** @scr.reference */
    protected ThreadPoolManager threadPoolManager;

    /**
     * Activate this component.
     * Start the scheduler.
     * @param ctx The component context.
     * @throws Exception
     */
    protected void activate(ComponentContext ctx) throws Exception {
        this.context = ctx;
        synchronized ( this.registeredJobs ) {
            this.init();
            for( Object[] arr : this.registeredJobs ) {
                try {
                    this.register((String)arr[0], (ServiceReference)arr[1]);
                } catch (Exception e) {
                    // we don't want that one malicious service brings down the scheduler, so we just log
                    // the exception and continue
                    this.logger.error("Exception during registering job service {}.", arr[1], e);
                }
            }
            this.registeredJobs.clear();
        }
    }

    /**
     * Deactivate this component.
     * Stop the scheduler.
     * @param ctx The component context.
     */
    protected void deactivate(ComponentContext ctx) {
        synchronized (this.registeredJobs ) {
            this.dispose();
        }
        this.context = null;
    }

    protected void init() throws SchedulerException {
        // if we don't have a thread pool manager, we use the default thread pool
        final ThreadPoolManager tpm = this.threadPoolManager;
        if ( tpm == null ) {
            final SchedulerFactory factory = new StdSchedulerFactory();
            this.scheduler = factory.getScheduler();
        } else {
            final ThreadPool pool = tpm.get(THREAD_POOL_NAME);
            final QuartzThreadPool quartzPool = new QuartzThreadPool(pool);
            final DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance();
            factory.createScheduler(quartzPool, new RAMJobStore());
            this.scheduler = factory.getScheduler();
        }
        this.scheduler.start();
        if ( this.logger.isDebugEnabled() ) {
            this.logger.debug("Scheduler started.");
        }
    }

    protected void dispose() {
        if ( this.scheduler != null ) {
            try {
                this.scheduler.shutdown();
            } catch (SchedulerException e) {
                this.logger.debug("Exception during shutdown of scheduler.", e);
            }
            if ( this.logger.isDebugEnabled() ) {
                this.logger.debug("Scheduler stopped.");
            }
            this.scheduler = null;
        }
    }

    /**
     * Add a job to the scheduler
     *
     * @param name The name of the job to add (or null)
     * @param Tje jopb
     * @param trigger a Trigger
     * @param canRunConcurrently whether this job can be run concurrently
     *
     * @throws Exception thrown in case of errors
     */
    protected void scheduleJob(String name,
                               final Object job,
                               final Map<String, Serializable>    config,
                               final Trigger trigger,
                               final boolean canRunConcurrently)
    throws Exception {
        // check if the supplied object is valid
        this.checkJob(job);

        // if there is already a job with the name, remove it first
        if ( name != null ) {
            try {
                final JobDetail jobdetail = this.scheduler.getJobDetail(name, DEFAULT_QUARTZ_JOB_GROUP);
                if (jobdetail != null) {
                    this.removeJob(name);
                }
            } catch (final SchedulerException ignored) {
            }
        } else {
            name = "Sling Quartz Scheduler " + UUID.randomUUID().toString();
        }

        // create the data map
        final JobDataMap jobDataMap = this.initDataMap(name, job, config, canRunConcurrently);

        final JobDetail detail = this.createJobDetail(name, jobDataMap);

        this.logger.debug("Scheduling job {} with name {} and trigger {}", new Object[] {job, name, trigger});
        this.scheduler.scheduleJob(detail, trigger);
    }

    /**
     * Initialize the data map for the job executor.
     * @param jobName
     * @param job
     * @param config
     * @param concurent
     * @return
     */
    protected JobDataMap initDataMap(String  jobName,
                                     Object  job,
                                     Map<String, Serializable> config,
                                     boolean concurent) {
        final JobDataMap jobDataMap = new JobDataMap();

        jobDataMap.put(DATA_MAP_OBJECT, job);

        jobDataMap.put(DATA_MAP_NAME, jobName);
        jobDataMap.put(DATA_MAP_RUN_CONCURRENT, (concurent? Boolean.TRUE: Boolean.FALSE));
        jobDataMap.put(DATA_MAP_LOGGER, this.logger);
        if ( config != null ) {
            jobDataMap.put(DATA_MAP_CONFIGURATION, config);
        }

        return jobDataMap;
    }

    /**
     * Create the job detail.
     * @param name
     * @param jobDataMap
     * @return
     */
    protected JobDetail createJobDetail(String name, JobDataMap jobDataMap) {
        final JobDetail detail = new JobDetail(name, DEFAULT_QUARTZ_JOB_GROUP, QuartzJobExecutor.class);
        detail.setJobDataMap(jobDataMap);
        return detail;
    }

    /**
     * Check the job object, either runnable or job is allowed
     */
    protected void checkJob(Object job)
    throws Exception {
        if (!(job instanceof Runnable) && !(job instanceof Job)) {
            throw new Exception("Job object is neither an instance of " + Runnable.class.getName() + " nor " + Job.class.getName());
        }
    }

    /**
     * @see org.apache.sling.commons.scheduler.Scheduler#addJob(java.lang.String, java.lang.Object, java.util.Map, java.lang.String, boolean)
     */
    public void addJob(String name,
                       Object job,
                       Map<String, Serializable>    config,
                       String schedulingExpression,
                       boolean canRunConcurrently)
    throws Exception {
        final CronTrigger cronJobEntry = new CronTrigger(name, DEFAULT_QUARTZ_JOB_GROUP);

        try {
            cronJobEntry.setCronExpression(schedulingExpression);
        } catch (final ParseException pe) {
            throw new Exception(pe.getMessage(), pe);
        }
        this.scheduleJob(name, job, config, cronJobEntry, canRunConcurrently);
    }

    /**
     * @see org.apache.sling.commons.scheduler.Scheduler#addPeriodicJob(java.lang.String, java.lang.Object, java.util.Map, long, boolean)
     */
    public void addPeriodicJob(String name, Object job, Map<String, Serializable> config, long period, boolean canRunConcurrently)
    throws Exception {
        final long ms = period * 1000;
        if ( name == null ) {
            name = "Sling Quartz Scheduler " + UUID.randomUUID().toString();
        }
        final SimpleTrigger timeEntry =
            new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP, new Date(System.currentTimeMillis() + ms), null,
                              SimpleTrigger.REPEAT_INDEFINITELY, ms);

        this.scheduleJob(name, job, config, timeEntry, canRunConcurrently);
    }

    /**
     * @see org.apache.sling.commons.scheduler.Scheduler#fireJob(java.lang.Object, java.util.Map)
     */
    public void fireJob(Object job, Map<String, Serializable> config)
    throws Exception {
        this.checkJob(job);
        final String name = job.getClass().getName();
        final JobDataMap dataMap = this.initDataMap(name, job, config, true);

        final JobDetail detail = this.createJobDetail(name, dataMap);

        final Trigger trigger = new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP);
        this.scheduler.scheduleJob(detail, trigger);
    }

    /**
     * @see org.apache.sling.commons.scheduler.Scheduler#fireJobAt(java.lang.String, java.lang.Object, java.util.Map, java.util.Date)
     */
    public void fireJobAt(String name, Object job, Map<String, Serializable> config, Date date) throws Exception {
        if ( name == null ) {
            name = "Sling Quartz Scheduler " + UUID.randomUUID().toString();
        }
        final SimpleTrigger trigger = new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP, date);
        this.scheduleJob(name, job, config, trigger, true);
    }

    /**
     * @see org.apache.sling.commons.scheduler.Scheduler#removeJob(java.lang.String)
     */
    public void removeJob(String name) throws NoSuchElementException {
        try {
            this.scheduler.deleteJob(name, DEFAULT_QUARTZ_JOB_GROUP);
        } catch (final SchedulerException se) {
            throw new NoSuchElementException(se.getMessage());
        }
    }

    protected void register(String type, ServiceReference ref)
    throws Exception {
        final Object job = this.context.locateService(type, ref);
        if ( ref != null ) {
            this.checkJob(job);
            String name = (String)ref.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME);
            if ( name == null ) {
                name = (String)ref.getProperty(Constants.SERVICE_PID);
            }
            if ( name != null ) {
                final Boolean concurrent = (Boolean)ref.getProperty(Scheduler.PROPERTY_SCHEDULER_CONCURRENT);
                final String expression = (String)ref.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION);
                if ( expression != null ) {
                    this.addJob(name, job, null, expression, (concurrent != null ? concurrent : true));
                } else {
                    final Long period = (Long)ref.getProperty(Scheduler.PROPERTY_SCHEDULER_PERIOD);
                    if ( period != null ) {
                        this.addPeriodicJob(name, job, null, period, (concurrent != null ? concurrent : true));
                    }
                }
            } else {
                throw new Exception("Job service must either have a PID or a configured property 'scheduler.name'.");
            }
        }
    }

    protected void unregister(ServiceReference ref) {
        String name = (String)ref.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME);
        if ( name == null ) {
            name = (String)ref.getProperty(Constants.SERVICE_PID);
        }
        if ( name != null ) {
            this.removeJob(name);
        }
    }

    /**
     * Bind a new job.
     * @param ref
     * @throws Exception
     */
    protected void bindJob(ServiceReference ref)
    throws Exception {
        synchronized ( this.registeredJobs ) {
            if ( this.scheduler != null ) {
                this.register("job", ref);
            } else {
                this.registeredJobs.add(new Object[] {"job", ref});
            }
        }
    }

    /**
     * Unbind a job.
     * @param ref
     */
    protected void unbindJob(ServiceReference ref) {
        synchronized ( this.registeredJobs ) {
            if ( this.scheduler != null ) {
                this.unregister(ref);
            }
        }
    }

    /**
     * Bind a new job.
     * @param ref
     * @throws Exception
     */
    protected void bindTask(ServiceReference ref)
    throws Exception {
        synchronized ( this.registeredJobs ) {
            if ( this.scheduler != null ) {
                this.register("task", ref);
            } else {
                this.registeredJobs.add(new Object[] {"task", ref});
            }
        }
    }

    /**
     * Unbind a job.
     * @param ref
     */
    protected void unbindTask(ServiceReference ref) {
        synchronized ( this.registeredJobs ) {
            if ( this.scheduler != null ) {
                this.unregister(ref);
            }
        }
    }

    private static final class QuartzThreadPool implements org.quartz.spi.ThreadPool {

        /** Default log. */
        protected final Logger logger = LoggerFactory.getLogger(this.getClass());

        /** Our executor thread pool */
        private ThreadPool executor;

        /**
         * Create a new wrapper implementation for Quartz.
         */
        public QuartzThreadPool(final ThreadPool executor) {
            this.executor = executor;
        }

        /* (non-Javadoc)
         * @see org.quartz.spi.QuartzThreadPool#getPoolSize()
         */
        public int getPoolSize() {
            return this.executor.getConfiguration().getMaxPoolSize();
        }

        /* (non-Javadoc)
         * @see org.quartz.spi.QuartzThreadPool#initialize()
         */
        public void initialize() {
            // nothing to do
        }

        /* (non-Javadoc)
         * @see org.quartz.spi.QuartzThreadPool#runInThread(java.lang.Runnable)
         */
        public boolean runInThread(final Runnable job) {
            this.executor.execute(job);

            return true;
        }

        /**
         * @see org.quartz.spi.ThreadPool#blockForAvailableThreads()
         */
        public int blockForAvailableThreads() {
            return this.executor.getConfiguration().getMaxPoolSize() - this.executor.getConfiguration().getQueueSize();
        }

        /* (non-Javadoc)
         * @see org.quartz.spi.QuartzThreadPool#shutdown(boolean)
         */
        public void shutdown(final boolean waitForJobsToComplete) {
            // the pool is managed by the thread pool manager,
            // so we can just return
            this.executor = null;
        }
    }

}
TOP

Related Classes of org.apache.sling.commons.scheduler.impl.QuartzScheduler$QuartzThreadPool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.