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 }