001    /* 
002     * Copyright 2008-2009 the original author or authors.
003     * The contents of this file are subject to the Mozilla Public License
004     * Version 1.1 (the "License"); you may not use this file except in
005     * compliance with the License. You may obtain a copy of the License at
006     * http://www.mozilla.org/MPL/
007     *
008     * Software distributed under the License is distributed on an "AS IS"
009     * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
010     * License for the specific language governing rights and limitations
011     * under the License.
012     */
013     
014    package com.mtgi.analytics.aop.config.v11;
015    
016    import org.quartz.JobDetail;
017    import org.quartz.Scheduler;
018    import org.quartz.SchedulerException;
019    import org.quartz.Trigger;
020    import org.springframework.beans.MutablePropertyValues;
021    import org.springframework.beans.factory.BeanFactory;
022    import org.springframework.beans.factory.InitializingBean;
023    import org.springframework.beans.factory.config.BeanDefinition;
024    import org.springframework.beans.factory.config.BeanDefinitionHolder;
025    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
026    import org.springframework.beans.factory.xml.ParserContext;
027    import org.springframework.scheduling.SchedulingException;
028    import org.springframework.scheduling.quartz.JobDetailAwareTrigger;
029    
030    import com.mtgi.analytics.aop.config.TemplateBeanDefinitionParser;
031    
032    /**
033     * Adds a trigger to a Quartz scheduler automatically after a bean factory is initialized.
034     * Note that the source of the scheduler and the trigger does not necessarily
035     * have to be the bean factory that contains this bean.  This is intended to assist in
036     * processing of a {@link TemplateBeanDefinitionParser}, in which beans are
037     * defined in a source template factory and then promoted out into a target factory after
038     * transformation.
039     * 
040     * @see TemplateBeanDefinitionParser
041     */
042    public class SchedulerActivationPostProcessor implements InitializingBean {
043    
044            private BeanFactory sourceFactory; 
045            private String schedulerName;
046            private String triggerName;
047            
048            public void setSourceFactory(BeanFactory sourceFactory) {
049                    this.sourceFactory = sourceFactory;
050            }
051    
052            public void setSchedulerName(String schedulerName) {
053                    this.schedulerName = schedulerName;
054            }
055    
056            public void setTriggerName(String triggerName) {
057                    this.triggerName = triggerName;
058            }
059    
060            public void afterPropertiesSet() throws Exception {
061                    Scheduler scheduler = (Scheduler)sourceFactory.getBean(schedulerName, Scheduler.class);
062                    Trigger trigger = (Trigger)sourceFactory.getBean(triggerName, Trigger.class);
063                    try {
064                            if (trigger instanceof JobDetailAwareTrigger) {
065                                    JobDetail job = ((JobDetailAwareTrigger)trigger).getJobDetail();
066                                    scheduler.addJob(job, false);
067                            }
068                            scheduler.scheduleJob(trigger);
069                    } catch (SchedulerException e) {
070                            throw new SchedulingException("error scheduling trigger [" + trigger + "]", e);
071                    }
072            }
073            
074            /**
075             * Convenience method to register a {@link SchedulerActivationPostProcessor} in the given BeanFactory
076             * parse context with the given properties.
077             * @param parseContext the target bean factory in this context will have a {@link SchedulerActivationPostProcessor} registered
078             * @param sourceFactory the source for both the named scheduler and trigger instances
079             * @param schedulerName the name of the Quartz {@link Scheduler} in <code>sourceFactory</code> to use
080             * @param triggerName the name of the Quarty {@link Trigger} in <code>sourceFactory</code> that must be scheduled
081             */
082            public static void registerPostProcessor(ParserContext parseContext, BeanFactory sourceFactory, String schedulerName, String triggerName) {
083                    BeanDefinitionBuilder scheduleBootstrap = BeanDefinitionBuilder.rootBeanDefinition(SchedulerActivationPostProcessor.class);
084                    scheduleBootstrap.addPropertyValue("sourceFactory", sourceFactory);
085                    scheduleBootstrap.addPropertyValue("schedulerName", schedulerName);
086                    scheduleBootstrap.addPropertyValue("triggerName", triggerName);
087                    scheduleBootstrap.setLazyInit(false);
088                    parseContext.getReaderContext().registerWithGeneratedName(scheduleBootstrap.getBeanDefinition());
089            }
090            
091            /**
092             * Convenience method to override a CronTrigger bean definition with the given cron expression
093             * and base name.
094             */
095            public static void configureTriggerDefinition(BeanDefinition trigger, String cronExpression, String name) {
096                    MutablePropertyValues props = trigger.getPropertyValues();
097                    if (cronExpression != null) {
098                            props.removePropertyValue("cronExpression");
099                            props.addPropertyValue("cronExpression", cronExpression);
100                    }
101                    props.addPropertyValue("name", name + "_trigger");
102                    
103                    unwrapInnerBean(trigger, "jobDetail").getPropertyValues().addPropertyValue("name", name + "_job");
104            }
105    
106            public static BeanDefinition unwrapInnerBean(BeanDefinition parent, String property) {
107                    Object value = parent.getPropertyValues().getPropertyValue(property).getValue();
108                    if (value == null)
109                            return null;
110                    if (value instanceof BeanDefinition)
111                            return (BeanDefinition)value;
112                    if (value instanceof BeanDefinitionHolder)
113                            return ((BeanDefinitionHolder)value).getBeanDefinition();
114                    throw new IllegalArgumentException("Don't know how to convert " + value.getClass() + " into a BeanDefinition for property " + property);
115            }
116    
117    }