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.impl;
018
019 import java.util.ArrayList;
020 import java.util.LinkedHashSet;
021 import java.util.List;
022 import java.util.Set;
023 import java.util.Stack;
024
025 import org.apache.camel.Exchange;
026 import org.apache.camel.Message;
027 import org.apache.camel.Service;
028 import org.apache.camel.spi.RouteContext;
029 import org.apache.camel.spi.Synchronization;
030 import org.apache.camel.spi.TracedRouteNodes;
031 import org.apache.camel.spi.UnitOfWork;
032 import org.apache.camel.util.EventHelper;
033 import org.apache.camel.util.UuidGenerator;
034 import org.apache.commons.logging.Log;
035 import org.apache.commons.logging.LogFactory;
036
037 /**
038 * The default implementation of {@link org.apache.camel.spi.UnitOfWork}
039 *
040 * @version $Revision: 899628 $
041 */
042 public class DefaultUnitOfWork implements UnitOfWork, Service {
043 private static final transient Log LOG = LogFactory.getLog(DefaultUnitOfWork.class);
044
045 private String id;
046 private List<Synchronization> synchronizations;
047 private Message originalInMessage;
048 private final TracedRouteNodes tracedRouteNodes;
049 private Set<Object> transactedBy;
050 private final Stack<RouteContext> routeContextStack = new Stack<RouteContext>();
051
052 public DefaultUnitOfWork(Exchange exchange) {
053 tracedRouteNodes = new DefaultTracedRouteNodes();
054
055 // TODO: optimize to only copy original message if enabled to do so in the route
056 // special for JmsMessage as it can cause it to loose headers later.
057 // This will be resolved when we get the message facade with copy on write implemented
058 if (exchange.getIn().getClass().getSimpleName().equals("JmsMessage")) {
059 this.originalInMessage = new DefaultMessage();
060 this.originalInMessage.setBody(exchange.getIn().getBody());
061 // cannot copy headers with a JmsMessage as the underlying javax.jms.Message object goes nuts
062 } else {
063 this.originalInMessage = exchange.getIn().copy();
064 }
065
066 // fire event
067 EventHelper.notifyExchangeCreated(exchange.getContext(), exchange);
068
069 // register to inflight registry
070 if (exchange.getContext() != null) {
071 exchange.getContext().getInflightRepository().add(exchange);
072 }
073 }
074
075 public void start() throws Exception {
076 id = null;
077 }
078
079 public void stop() throws Exception {
080 // need to clean up when we are stopping to not leak memory
081 if (synchronizations != null) {
082 synchronizations.clear();
083 }
084 if (tracedRouteNodes != null) {
085 tracedRouteNodes.clear();
086 }
087 if (transactedBy != null) {
088 transactedBy.clear();
089 }
090 originalInMessage = null;
091
092 if (!routeContextStack.isEmpty()) {
093 routeContextStack.clear();
094 }
095 }
096
097 public synchronized void addSynchronization(Synchronization synchronization) {
098 if (synchronizations == null) {
099 synchronizations = new ArrayList<Synchronization>();
100 }
101 synchronizations.add(synchronization);
102 }
103
104 public synchronized void removeSynchronization(Synchronization synchronization) {
105 if (synchronizations != null) {
106 synchronizations.remove(synchronization);
107 }
108 }
109
110 public void handoverSynchronization(Exchange target) {
111 if (synchronizations == null || synchronizations.isEmpty()) {
112 return;
113 }
114
115 for (Synchronization synchronization : synchronizations) {
116 target.addOnCompletion(synchronization);
117 }
118
119 // clear this list as its handed over to the other exchange
120 this.synchronizations.clear();
121 }
122
123 public void done(Exchange exchange) {
124 boolean failed = exchange.isFailed();
125
126 // fire event to signal the exchange is done
127 try {
128 if (failed) {
129 EventHelper.notifyExchangeFailed(exchange.getContext(), exchange);
130 } else {
131 EventHelper.notifyExchangeDone(exchange.getContext(), exchange);
132 }
133 } catch (Exception e) {
134 // must catch exceptions to ensure synchronizations is also invoked
135 LOG.warn("Exception occurred during event notification. This exception will be ignored.", e);
136 }
137
138 if (synchronizations != null && !synchronizations.isEmpty()) {
139 // invoke synchronization callbacks
140 for (Synchronization synchronization : synchronizations) {
141 try {
142 if (failed) {
143 synchronization.onFailure(exchange);
144 } else {
145 synchronization.onComplete(exchange);
146 }
147 } catch (Exception e) {
148 // must catch exceptions to ensure all synchronizations have a chance to run
149 LOG.warn("Exception occurred during onCompletion. This exception will be ignored.", e);
150 }
151 }
152 }
153
154 // unregister from inflight registry
155 if (exchange.getContext() != null) {
156 exchange.getContext().getInflightRepository().remove(exchange);
157 }
158 }
159
160 public String getId() {
161 if (id == null) {
162 id = UuidGenerator.get().generateUuid();
163 }
164 return id;
165 }
166
167 public Message getOriginalInMessage() {
168 return originalInMessage;
169 }
170
171 public TracedRouteNodes getTracedRouteNodes() {
172 return tracedRouteNodes;
173 }
174
175 public boolean isTransactedBy(Object transactionDefinition) {
176 return getTransactedBy().contains(transactionDefinition);
177 }
178
179 public void beginTransactedBy(Object transactionDefinition) {
180 getTransactedBy().add(transactionDefinition);
181 }
182
183 public void endTransactedBy(Object transactionDefinition) {
184 getTransactedBy().remove(transactionDefinition);
185 }
186
187 public RouteContext getRouteContext() {
188 if (routeContextStack.isEmpty()) {
189 return null;
190 }
191 return routeContextStack.peek();
192 }
193
194 public void pushRouteContext(RouteContext routeContext) {
195 routeContextStack.add(routeContext);
196 }
197
198 public RouteContext popRouteContext() {
199 if (routeContextStack.isEmpty()) {
200 return null;
201 }
202 return routeContextStack.pop();
203 }
204
205 private Set<Object> getTransactedBy() {
206 if (transactedBy == null) {
207 transactedBy = new LinkedHashSet<Object>();
208 }
209 return transactedBy;
210 }
211 }