001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.quartz;
018    
019    import java.util.ArrayList;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import javax.jbi.management.DeploymentException;
025    import javax.jbi.messaging.ExchangeStatus;
026    import javax.jbi.messaging.InOnly;
027    import javax.jbi.messaging.MessageExchange;
028    import javax.jbi.messaging.MessagingException;
029    import javax.jbi.messaging.NormalizedMessage;
030    
031    import org.apache.servicemix.common.EndpointSupport;
032    import org.apache.servicemix.common.endpoints.ConsumerEndpoint;
033    import org.apache.servicemix.quartz.support.DefaultQuartzMarshaler;
034    import org.apache.servicemix.quartz.support.JobDetailBean;
035    import org.apache.servicemix.quartz.support.QuartzMarshaler;
036    import org.apache.servicemix.quartz.support.ServiceMixJob;
037    import org.quartz.Calendar;
038    import org.quartz.JobDetail;
039    import org.quartz.JobExecutionContext;
040    import org.quartz.JobExecutionException;
041    import org.quartz.ObjectAlreadyExistsException;
042    import org.quartz.Scheduler;
043    import org.quartz.Trigger;
044    import org.springframework.scheduling.quartz.JobDetailAwareTrigger;
045    
046    /**
047     * @org.apache.xbean.XBean element="endpoint"
048     */
049    public class QuartzEndpoint extends ConsumerEndpoint {
050    
051        private Trigger trigger;
052        private List<Trigger> triggers;
053        private Map<String, Calendar> calendars;
054        private JobDetail jobDetail;
055        private QuartzMarshaler marshaler = new DefaultQuartzMarshaler();
056        
057        /**
058         * @return the triggers
059         */
060        public List<Trigger> getTriggers() {
061            return triggers;
062        }
063    
064        /**
065         * A list of of {@link org.quartz.Trigger} instances to allow configuring multiple schedules for the same endpoint.
066         *
067         * @param triggers the triggers to set
068         */
069        public void setTriggers(List<Trigger> triggers) {
070            this.triggers = triggers;
071        }
072    
073        /**
074         * @return the trigger
075         */
076        public Trigger getTrigger() {
077            return trigger;
078        }
079    
080        /**
081         * A single {@link org.quartz.Trigger} instance to define the trigger schedule.
082         *
083         * @param trigger the trigger to set
084         */
085        public void setTrigger(Trigger trigger) {
086            this.trigger = trigger;
087        }
088    
089        /**
090         * @return the calendar
091         */
092        public Map<String, Calendar> getCalendars() {
093            return calendars;
094        }
095    
096        /**
097         * A map with {@link org.quartz.Calendar} instances to define the trigger schedule.
098         *
099         * @param calendar the calendar to set
100         */
101        public void setCalendars(Map<String, Calendar> calendars) {
102            this.calendars = calendars;
103        }
104    
105        /**
106         * @return the job
107         */
108        public JobDetail getJobDetail() {
109            return jobDetail;
110        }
111    
112        /**
113         * Set a custom JobDetail bean to be used in the triggered events.
114         *
115         * @param job the job to set
116         */
117        public void setJobDetail(JobDetail job) {
118            this.jobDetail = job;
119        }
120    
121        public QuartzMarshaler getMarshaler() {
122            return marshaler;
123        }
124    
125        /**
126         * Set a custom marshaler class to translate the JobDetail information into a normalized message.
127         *
128         * @param marshaler
129         */
130        public void setMarshaler(QuartzMarshaler marshaler) {
131            this.marshaler = marshaler;
132        }
133        
134        @Override
135        public String getLocationURI() {
136            return null;
137        }
138    
139        public void process(MessageExchange exchange) throws Exception {
140            if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
141                throw new IllegalStateException("Unexpected ACTIVE exchange: " + exchange);
142            }
143        }
144    
145        public void onJobExecute(JobExecutionContext context) throws JobExecutionException {
146            if (logger.isDebugEnabled()) {
147                logger.debug("Firing Quartz Job with context: " + context);
148            }
149            try {
150                InOnly exchange = getExchangeFactory().createInOnlyExchange();
151                NormalizedMessage message = exchange.createMessage();
152                getMarshaler().populateNormalizedMessage(message, context);
153                exchange.setInMessage(message);
154                configureExchangeTarget(exchange);
155                send(exchange);
156            } catch (MessagingException e) {
157                throw new JobExecutionException(e);
158            }
159        }
160        
161        public void validate() throws DeploymentException {
162            super.validate();
163            if (trigger instanceof JobDetailAwareTrigger) {
164                JobDetail jb = ((JobDetailAwareTrigger) trigger).getJobDetail();
165                if (jobDetail != null && jb != null && jobDetail != jb) {
166                    throw new DeploymentException("trigger and jobDetail can not be set on endpoint at the same time");
167                }
168                jobDetail = jb;
169            }
170            if (jobDetail == null) {
171                JobDetailBean j = new JobDetailBean();
172                j.setName(EndpointSupport.getKey(this));
173                jobDetail = j;
174            }
175            if (triggers == null) {
176                triggers = new ArrayList<Trigger>();
177            }
178            if (trigger != null && triggers != null && triggers.size() > 0) {
179                if (triggers.size() != 1 || triggers.get(0) != trigger) {
180                    throw new DeploymentException("trigger and triggers can not be set at the same time");
181                }
182            }
183            if (trigger != null && !triggers.contains(trigger)) {
184                triggers.add(trigger);
185            }
186            if (calendars == null) {
187                calendars = new HashMap<String, Calendar>();
188            }
189            for (Trigger t : triggers) {
190                if (t.getCalendarName() != null && calendars.get(t.getCalendarName()) == null) {
191                    throw new DeploymentException("Trigger references an unknown calendar " + t.getCalendarName());
192                }
193                t.setJobName(jobDetail.getName());
194                t.setJobGroup(jobDetail.getGroup());
195                t.setName(jobDetail.getName() + " - " + t.getName());
196            }
197        }
198        
199        public void start() throws Exception {
200            QuartzComponent component = (QuartzComponent) getServiceUnit().getComponent(); 
201            Scheduler scheduler = component.getScheduler();
202            jobDetail.getJobDataMap().put(ServiceMixJob.COMPONENT_NAME, component.getComponentName());
203            jobDetail.getJobDataMap().put(ServiceMixJob.ENDPOINT_NAME, EndpointSupport.getKey(this));
204            for (Map.Entry<String, Calendar> e : getCalendars().entrySet()) {
205                scheduler.addCalendar(e.getKey(), e.getValue(), true, true);
206            }
207            scheduler.addJob(getJobDetail(), true);
208            for (Trigger trg : getTriggers()) {
209                boolean triggerExists = scheduler.getTrigger(trg.getName(), trg.getGroup()) != null;
210                if (!triggerExists) {
211                    try {
212                        scheduler.scheduleJob(trg);
213                    } catch (ObjectAlreadyExistsException ex) {
214                        scheduler.rescheduleJob(trg.getName(), trg.getGroup(), trg);
215                    }
216                } else {
217                    scheduler.rescheduleJob(trg.getName(), trg.getGroup(), trg);
218                }
219            }
220            super.start();
221        }
222        
223        public void stop() throws Exception {
224            super.stop();
225            Scheduler scheduler = ((QuartzComponent) getServiceUnit().getComponent()).getScheduler();
226            for (Trigger trg : getTriggers()) {
227                scheduler.unscheduleJob(trg.getName(), trg.getGroup());
228            }
229            scheduler.deleteJob(getJobDetail().getName(), getJobDetail().getGroup());
230            for (Map.Entry<String, Calendar> e : getCalendars().entrySet()) {
231                scheduler.deleteCalendar(e.getKey());
232            }
233        }
234    
235    }