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.util;
018    
019    import java.io.InputStream;
020    import java.io.OutputStream;
021    import java.io.Reader;
022    import java.io.Writer;
023    import javax.xml.transform.stream.StreamSource;
024    
025    import org.apache.camel.Exchange;
026    import org.apache.camel.Message;
027    import org.apache.camel.StreamCache;
028    import org.apache.camel.converter.jaxp.BytesSource;
029    import org.apache.camel.converter.jaxp.StringSource;
030    
031    /**
032     * Some helper methods when working with {@link org.apache.camel.Message}.
033     *
034     * @version $Revision: 813717 $
035     */
036    public final class MessageHelper {
037    
038        /**
039         * Utility classes should not have a public constructor.
040         */
041        private MessageHelper() {
042        }
043    
044        /**
045         * Extracts the given body and returns it as a String, that
046         * can be used for logging etc.
047         * <p/>
048         * Will handle stream based bodies wrapped in StreamCache.
049         *
050         * @param message  the message with the body
051         * @return the body as String, can return <tt>null</null> if no body
052         */
053        public static String extractBodyAsString(Message message) {
054            if (message == null) {
055                return null;
056            }
057    
058            StreamCache newBody = message.getBody(StreamCache.class);
059            if (newBody != null) {
060                message.setBody(newBody);
061            }
062    
063            Object answer = message.getBody(String.class);
064            if (answer == null) {
065                answer = message.getBody();
066            }
067    
068            if (newBody != null) {
069                // Reset the InputStreamCache
070                newBody.reset();
071            }
072    
073            return answer != null ? answer.toString() : null;
074        }
075    
076        /**
077         * Gets the given body class type name as a String.
078         * <p/>
079         * Will skip java.lang. for the build in Java types.
080         *
081         * @param message  the message with the body
082         * @return the body typename as String, can return <tt>null</null> if no body
083         */
084        public static String getBodyTypeName(Message message) {
085            if (message == null) {
086                return null;
087            }
088            String answer = ObjectHelper.classCanonicalName(message.getBody());
089            if (answer != null && answer.startsWith("java.lang.")) {
090                return answer.substring(10);
091            }
092            return answer;
093        }
094        
095        /**
096         * If the message body contains a {@link StreamCache} instance, reset the cache to 
097         * enable reading from it again.
098         * 
099         * @param message the message for which to reset the body
100         */
101        public static void resetStreamCache(Message message) {
102            if (message == null) {
103                return;
104            }
105            if (message.getBody() instanceof StreamCache) {
106                ((StreamCache) message.getBody()).reset();
107            }
108        }
109        
110        /**
111         * Returns the MIME content type on the message or <tt>null</tt> if none defined
112         */
113        public static String getContentType(Message message) {        
114            return message.getHeader(Exchange.CONTENT_TYPE, String.class);
115        }
116    
117        /**
118         * Returns the MIME content encoding on the message or <tt>null</tt> if none defined
119         */
120        public static String getContentEncoding(Message message) {
121            return message.getHeader(Exchange.CONTENT_ENCODING, String.class);
122        }
123    
124        /**
125         * Extracts the body for logging purpose.
126         * <p/>
127         * Will clip the body if its too big for logging.
128         *
129         * @see org.apache.camel.Exchange#LOG_DEBUG_BODY_MAX_CHARS
130         * @param message the message
131         * @return the logging message
132         */
133        public static String extractBodyForLogging(Message message) {
134            Object obj = message.getBody();
135            if (obj == null) {
136                return "Message: [Body is null]";
137            }
138    
139            // do not log streams by default
140            boolean streams = false;
141            if (message.getExchange() != null) {
142                String property = message.getExchange().getContext().getProperties().get(Exchange.LOG_DEBUG_BODY_STREAMS);
143                if (property != null) {
144                    streams = message.getExchange().getContext().getTypeConverter().convertTo(Boolean.class, property);
145                }
146            }
147    
148            if (obj instanceof StringSource || obj instanceof BytesSource) {
149                // these two are okay
150            } else if (!streams && obj instanceof StreamSource) {
151                return "Message: [Body is instance of java.xml.transform.StreamSource]";
152            } else if (!streams && obj instanceof InputStream) {
153                return "Message: [Body is instance of java.io.InputStream]";
154            } else if (!streams && obj instanceof OutputStream) {
155                return "Message: [Body is instance of java.io.OutputStream]";
156            } else if (!streams && obj instanceof Reader) {
157                return "Message: [Body is instance of java.io.Reader]";
158            } else if (!streams && obj instanceof Writer) {
159                return "Message: [Body is instance of java.io.Writer]";
160            }
161    
162            // default to 1000 chars
163            int length = 1000;
164    
165            if (message.getExchange() != null) {
166                String property = message.getExchange().getContext().getProperties().get(Exchange.LOG_DEBUG_BODY_MAX_CHARS);
167                if (property != null) {
168                    length = message.getExchange().getContext().getTypeConverter().convertTo(Integer.class, property);
169                }
170            }
171    
172            String body = obj.toString();
173            if (body == null) {
174                return "Message: [Body is null]";
175            }
176    
177            // clip body if length enabled and the body is too big
178            if (length > 0 && body.length() > length) {
179                body = body.substring(0, length) + "... [Body clipped after " + length + " chars, total length is " + body.length() + "]";
180            }
181    
182            return "Message: " + body;
183        }
184    
185    }