blob: 7bab1342b5f3cacd0a7f221c95db66e44c0fd0b7 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.util;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Map;
import java.util.TreeMap;
import javax.xml.transform.stream.StreamSource;
import org.apache.camel.BytesSource;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.StreamCache;
import org.apache.camel.StringSource;
import org.apache.camel.WrappedFile;
/**
* Some helper methods when working with {@link org.apache.camel.Message}.
*
* @version
*/
public final class MessageHelper {
/**
* Utility classes should not have a public constructor.
*/
private MessageHelper() {
}
/**
* Extracts the given body and returns it as a String, that can be used for
* logging etc.
* <p/>
* Will handle stream based bodies wrapped in StreamCache.
*
* @param message the message with the body
* @return the body as String, can return <tt>null</null> if no body
*/
public static String extractBodyAsString(Message message) {
if (message == null) {
return null;
}
StreamCache newBody = message.getBody(StreamCache.class);
if (newBody != null) {
message.setBody(newBody);
}
Object answer = message.getBody(String.class);
if (answer == null) {
answer = message.getBody();
}
if (newBody != null) {
// Reset the InputStreamCache
newBody.reset();
}
return answer != null ? answer.toString() : null;
}
/**
* Gets the given body class type name as a String.
* <p/>
* Will skip java.lang. for the build in Java types.
*
* @param message the message with the body
* @return the body type name as String, can return
* <tt>null</null> if no body
*/
public static String getBodyTypeName(Message message) {
if (message == null) {
return null;
}
String answer = ObjectHelper.classCanonicalName(message.getBody());
if (answer != null && answer.startsWith("java.lang.")) {
return answer.substring(10);
}
return answer;
}
/**
* If the message body contains a {@link StreamCache} instance, reset the
* cache to enable reading from it again.
*
* @param message the message for which to reset the body
*/
public static void resetStreamCache(Message message) {
if (message == null) {
return;
}
if (message.getBody() instanceof StreamCache) {
((StreamCache)message.getBody()).reset();
}
}
/**
* Returns the MIME content type on the message or <tt>null</tt> if none
* defined
*/
public static String getContentType(Message message) {
return message.getHeader(Exchange.CONTENT_TYPE, String.class);
}
/**
* Returns the MIME content encoding on the message or <tt>null</tt> if none
* defined
*/
public static String getContentEncoding(Message message) {
return message.getHeader(Exchange.CONTENT_ENCODING, String.class);
}
/**
* Extracts the body for logging purpose.
* <p/>
* Will clip the body if its too big for logging. Will prepend the message
* with <tt>Message: </tt>
*
* @see org.apache.camel.Exchange#LOG_DEBUG_BODY_STREAMS
* @see org.apache.camel.Exchange#LOG_DEBUG_BODY_MAX_CHARS
* @param message the message
* @return the logging message
*/
public static String extractBodyForLogging(Message message) {
return extractBodyForLogging(message, "Message: ");
}
/**
* Extracts the body for logging purpose.
* <p/>
* Will clip the body if its too big for logging.
*
* @see org.apache.camel.Exchange#LOG_DEBUG_BODY_STREAMS
* @see org.apache.camel.Exchange#LOG_DEBUG_BODY_MAX_CHARS
* @param message the message
* @param prepend a message to prepend
* @return the logging message
*/
public static String extractBodyForLogging(Message message, String prepend) {
boolean streams = false;
if (message.getExchange() != null) {
String property = message.getExchange().getContext().getProperty(Exchange.LOG_DEBUG_BODY_STREAMS);
if (property != null) {
streams = message.getExchange().getContext().getTypeConverter().convertTo(Boolean.class, message.getExchange(), property);
}
}
// default to 1000 chars
int maxChars = 1000;
if (message.getExchange() != null) {
String property = message.getExchange().getContext().getProperty(Exchange.LOG_DEBUG_BODY_MAX_CHARS);
if (property != null) {
maxChars = message.getExchange().getContext().getTypeConverter().convertTo(Integer.class, property);
}
}
return extractBodyForLogging(message, prepend, streams, false, maxChars);
}
/**
* Extracts the body for logging purpose.
* <p/>
* Will clip the body if its too big for logging.
*
* @see org.apache.camel.Exchange#LOG_DEBUG_BODY_MAX_CHARS
* @param message the message
* @param prepend a message to prepend
* @param allowStreams whether or not streams is allowed
* @param allowFiles whether or not files is allowed
* @param maxChars limit to maximum number of chars. Use 0 or negative value
* to not limit at all.
* @return the logging message
*/
public static String extractBodyForLogging(Message message, String prepend, boolean allowStreams, boolean allowFiles, int maxChars) {
Object obj = message.getBody();
if (obj == null) {
return prepend + "[Body is null]";
}
if (!allowStreams) {
if (obj instanceof StreamSource && !(obj instanceof StringSource || obj instanceof BytesSource)) {
/*
* Generally do not log StreamSources but as StringSource and
* ByteSource are memory based they are ok
*/
return prepend + "[Body is instance of java.xml.transform.StreamSource]";
} else if (obj instanceof StreamCache) {
return prepend + "[Body is instance of org.apache.camel.StreamCache]";
} else if (obj instanceof InputStream) {
return prepend + "[Body is instance of java.io.InputStream]";
} else if (obj instanceof OutputStream) {
return prepend + "[Body is instance of java.io.OutputStream]";
} else if (obj instanceof Reader) {
return prepend + "[Body is instance of java.io.Reader]";
} else if (obj instanceof Writer) {
return prepend + "[Body is instance of java.io.Writer]";
} else if (obj instanceof WrappedFile || obj instanceof File) {
return prepend + "[Body is file based: " + obj + "]";
}
}
// is the body a stream cache
StreamCache cache;
if (obj instanceof StreamCache) {
cache = (StreamCache)obj;
} else {
cache = null;
}
// grab the message body as a string
String body = null;
if (message.getExchange() != null) {
try {
body = message.getExchange().getContext().getTypeConverter().convertTo(String.class, message.getExchange(), obj);
} catch (Exception e) {
// ignore as the body is for logging purpose
}
}
if (body == null) {
body = obj.toString();
}
// reset stream cache after use
if (cache != null) {
cache.reset();
}
if (body == null) {
return prepend + "[Body is null]";
}
// clip body if length enabled and the body is too big
if (maxChars > 0 && body.length() > maxChars) {
body = body.substring(0, maxChars) + "... [Body clipped after " + maxChars + " chars, total length is " + body.length() + "]";
}
return prepend + body;
}
/**
* Dumps the message as a generic XML structure.
*
* @param message the message
* @return the XML
*/
public static String dumpAsXml(Message message) {
return dumpAsXml(message, true);
}
/**
* Dumps the message as a generic XML structure.
*
* @param message the message
* @param includeBody whether or not to include the message body
* @return the XML
*/
public static String dumpAsXml(Message message, boolean includeBody) {
return dumpAsXml(message, includeBody, 0);
}
/**
* Dumps the message as a generic XML structure.
*
* @param message the message
* @param includeBody whether or not to include the message body
* @param indent number of spaces to indent
* @return the XML
*/
public static String dumpAsXml(Message message, boolean includeBody, int indent) {
return dumpAsXml(message, includeBody, indent, false, true, 128 * 1024);
}
/**
* Dumps the message as a generic XML structure.
*
* @param message the message
* @param includeBody whether or not to include the message body
* @param indent number of spaces to indent
* @param allowStreams whether to include message body if they are stream based
* @param allowFiles whether to include message body if they are file based
* @param maxChars clip body after maximum chars (to avoid very big messages). Use 0 or negative value to not limit at all.
* @return the XML
*/
public static String dumpAsXml(Message message, boolean includeBody, int indent, boolean allowStreams, boolean allowFiles, int maxChars) {
StringBuilder sb = new StringBuilder();
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < indent; i++) {
prefix.append(" ");
}
// include exchangeId as attribute on the <message> tag
sb.append(prefix);
sb.append("<message exchangeId=\"").append(message.getExchange().getExchangeId()).append("\">\n");
// headers
if (message.hasHeaders()) {
sb.append(prefix);
sb.append(" <headers>\n");
// sort the headers so they are listed A..Z
Map<String, Object> headers = new TreeMap<String, Object>(message.getHeaders());
for (Map.Entry<String, Object> entry : headers.entrySet()) {
Object value = entry.getValue();
String type = ObjectHelper.classCanonicalName(value);
sb.append(prefix);
sb.append(" <header key=\"" + entry.getKey() + "\"");
if (type != null) {
sb.append(" type=\"" + type + "\"");
}
sb.append(">");
// dump header value as XML, use Camel type converter to convert
// to String
if (value != null) {
try {
String xml = message.getExchange().getContext().getTypeConverter().convertTo(String.class,
message.getExchange(), value);
if (xml != null) {
// must always xml encode
sb.append(StringHelper.xmlEncode(xml));
}
} catch (Exception e) {
// ignore as the body is for logging purpose
}
}
sb.append("</header>\n");
}
sb.append(prefix);
sb.append(" </headers>\n");
}
if (includeBody) {
sb.append(prefix);
sb.append(" <body");
String type = ObjectHelper.classCanonicalName(message.getBody());
if (type != null) {
sb.append(" type=\"" + type + "\"");
}
sb.append(">");
String xml = extractBodyForLogging(message, "", allowStreams, allowFiles, maxChars);
if (xml != null) {
// must always xml encode
sb.append(StringHelper.xmlEncode(xml));
}
sb.append("</body>\n");
}
sb.append(prefix);
sb.append("</message>");
return sb.toString();
}
/**
* Copies the headers from the source to the target message.
*
* @param source the source message
* @param target the target message
* @param override whether to override existing headers
*/
public static void copyHeaders(Message source, Message target, boolean override) {
if (!source.hasHeaders()) {
return;
}
for (Map.Entry<String, Object> entry : source.getHeaders().entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (target.getHeader(key) == null || override) {
target.setHeader(key, value);
}
}
}
}