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.Map;
020    import java.util.concurrent.ConcurrentHashMap;
021    
022    import org.apache.camel.CamelContext;
023    import org.apache.camel.Exchange;
024    import org.apache.camel.ExchangePattern;
025    import org.apache.camel.ExchangeProperty;
026    import org.apache.camel.Message;
027    import org.apache.camel.RuntimeCamelException;
028    import org.apache.camel.spi.UnitOfWork;
029    import org.apache.camel.util.UuidGenerator;
030    import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
031    
032    /**
033     * A default implementation of {@link Exchange}
034     *
035     * @version $Revision: 53289 $
036     */
037    public class DefaultExchange implements Exchange {
038    
039        private static final UuidGenerator DEFAULT_ID_GENERATOR = new UuidGenerator();
040        protected final CamelContext context;
041        private Map<String, Object> properties;
042        private Message in;
043        private Message out;
044        private Message fault;
045        private Throwable exception;
046        private String exchangeId;
047        private UnitOfWork unitOfWork;
048        private ExchangePattern pattern;
049    
050        public DefaultExchange(CamelContext context) {
051            this(context, ExchangePattern.InOnly);
052        }
053    
054        public DefaultExchange(CamelContext context, ExchangePattern pattern) {
055            this.context = context;
056            this.pattern = pattern;
057        }
058    
059        public DefaultExchange(DefaultExchange parent) {
060            this(parent.getContext(), parent.getPattern());
061            this.unitOfWork = parent.getUnitOfWork();
062        }
063    
064        @Override
065        public String toString() {
066            return "Exchange[" + in + "]";
067        }
068    
069        public Exchange copy() {
070            Exchange exchange = newInstance();
071            exchange.copyFrom(this);
072            return exchange;
073        }
074    
075        public void copyFrom(Exchange exchange) {
076            if (exchange == this) {
077                return;
078            }
079            setProperties(safeCopy(exchange.getProperties()));
080    
081            // this can cause strangeness if we copy, say, a FileMessage onto an FtpExchange with overloaded getExchange() methods etc.
082            safeCopy(getIn(), exchange, exchange.getIn());
083            Message copyOut = exchange.getOut(false);
084            if (copyOut != null) {
085                safeCopy(getOut(true), exchange, copyOut);
086            }
087            Message copyFault = exchange.getFault(false);
088            if (copyFault != null) {
089                safeCopy(getFault(true), exchange, copyFault);
090            }
091            setException(exchange.getException());
092    
093            unitOfWork = exchange.getUnitOfWork();
094            pattern = exchange.getPattern();
095        }
096    
097        private static void safeCopy(Message message, Exchange exchange, Message that) {
098            if (message != null) {
099                message.copyFrom(that);
100            }
101        }
102    
103        private static Map<String, Object> safeCopy(Map<String, Object> properties) {
104            if (properties == null) {
105                return null;
106            }
107            return new ConcurrentHashMap<String, Object>(properties);
108        }
109    
110        private static Message safeCopy(Exchange exchange, Message message) {
111            if (message == null) {
112                return null;
113            }
114            Message answer = message.copy();
115            if (answer instanceof MessageSupport) {
116                MessageSupport messageSupport = (MessageSupport) answer;
117                messageSupport.setExchange(exchange);
118            }
119            return answer;
120        }
121    
122        public Exchange newInstance() {
123            return new DefaultExchange(this);
124        }
125    
126        public CamelContext getContext() {
127            return context;
128        }
129    
130        public Object getProperty(String name) {
131            if (properties != null) {
132                return properties.get(name);
133            }
134            return null;
135        }
136    
137        public <T> T getProperty(String name, Class<T> type) {
138            Object value = getProperty(name);
139    
140            // if the property is also a well known property in ExchangeProperty then validate that the
141            // value is of the same type
142            ExchangeProperty<?> property = ExchangeProperty.getByName(name);
143            if (property != null) {
144                validateExchangePropertyIsExpectedType(property, type, value);
145            }
146    
147            return getContext().getTypeConverter().convertTo(type, this, value);
148        }
149    
150        public void setProperty(String name, Object value) {
151            ExchangeProperty<?> property = ExchangeProperty.getByName(name);
152    
153            // if the property is also a well known property in ExchangeProperty then validate that the
154            // value is of the same type
155            if (property != null) {
156                Class type = value.getClass();
157                validateExchangePropertyIsExpectedType(property, type, value);
158            }
159            if (value != null) {
160                // avoid the NullPointException
161                getProperties().put(name, value);
162            } else {
163                // if the value is null , we just remove the key from the map
164                if (name != null) {
165                    getProperties().remove(name);
166                }
167            }
168        }
169    
170        private <T> void validateExchangePropertyIsExpectedType(ExchangeProperty<?> property, Class<T> type, Object value) {
171            if (value != null && property != null && !property.type().isAssignableFrom(type)) {
172                throw new RuntimeCamelException("Type cast exception while getting an "
173                        + "Exchange Property value '" + value.toString()
174                        + "' on Exchange " + this
175                        + " for a well known Exchange Property with these traits: " + property);
176            }
177        }
178    
179        public Object removeProperty(String name) {
180            return getProperties().remove(name);
181        }
182    
183        public Map<String, Object> getProperties() {
184            if (properties == null) {
185                properties = new ConcurrentHashMap<String, Object>();
186            }
187            return properties;
188        }
189    
190        public void setProperties(Map<String, Object> properties) {
191            this.properties = properties;
192        }
193    
194        public Message getIn() {
195            if (in == null) {
196                in = createInMessage();
197                configureMessage(in);
198            }
199            return in;
200        }
201    
202        public void setIn(Message in) {
203            this.in = in;
204            configureMessage(in);
205        }
206    
207        public Message getOut() {
208            return getOut(true);
209        }
210    
211        public Message getOut(boolean lazyCreate) {
212            if (out == null && lazyCreate) {
213                out = createOutMessage();
214                configureMessage(out);
215            }
216            return out;
217        }
218    
219        public void setOut(Message out) {
220            this.out = out;
221            configureMessage(out);
222        }
223    
224        public Throwable getException() {
225            return exception;
226        }
227    
228        public void setException(Throwable exception) {
229            this.exception = exception;
230        }
231    
232        public ExchangePattern getPattern() {
233            return pattern;
234        }
235    
236        public void setPattern(ExchangePattern pattern) {
237            this.pattern = pattern;
238        }
239    
240        public void throwException() throws Exception {
241            if (exception == null) {
242                return;
243            }
244            if (exception instanceof RuntimeException) {
245                throw (RuntimeException)exception;
246            }
247            if (exception instanceof Exception) {
248                throw (Exception)exception;
249            }
250            throw wrapRuntimeCamelException(exception);
251        }
252    
253        public Message getFault() {
254            return getFault(true);
255        }
256    
257        public Message getFault(boolean lazyCreate) {
258            if (fault == null && lazyCreate) {
259                fault = createFaultMessage();
260                configureMessage(fault);
261            }
262            return fault;
263        }
264    
265        public void setFault(Message fault) {
266            this.fault = fault;
267            configureMessage(fault);
268        }
269    
270        public String getExchangeId() {
271            if (exchangeId == null) {
272                exchangeId = DefaultExchange.DEFAULT_ID_GENERATOR.generateId();
273            }
274            return exchangeId;
275        }
276    
277        public void setExchangeId(String id) {
278            this.exchangeId = id;
279        }
280    
281        public boolean isFailed() {
282            Message faultMessage = getFault(false);
283            if (faultMessage != null) {
284                Object faultBody = faultMessage.getBody();
285                if (faultBody != null) {
286                    return true;
287                }
288            }
289            return getException() != null;
290        }
291    
292        public boolean isTransacted() {
293            ExchangeProperty<?> property = ExchangeProperty.get("transacted");
294            return property != null && property.get(this) == Boolean.TRUE;
295        }
296    
297        public UnitOfWork getUnitOfWork() {
298            return unitOfWork;
299        }
300    
301        public void setUnitOfWork(UnitOfWork unitOfWork) {
302            this.unitOfWork = unitOfWork;
303        }
304    
305        /**
306         * Factory method used to lazily create the IN message
307         */
308        protected Message createInMessage() {
309            return new DefaultMessage();
310        }
311    
312        /**
313         * Factory method to lazily create the OUT message
314         */
315        protected Message createOutMessage() {
316            return new DefaultMessage();
317        }
318    
319        /**
320         * Factory method to lazily create the FAULT message
321         */
322        protected Message createFaultMessage() {
323            return new DefaultMessage();
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    }