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