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.List;
021    import java.util.Map;
022    import java.util.concurrent.ConcurrentHashMap;
023    
024    import org.apache.camel.CamelContext;
025    import org.apache.camel.Endpoint;
026    import org.apache.camel.Exchange;
027    import org.apache.camel.ExchangePattern;
028    import org.apache.camel.Message;
029    import org.apache.camel.spi.Synchronization;
030    import org.apache.camel.spi.UnitOfWork;
031    import org.apache.camel.util.ExchangeHelper;
032    import org.apache.camel.util.ObjectHelper;
033    
034    /**
035     * A default implementation of {@link Exchange}
036     *
037     * @version 
038     */
039    public final class DefaultExchange implements Exchange {
040    
041        protected final CamelContext context;
042        private Map<String, Object> properties;
043        private Message in;
044        private Message out;
045        private Exception exception;
046        private String exchangeId;
047        private UnitOfWork unitOfWork;
048        private ExchangePattern pattern;
049        private Endpoint fromEndpoint;
050        private String fromRouteId;
051        private List<Synchronization> onCompletions;
052    
053        public DefaultExchange(CamelContext context) {
054            this(context, ExchangePattern.InOnly);
055        }
056    
057        public DefaultExchange(CamelContext context, ExchangePattern pattern) {
058            this.context = context;
059            this.pattern = pattern;
060        }
061    
062        public DefaultExchange(Exchange parent) {
063            this(parent.getContext(), parent.getPattern());
064            this.fromEndpoint = parent.getFromEndpoint();
065            this.fromRouteId = parent.getFromRouteId();
066            this.unitOfWork = parent.getUnitOfWork();
067        }
068    
069        public DefaultExchange(Endpoint fromEndpoint) {
070            this(fromEndpoint, ExchangePattern.InOnly);
071        }
072    
073        public DefaultExchange(Endpoint fromEndpoint, ExchangePattern pattern) {
074            this(fromEndpoint.getCamelContext(), pattern);
075            this.fromEndpoint = fromEndpoint;
076        }
077    
078        @Override
079        public String toString() {
080            return "Exchange[" + (out == null ? in : out) + "]";
081        }
082    
083        public Exchange copy() {
084            DefaultExchange exchange = new DefaultExchange(this);
085    
086            if (hasProperties()) {
087                exchange.setProperties(safeCopy(getProperties()));
088            }
089            
090            exchange.setIn(getIn().copy());
091            if (hasOut()) {
092                exchange.setOut(getOut().copy());
093            }
094            exchange.setException(getException());
095            return exchange;
096        }
097    
098        private static Map<String, Object> safeCopy(Map<String, Object> properties) {
099            if (properties == null) {
100                return null;
101            }
102            return new ConcurrentHashMap<String, Object>(properties);
103        }
104    
105        public CamelContext getContext() {
106            return context;
107        }
108    
109        public Object getProperty(String name) {
110            if (hasProperties()) {
111                // use intern String for properties which is Camel* properties
112                // this reduces memory allocations needed for those common properties
113                if (name.startsWith("Camel")) {
114                    name = name.intern();
115                }
116                return properties.get(name);
117            }
118            return null;
119        }
120    
121        public Object getProperty(String name, Object defaultValue) {
122            Object answer = getProperty(name);
123            return answer != null ? answer : defaultValue;
124        }
125    
126        @SuppressWarnings("unchecked")
127        public <T> T getProperty(String name, Class<T> type) {
128            Object value = getProperty(name);
129            if (value == null) {
130                // lets avoid NullPointerException when converting to boolean for null values
131                if (boolean.class.isAssignableFrom(type)) {
132                    return (T) Boolean.FALSE;
133                }
134                return null;
135            }
136    
137            // eager same instance type test to avoid the overhead of invoking the type converter
138            // if already same type
139            if (type.isInstance(value)) {
140                return type.cast(value);
141            }
142    
143            return ExchangeHelper.convertToType(this, type, value);
144        }
145    
146        @SuppressWarnings("unchecked")
147        public <T> T getProperty(String name, Object defaultValue, Class<T> type) {
148            Object value = getProperty(name, defaultValue);
149            if (value == null) {
150                // lets avoid NullPointerException when converting to boolean for null values
151                if (boolean.class.isAssignableFrom(type)) {
152                    return (T) Boolean.FALSE;
153                }
154                return null;
155            }
156    
157            // eager same instance type test to avoid the overhead of invoking the type converter
158            // if already same type
159            if (type.isInstance(value)) {
160                return type.cast(value);
161            }
162    
163            return ExchangeHelper.convertToType(this, type, value);
164        }
165    
166        public void setProperty(String name, Object value) {
167            // use intern String for properties which is Camel* properties
168            // this reduces memory allocations needed for those common properties
169            if (name != null && name.startsWith("Camel")) {
170                name = name.intern();
171            }
172            if (value != null) {
173                // avoid the NullPointException
174                getProperties().put(name, value);
175            } else {
176                // if the value is null, we just remove the key from the map
177                if (name != null) {
178                    getProperties().remove(name);
179                }
180            }
181        }
182    
183        public Object removeProperty(String name) {
184            if (!hasProperties()) {
185                return null;
186            }
187            return getProperties().remove(name);
188        }
189    
190        public Map<String, Object> getProperties() {
191            if (properties == null) {
192                properties = new ConcurrentHashMap<String, Object>();
193            }
194            return properties;
195        }
196    
197        public boolean hasProperties() {
198            return properties != null && !properties.isEmpty();
199        }
200    
201        public void setProperties(Map<String, Object> properties) {
202            this.properties = properties;
203        }
204    
205        public Message getIn() {
206            if (in == null) {
207                in = new DefaultMessage();
208                configureMessage(in);
209            }
210            return in;
211        }
212    
213        public <T> T getIn(Class<T> type) {
214            Message in = getIn();
215    
216            // eager same instance type test to avoid the overhead of invoking the type converter
217            // if already same type
218            if (type.isInstance(in)) {
219                return type.cast(in);
220            }
221    
222            // fallback to use type converter
223            return context.getTypeConverter().convertTo(type, this, in);
224        }
225    
226        public void setIn(Message in) {
227            this.in = in;
228            configureMessage(in);
229        }
230    
231        public Message getOut() {
232            // lazy create
233            if (out == null) {
234                out = (in != null && in instanceof MessageSupport)
235                    ? ((MessageSupport)in).newInstance() : new DefaultMessage();
236                configureMessage(out);
237            }
238            return out;
239        }
240    
241        public <T> T getOut(Class<T> type) {
242            if (!hasOut()) {
243                return null;
244            }
245    
246            Message out = getOut();
247    
248            // eager same instance type test to avoid the overhead of invoking the type converter
249            // if already same type
250            if (type.isInstance(out)) {
251                return type.cast(out);
252            }
253    
254            // fallback to use type converter
255            return context.getTypeConverter().convertTo(type, this, out);
256        }
257    
258        public boolean hasOut() {
259            return out != null;
260        }
261    
262        public void setOut(Message out) {
263            this.out = out;
264            configureMessage(out);
265        }
266    
267        public Exception getException() {
268            return exception;
269        }
270    
271        public <T> T getException(Class<T> type) {
272            return ObjectHelper.getException(type, exception);
273        }
274    
275        public void setException(Throwable t) {
276            if (t == null) {
277                this.exception = null;
278            } else if (t instanceof Exception) {
279                this.exception = (Exception) t;
280            } else {
281                // wrap throwable into an exception
282                this.exception = ObjectHelper.wrapCamelExecutionException(this, t);
283            }
284        }
285    
286        public ExchangePattern getPattern() {
287            return pattern;
288        }
289    
290        public void setPattern(ExchangePattern pattern) {
291            this.pattern = pattern;
292        }
293    
294        public Endpoint getFromEndpoint() {
295            return fromEndpoint;
296        }
297    
298        public void setFromEndpoint(Endpoint fromEndpoint) {
299            this.fromEndpoint = fromEndpoint;
300        }
301    
302        public String getFromRouteId() {
303            return fromRouteId;
304        }
305    
306        public void setFromRouteId(String fromRouteId) {
307            this.fromRouteId = fromRouteId;
308        }
309    
310        public String getExchangeId() {
311            if (exchangeId == null) {
312                exchangeId = createExchangeId();
313            }
314            return exchangeId;
315        }
316    
317        public void setExchangeId(String id) {
318            this.exchangeId = id;
319        }
320    
321        public boolean isFailed() {
322            return (hasOut() && getOut().isFault()) || getException() != null;
323        }
324    
325        public boolean isTransacted() {
326            UnitOfWork uow = getUnitOfWork();
327            if (uow != null) {
328                return uow.isTransacted();
329            } else {
330                return false;
331            }
332        }
333    
334        public Boolean isExternalRedelivered() {
335            Boolean answer = null;
336    
337            // check property first, as the implementation details to know if the message
338            // was externally redelivered is message specific, and thus the message implementation
339            // could potentially change during routing, and therefore later we may not know if the
340            // original message was externally redelivered or not, therefore we store this detail
341            // as a exchange property to keep it around for the lifecycle of the exchange
342            if (hasProperties()) {
343                answer = getProperty(Exchange.EXTERNAL_REDELIVERED, null, Boolean.class);
344            }
345            
346            if (answer == null) {
347                // lets avoid adding methods to the Message API, so we use the
348                // DefaultMessage to allow component specific messages to extend
349                // and implement the isExternalRedelivered method.
350                DefaultMessage msg = getIn(DefaultMessage.class);
351                if (msg != null) {
352                    answer = msg.isTransactedRedelivered();
353                    // store as property to keep around
354                    setProperty(Exchange.EXTERNAL_REDELIVERED, answer);
355                }
356            }
357    
358            return answer;
359        }
360    
361        public boolean isRollbackOnly() {
362            return Boolean.TRUE.equals(getProperty(Exchange.ROLLBACK_ONLY)) || Boolean.TRUE.equals(getProperty(Exchange.ROLLBACK_ONLY_LAST));
363        }
364    
365        public UnitOfWork getUnitOfWork() {
366            return unitOfWork;
367        }
368    
369        public void setUnitOfWork(UnitOfWork unitOfWork) {
370            this.unitOfWork = unitOfWork;
371            if (onCompletions != null) {
372                // now an unit of work has been assigned so add the on completions
373                // we might have registered already
374                for (Synchronization onCompletion : onCompletions) {
375                    unitOfWork.addSynchronization(onCompletion);
376                }
377                // cleanup the temporary on completion list as they now have been registered
378                // on the unit of work
379                onCompletions.clear();
380                onCompletions = null;
381            }
382        }
383    
384        public void addOnCompletion(Synchronization onCompletion) {
385            if (unitOfWork == null) {
386                // unit of work not yet registered so we store the on completion temporary
387                // until the unit of work is assigned to this exchange by the UnitOfWorkProcessor
388                if (onCompletions == null) {
389                    onCompletions = new ArrayList<Synchronization>();
390                }
391                onCompletions.add(onCompletion);
392            } else {
393                getUnitOfWork().addSynchronization(onCompletion);
394            }
395        }
396    
397        public boolean containsOnCompletion(Synchronization onCompletion) {
398            if (unitOfWork != null) {
399                // if there is an unit of work then the completions is moved there
400                return unitOfWork.containsSynchronization(onCompletion);
401            } else {
402                // check temporary completions if no unit of work yet
403                return onCompletions != null && onCompletions.contains(onCompletion);
404            }
405        }
406    
407        public void handoverCompletions(Exchange target) {
408            if (onCompletions != null) {
409                for (Synchronization onCompletion : onCompletions) {
410                    target.addOnCompletion(onCompletion);
411                }
412                // cleanup the temporary on completion list as they have been handed over
413                onCompletions.clear();
414                onCompletions = null;
415            } else if (unitOfWork != null) {
416                // let unit of work handover
417                unitOfWork.handoverSynchronization(target);
418            }
419        }
420    
421        public List<Synchronization> handoverCompletions() {
422            List<Synchronization> answer = null;
423            if (onCompletions != null) {
424                answer = new ArrayList<Synchronization>(onCompletions);
425                onCompletions.clear();
426                onCompletions = null;
427            }
428            return answer;
429        }
430    
431        /**
432         * Configures the message after it has been set on the exchange
433         */
434        protected void configureMessage(Message message) {
435            if (message instanceof MessageSupport) {
436                MessageSupport messageSupport = (MessageSupport)message;
437                messageSupport.setExchange(this);
438            }
439        }
440    
441        @SuppressWarnings("deprecation")
442        protected String createExchangeId() {
443            String answer = null;
444            if (in != null) {
445                answer = in.createExchangeId();
446            }
447            if (answer == null) {
448                answer = context.getUuidGenerator().generateUuid();
449            }
450            return answer;
451        }
452    }