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.camel.bam.rules;
018
019 import org.apache.camel.Exchange;
020 import org.apache.camel.Processor;
021 import org.apache.camel.bam.TimeExpression;
022 import org.apache.camel.bam.model.ActivityState;
023 import org.apache.camel.bam.model.ProcessInstance;
024 import org.apache.camel.builder.FromBuilder;
025 import org.apache.camel.builder.ProcessorFactory;
026 import org.apache.camel.impl.DefaultExchange;
027 import org.apache.camel.impl.ServiceSupport;
028 import static org.apache.camel.util.ServiceHelper.startServices;
029 import static org.apache.camel.util.ServiceHelper.stopServices;
030 import org.apache.camel.util.Time;
031 import org.apache.commons.logging.Log;
032 import org.apache.commons.logging.LogFactory;
033
034 import java.util.Date;
035
036 /**
037 * A temporal rule for use within BAM
038 *
039 * @version $Revision: $
040 */
041 public class TemporalRule extends ServiceSupport {
042 private static final transient Log log = LogFactory.getLog(TemporalRule.class);
043 private TimeExpression first;
044 private TimeExpression second;
045 private long expectedMillis;
046 private long overdueMillis;
047 private Processor overdueAction;
048 private ProcessorFactory overdueProcessorFactory;
049
050 public TemporalRule(TimeExpression first, TimeExpression second) {
051 this.first = first;
052 this.second = second;
053 }
054
055 public TemporalRule expectWithin(Time builder) {
056 return expectWithin(builder.toMillis());
057 }
058
059 public TemporalRule expectWithin(long millis) {
060 expectedMillis = millis;
061 return this;
062 }
063
064 public FromBuilder errorIfOver(Time builder) {
065 return errorIfOver(builder.toMillis());
066 }
067
068 public FromBuilder errorIfOver(long millis) {
069 overdueMillis = millis;
070
071 FromBuilder builder = new FromBuilder(second.getBuilder().getProcessBuilder(), null);
072 overdueProcessorFactory = builder;
073 return builder;
074 }
075
076 public TimeExpression getFirst() {
077 return first;
078 }
079
080 public TimeExpression getSecond() {
081 return second;
082 }
083
084 public Processor getOverdueAction() throws Exception {
085 if (overdueAction == null && overdueProcessorFactory != null) {
086 overdueAction = overdueProcessorFactory.createProcessor();
087 }
088 return overdueAction;
089 }
090
091 public void processExchange(Exchange exchange, ProcessInstance instance) {
092 Date firstTime = first.evaluate(instance);
093 if (firstTime == null) {
094 // ignore as first event has not accurred yet
095 return;
096 }
097
098 // TODO now we might need to set the second activity state
099 // to 'grey' to indicate it now could happen?
100
101 // lets force the lazy creation of the second state
102 ActivityState secondState = second.getOrCreateActivityState(instance);
103 if (expectedMillis > 0L) {
104 Date expected = secondState.getTimeExpected();
105 if (expected == null) {
106 expected = add(firstTime, expectedMillis);
107 secondState.setTimeExpected(expected);
108 }
109 }
110 if (overdueMillis > 0L) {
111 Date overdue = secondState.getTimeOverdue();
112 if (overdue == null) {
113 overdue = add(firstTime, overdueMillis);
114 secondState.setTimeOverdue(overdue);
115 }
116 }
117 }
118
119 public void processExpired(ActivityState activityState) throws Exception {
120 Processor processor = getOverdueAction();
121 if (processor != null) {
122 Date now = new Date();
123 /*
124 TODO this doesn't work and returns null for some strange reason
125 ProcessInstance instance = activityState.getProcessInstance();
126 ActivityState secondState = second.getActivityState(instance);
127 if (secondState == null) {
128 log.error("Could not find the second state! Process is: " + instance + " with first state: " + first.getActivityState(instance) + " and the state I was called with was: " + activityState);
129 }
130 */
131
132 ActivityState secondState = activityState;
133 Date overdue = secondState.getTimeOverdue();
134 if (now.compareTo(overdue) >= 0) {
135 Exchange exchange = createExchange();
136 exchange.getIn().setBody(activityState);
137 processor.process(exchange);
138 }
139 else {
140 log.warn("Process has not actually expired; the time is: " + now + " but the overdue time is: " + overdue);
141 }
142 }
143 }
144
145 protected Exchange createExchange() {
146 return new DefaultExchange(second.getBuilder().getProcessBuilder().getContext());
147 }
148
149 /**
150 * Returns the date in the future adding the given number of millis
151 *
152 * @param date
153 * @param millis
154 * @return the date in the future
155 */
156 protected Date add(Date date, long millis) {
157 return new Date(date.getTime() + millis);
158 }
159
160 protected void doStart() throws Exception {
161 startServices(getOverdueAction());
162 }
163
164 protected void doStop() throws Exception {
165 stopServices(getOverdueAction());
166 }
167 }