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    import org.apache.camel.util.UuidGenerator;
034    
035    /**
036     * A default implementation of {@link Exchange}
037     *
038     * @version $Revision: 893110 $
039     */
040    public final class DefaultExchange implements Exchange {
041    
042        protected final CamelContext context;
043        private Map<String, Object> properties;
044        private Message in;
045        private Message out;
046        private Exception exception;
047        private String exchangeId;
048        private UnitOfWork unitOfWork;
049        private ExchangePattern pattern;
050        private Endpoint fromEndpoint;
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.unitOfWork = parent.getUnitOfWork();
066        }
067    
068        public DefaultExchange(Endpoint fromEndpoint) {
069            this(fromEndpoint, ExchangePattern.InOnly);
070        }
071        
072        public DefaultExchange(Endpoint fromEndpoint, ExchangePattern pattern) {
073            this(fromEndpoint.getCamelContext(), pattern);
074            this.fromEndpoint = fromEndpoint;
075        }
076    
077        @Override
078        public String toString() {
079            return "Exchange[" + in + "]";
080        }
081    
082        public Exchange copy() {
083            DefaultExchange exchange = new DefaultExchange(this);
084    
085            exchange.setProperties(safeCopy(getProperties()));
086            safeCopy(exchange.getIn(), getIn());
087            if (hasOut()) {
088                safeCopy(exchange.getOut(), getOut());
089            }
090            exchange.setException(getException());
091            return exchange;
092        }
093    
094        private static void safeCopy(Message message, Message that) {
095            if (message != null) {
096                message.copyFrom(that);
097            }
098        }
099    
100        private static Map<String, Object> safeCopy(Map<String, Object> properties) {
101            if (properties == null) {
102                return null;
103            }
104            return new ConcurrentHashMap<String, Object>(properties);
105        }
106    
107        public CamelContext getContext() {
108            return context;
109        }
110    
111        public Object getProperty(String name) {
112            if (properties != null) {
113                return properties.get(name);
114            }
115            return null;
116        }
117    
118        public Object getProperty(String name, Object defaultValue) {
119            Object answer = null;
120            if (properties != null) {
121                answer = properties.get(name);
122            }
123            return answer != null ? answer : defaultValue;
124        }
125    
126        public <T> T getProperty(String name, Class<T> type) {
127            Object value = getProperty(name);
128    
129            // eager same instance type test to avoid the overhead of invoking the type converter
130            // if already same type
131            if (type.isInstance(value)) {
132                return type.cast(value);
133            }
134    
135            return ExchangeHelper.convertToType(this, type, value);
136        }
137    
138        public <T> T getProperty(String name, Object defaultValue, Class<T> type) {
139            Object value = getProperty(name, defaultValue);
140    
141            // eager same instance type test to avoid the overhead of invoking the type converter
142            // if already same type
143            if (type.isInstance(value)) {
144                return type.cast(value);
145            }
146    
147            return ExchangeHelper.convertToType(this, type, value);
148        }
149    
150        public void setProperty(String name, Object value) {
151            if (value != null) {
152                // avoid the NullPointException
153                getProperties().put(name, value);
154            } else {
155                // if the value is null, we just remove the key from the map
156                if (name != null) {
157                    getProperties().remove(name);
158                }
159            }
160        }
161    
162        public Object removeProperty(String name) {
163            return getProperties().remove(name);
164        }
165    
166        public Map<String, Object> getProperties() {
167            if (properties == null) {
168                properties = new ConcurrentHashMap<String, Object>();
169            }
170            return properties;
171        }
172    
173        public boolean hasProperties() {
174            return properties != null && !properties.isEmpty();
175        }
176    
177        public void setProperties(Map<String, Object> properties) {
178            this.properties = properties;
179        }
180    
181        public Message getIn() {
182            if (in == null) {
183                in = new DefaultMessage();
184                configureMessage(in);
185            }
186            return in;
187        }
188    
189        public <T> T getIn(Class<T> type) {
190            Message in = getIn();
191    
192            // eager same instance type test to avoid the overhead of invoking the type converter
193            // if already same type
194            if (type.isInstance(in)) {
195                return type.cast(in);
196            }
197    
198            // fallback to use type converter
199            return context.getTypeConverter().convertTo(type, in);
200        }
201    
202        public void setIn(Message in) {
203            this.in = in;
204            configureMessage(in);
205        }
206    
207        public Message getOut() {
208            // lazy create
209            if (out == null) {
210                out = (in != null && in instanceof MessageSupport)
211                    ? ((MessageSupport)in).newInstance() : new DefaultMessage();
212                configureMessage(out);
213            }
214            return out;
215        }
216    
217        public <T> T getOut(Class<T> type) {
218            if (!hasOut()) {
219                return null;
220            }
221    
222            Message out = getOut();
223    
224            // eager same instance type test to avoid the overhead of invoking the type converter
225            // if already same type
226            if (type.isInstance(out)) {
227                return type.cast(out);
228            }
229    
230            // fallback to use type converter
231            return context.getTypeConverter().convertTo(type, out);
232        }
233    
234        public boolean hasOut() {
235            return out != null;
236        }
237    
238        public void setOut(Message out) {
239            this.out = out;
240            configureMessage(out);
241        }
242    
243        public Exception getException() {
244            return exception;
245        }
246    
247        public <T> T getException(Class<T> type) {
248            return ObjectHelper.getException(type, exception);
249        }
250    
251        public void setException(Exception exception) {
252            this.exception = exception;
253        }
254    
255        public ExchangePattern getPattern() {
256            return pattern;
257        }
258    
259        public void setPattern(ExchangePattern pattern) {
260            this.pattern = pattern;
261        }
262    
263        public Endpoint getFromEndpoint() {
264            return fromEndpoint;
265        }
266    
267        public void setFromEndpoint(Endpoint fromEndpoint) {
268            this.fromEndpoint = fromEndpoint;
269        }
270    
271        public String getExchangeId() {
272            if (exchangeId == null) {
273                exchangeId = createExchangeId();
274            }
275            return exchangeId;
276        }
277    
278        public void setExchangeId(String id) {
279            this.exchangeId = id;
280        }
281    
282        public boolean isFailed() {
283            return (hasOut() && getOut().isFault()) || getException() != null;
284        }
285    
286        public boolean isTransacted() {
287            return Boolean.TRUE.equals(getProperty(Exchange.TRANSACTED));
288        }
289    
290        public boolean isRollbackOnly() {
291            return Boolean.TRUE.equals(getProperty(Exchange.ROLLBACK_ONLY)) || Boolean.TRUE.equals(getProperty(Exchange.ROLLBACK_ONLY_LAST));
292        }
293    
294        public UnitOfWork getUnitOfWork() {
295            return unitOfWork;
296        }
297    
298        public void setUnitOfWork(UnitOfWork unitOfWork) {
299            this.unitOfWork = unitOfWork;
300            if (this.onCompletions != null) {
301                // now an unit of work has been assigned so add the on completions
302                // we might have registered already
303                for (Synchronization onCompletion : this.onCompletions) {
304                    this.unitOfWork.addSynchronization(onCompletion);
305                }
306                // cleanup the temporary on completion list as they now have been registered
307                // on the unit of work
308                this.onCompletions.clear();
309                this.onCompletions = null;
310            }
311        }
312    
313        public void addOnCompletion(Synchronization onCompletion) {
314            if (this.unitOfWork == null) {
315                // unit of work not yet registered so we store the on completion temporary
316                // until the unit of work is assigned to this exchange by the UnitOfWorkProcessor
317                if (this.onCompletions == null) {
318                    this.onCompletions = new ArrayList<Synchronization>();
319                }
320                this.onCompletions.add(onCompletion);
321            } else {
322                this.getUnitOfWork().addSynchronization(onCompletion);
323            }
324        }
325    
326        /**
327         * Configures the message after it has been set on the exchange
328         */
329        protected void configureMessage(Message message) {
330            if (message instanceof MessageSupport) {
331                MessageSupport messageSupport = (MessageSupport)message;
332                messageSupport.setExchange(this);
333            }
334        }
335    
336        protected String createExchangeId() {
337            String answer = null;
338            if (in != null) {
339                answer = in.createExchangeId();
340            }
341            if (answer == null) {
342                answer = UuidGenerator.get().generateUuid();
343            }
344            return answer;
345        }
346    }