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.io.Serializable;
020 import java.util.Collection;
021 import java.util.LinkedHashMap;
022 import java.util.Map;
023
024 import org.apache.camel.Exchange;
025 import org.apache.camel.util.ObjectHelper;
026 import org.slf4j.Logger;
027 import org.slf4j.LoggerFactory;
028
029 /**
030 * Holder object for sending an exchange over a remote wire as a serialized object.
031 * This is usually configured using the <tt>transferExchange=true</tt> option on the endpoint.
032 * <p/>
033 * As opposed to normal usage where only the body part of the exchange is transferred over the wire,
034 * this holder object serializes the following fields over the wire:
035 * <ul>
036 * <li>exchangeId</li>
037 * <li>in body</li>
038 * <li>out body</li>
039 * <li>in headers</li>
040 * <li>out headers</li>
041 * <li>fault body </li>
042 * <li>fault headers</li>
043 * <li>exchange properties</li>
044 * <li>exception</li>
045 * </ul>
046 * Any object that is not serializable will be skipped and Camel will log this at WARN level.
047 *
048 * @version
049 */
050 public class DefaultExchangeHolder implements Serializable {
051
052 private static final long serialVersionUID = 2L;
053 private static final transient Logger LOG = LoggerFactory.getLogger(DefaultExchangeHolder.class);
054
055 private String exchangeId;
056 private Object inBody;
057 private Object outBody;
058 private Boolean outFaultFlag = Boolean.FALSE;
059 private Map<String, Object> inHeaders;
060 private Map<String, Object> outHeaders;
061 private Map<String, Object> properties;
062 private Exception exception;
063
064 /**
065 * Creates a payload object with the information from the given exchange.
066 *
067 * @param exchange the exchange
068 * @return the holder object with information copied form the exchange
069 */
070 public static DefaultExchangeHolder marshal(Exchange exchange) {
071 return marshal(exchange, true);
072 }
073
074 /**
075 * Creates a payload object with the information from the given exchange.
076 *
077 * @param exchange the exchange
078 * @param includeProperties whether or not to include exchange properties
079 * @return the holder object with information copied form the exchange
080 */
081 public static DefaultExchangeHolder marshal(Exchange exchange, boolean includeProperties) {
082 DefaultExchangeHolder payload = new DefaultExchangeHolder();
083
084 payload.exchangeId = exchange.getExchangeId();
085 payload.inBody = checkSerializableBody("in body", exchange, exchange.getIn().getBody());
086 payload.safeSetInHeaders(exchange);
087 if (exchange.hasOut()) {
088 payload.outBody = checkSerializableBody("out body", exchange, exchange.getOut().getBody());
089 payload.outFaultFlag = exchange.getOut().isFault();
090 payload.safeSetOutHeaders(exchange);
091 }
092 if (includeProperties) {
093 payload.safeSetProperties(exchange);
094 }
095 payload.exception = exchange.getException();
096
097 return payload;
098 }
099
100 /**
101 * Transfers the information from the payload to the exchange.
102 *
103 * @param exchange the exchange to set values from the payload
104 * @param payload the payload with the values
105 */
106 public static void unmarshal(Exchange exchange, DefaultExchangeHolder payload) {
107 exchange.setExchangeId(payload.exchangeId);
108 exchange.getIn().setBody(payload.inBody);
109 if (payload.inHeaders != null) {
110 exchange.getIn().setHeaders(payload.inHeaders);
111 }
112 if (payload.outBody != null) {
113 exchange.getOut().setBody(payload.outBody);
114 if (payload.outHeaders != null) {
115 exchange.getOut().setHeaders(payload.outHeaders);
116 }
117 exchange.getOut().setFault(payload.outFaultFlag.booleanValue());
118 }
119 if (payload.properties != null) {
120 for (String key : payload.properties.keySet()) {
121 exchange.setProperty(key, payload.properties.get(key));
122 }
123 }
124 exchange.setException(payload.exception);
125 }
126
127 /**
128 * Adds a property to the payload.
129 * <p/>
130 * This can be done in special situations where additional information must be added which was not provided
131 * from the source.
132 *
133 * @param payload the serialized payload
134 * @param key the property key to add
135 * @param property the property value to add
136 */
137 public static void addProperty(DefaultExchangeHolder payload, String key, Serializable property) {
138 if (key == null || property == null) {
139 return;
140 }
141 if (payload.properties == null) {
142 payload.properties = new LinkedHashMap<String, Object>();
143 }
144 payload.properties.put(key, property);
145 }
146
147 public String toString() {
148 StringBuilder sb = new StringBuilder("DefaultExchangeHolder[exchangeId=").append(exchangeId);
149 sb.append("inBody=").append(inBody).append(", outBody=").append(outBody);
150 sb.append(", inHeaders=").append(inHeaders).append(", outHeaders=").append(outHeaders);
151 sb.append(", properties=").append(properties).append(", exception=").append(exception);
152 return sb.append(']').toString();
153 }
154
155 private Map<String, Object> safeSetInHeaders(Exchange exchange) {
156 if (exchange.getIn().hasHeaders()) {
157 Map<String, Object> map = checkMapSerializableObjects("in headers", exchange, exchange.getIn().getHeaders());
158 if (map != null && !map.isEmpty()) {
159 inHeaders = new LinkedHashMap<String, Object>(map);
160 }
161 }
162 return null;
163 }
164
165 private Map<String, Object> safeSetOutHeaders(Exchange exchange) {
166 if (exchange.hasOut() && exchange.getOut().hasHeaders()) {
167 Map<String, Object> map = checkMapSerializableObjects("out headers", exchange, exchange.getOut().getHeaders());
168 if (map != null && !map.isEmpty()) {
169 outHeaders = new LinkedHashMap<String, Object>(map);
170 }
171 }
172 return null;
173 }
174
175 private Map<String, Object> safeSetProperties(Exchange exchange) {
176 if (exchange.hasProperties()) {
177 Map<String, Object> map = checkMapSerializableObjects("properties", exchange, exchange.getProperties());
178 if (map != null && !map.isEmpty()) {
179 properties = new LinkedHashMap<String, Object>(map);
180 }
181 }
182 return null;
183 }
184
185 private static Object checkSerializableBody(String type, Exchange exchange, Object object) {
186 if (object == null) {
187 return null;
188 }
189
190 Serializable converted = exchange.getContext().getTypeConverter().convertTo(Serializable.class, exchange, object);
191 if (converted != null) {
192 return converted;
193 } else {
194 LOG.warn("Exchange " + type + " containing object: " + object + " of type: " + object.getClass().getCanonicalName() + " cannot be serialized, it will be excluded by the holder.");
195 return null;
196 }
197 }
198
199 private static Map<String, Object> checkMapSerializableObjects(String type, Exchange exchange, Map<String, Object> map) {
200 if (map == null) {
201 return null;
202 }
203
204 Map<String, Object> result = new LinkedHashMap<String, Object>();
205 for (Map.Entry<String, Object> entry : map.entrySet()) {
206
207 // silently skip any values which is null
208 if (entry.getValue() != null) {
209 Serializable converted = exchange.getContext().getTypeConverter().convertTo(Serializable.class, exchange, entry.getValue());
210
211 // if the converter is a map/collection we need to check its content as well
212 if (converted instanceof Collection) {
213 Collection<?> valueCol = (Collection<?>) converted;
214 if (!collectionContainsAllSerializableObjects(valueCol, exchange)) {
215 logCannotSerializeObject(type, entry.getKey(), entry.getValue());
216 continue;
217 }
218 } else if (converted instanceof Map) {
219 Map<?, ?> valueMap = (Map<?, ?>) converted;
220 if (!mapContainsAllSerializableObjects(valueMap, exchange)) {
221 logCannotSerializeObject(type, entry.getKey(), entry.getValue());
222 continue;
223 }
224 }
225
226 if (converted != null) {
227 result.put(entry.getKey(), converted);
228 } else {
229 logCannotSerializeObject(type, entry.getKey(), entry.getValue());
230 }
231 }
232 }
233
234 return result;
235 }
236
237 private static void logCannotSerializeObject(String type, String key, Object value) {
238 if (key.startsWith("Camel")) {
239 // log Camel at DEBUG level
240 if (LOG.isDebugEnabled()) {
241 LOG.debug("Exchange {} containing key: {} with object: {} of type: {} cannot be serialized, it will be excluded by the holder."
242 , new Object[]{type, key, value, ObjectHelper.classCanonicalName(value)});
243 }
244 } else {
245 // log regular at WARN level
246 LOG.warn("Exchange {} containing key: {} with object: {} of type: {} cannot be serialized, it will be excluded by the holder."
247 , new Object[]{type, key, value, ObjectHelper.classCanonicalName(value)});
248 }
249 }
250
251 private static boolean collectionContainsAllSerializableObjects(Collection<?> col, Exchange exchange) {
252 for (Object value : col) {
253 if (value != null) {
254 Serializable converted = exchange.getContext().getTypeConverter().convertTo(Serializable.class, exchange, value);
255 if (converted == null) {
256 return false;
257 }
258 }
259 }
260 return true;
261 }
262
263 private static boolean mapContainsAllSerializableObjects(Map<?, ?> map, Exchange exchange) {
264 for (Object value : map.values()) {
265 if (value != null) {
266 Serializable converted = exchange.getContext().getTypeConverter().convertTo(Serializable.class, exchange, value);
267 if (converted == null) {
268 return false;
269 }
270 }
271 }
272 return true;
273 }
274
275 }