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.HashMap;
020    import java.util.Map;
021    import java.util.concurrent.ConcurrentHashMap;
022    
023    import org.apache.camel.CamelContext;
024    import org.apache.camel.Exchange;
025    import org.apache.camel.ExchangePattern;
026    import org.apache.camel.ExchangeProperty;
027    import org.apache.camel.Message;
028    import org.apache.camel.RuntimeCamelException;
029    import org.apache.camel.spi.UnitOfWork;
030    import org.apache.camel.util.UuidGenerator;
031    
032    /**
033     * A default implementation of {@link Exchange}
034     *
035     * @version $Revision: 46968 $
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 { // if the value is null , we just remove the key from the map
163                if (name !=  null) {
164                    getProperties().remove(name);
165                }
166            }
167        }
168    
169        private <T> void validateExchangePropertyIsExpectedType(ExchangeProperty<?> property, Class<T> type, Object value) {
170            if (value != null && property != null && !property.type().isAssignableFrom(type)) {
171                throw new RuntimeCamelException("Type cast exception while getting an "
172                        + "Exchange Property value '" + value.toString()
173                        + "' on Exchange " + this
174                        + " for a well known Exchange Property with these traits: " + property);
175            }
176        }
177    
178        public Object removeProperty(String name) {
179            return getProperties().remove(name);
180        }
181    
182        public Map<String, Object> getProperties() {
183            if (properties == null) {
184                properties = new ConcurrentHashMap<String, Object>();
185            }
186            return properties;
187        }
188    
189        public void setProperties(Map<String, Object> properties) {
190            this.properties = properties;
191        }
192    
193        public Message getIn() {
194            if (in == null) {
195                in = createInMessage();
196                configureMessage(in);
197            }
198            return in;
199        }
200    
201        public void setIn(Message in) {
202            this.in = in;
203            configureMessage(in);
204        }
205    
206        public Message getOut() {
207            return getOut(true);
208        }
209    
210        public Message getOut(boolean lazyCreate) {
211            if (out == null && lazyCreate) {
212                out = createOutMessage();
213                configureMessage(out);
214            }
215            return out;
216        }
217    
218        public void setOut(Message out) {
219            this.out = out;
220            configureMessage(out);
221        }
222    
223        public Throwable getException() {
224            return exception;
225        }
226    
227        public void setException(Throwable exception) {
228            this.exception = exception;
229        }
230    
231        public ExchangePattern getPattern() {
232            return pattern;
233        }
234    
235        public void setPattern(ExchangePattern pattern) {
236            this.pattern = pattern;
237        }
238    
239        public void throwException() throws Exception {
240            if (exception == null) {
241                return;
242            }
243            if (exception instanceof RuntimeException) {
244                throw (RuntimeException)exception;
245            }
246            if (exception instanceof Exception) {
247                throw (Exception)exception;
248            }
249            throw new RuntimeCamelException(exception);
250        }
251    
252        public Message getFault() {
253            return getFault(true);
254        }
255    
256        public Message getFault(boolean lazyCreate) {
257            if (fault == null && lazyCreate) {
258                fault = createFaultMessage();
259                configureMessage(fault);
260            }
261            return fault;
262        }
263    
264        public void setFault(Message fault) {
265            this.fault = fault;
266            configureMessage(fault);
267        }
268    
269        public String getExchangeId() {
270            if (exchangeId == null) {
271                exchangeId = DefaultExchange.DEFAULT_ID_GENERATOR.generateId();
272            }
273            return exchangeId;
274        }
275    
276        public void setExchangeId(String id) {
277            this.exchangeId = id;
278        }
279    
280        public boolean isFailed() {
281            Message faultMessage = getFault(false);
282            if (faultMessage != null) {
283                Object faultBody = faultMessage.getBody();
284                if (faultBody != null) {
285                    return true;
286                }
287            }
288            return getException() != null;
289        }
290    
291        public boolean isTransacted() {
292            ExchangeProperty<?> property = ExchangeProperty.get("transacted");
293            return property != null && property.get(this) == Boolean.TRUE;
294        }
295    
296        public UnitOfWork getUnitOfWork() {
297            return unitOfWork;
298        }
299    
300        public void setUnitOfWork(UnitOfWork unitOfWork) {
301            this.unitOfWork = unitOfWork;
302        }
303    
304        /**
305         * Factory method used to lazily create the IN message
306         */
307        protected Message createInMessage() {
308            return new DefaultMessage();
309        }
310    
311        /**
312         * Factory method to lazily create the OUT message
313         */
314        protected Message createOutMessage() {
315            return new DefaultMessage();
316        }
317    
318        /**
319         * Factory method to lazily create the FAULT message
320         */
321        protected Message createFaultMessage() {
322            return new DefaultMessage();
323        }
324    
325        /**
326         * Configures the message after it has been set on the exchange
327         */
328        protected void configureMessage(Message message) {
329            if (message instanceof MessageSupport) {
330                MessageSupport messageSupport = (MessageSupport)message;
331                messageSupport.setExchange(this);
332            }
333        }
334    
335    }