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 */
017package org.apache.camel.impl;
018
019import org.apache.camel.Exchange;
020import org.apache.camel.InvalidPayloadException;
021import org.apache.camel.Message;
022import org.apache.camel.TypeConverter;
023import org.apache.camel.spi.DataType;
024import org.apache.camel.spi.DataTypeAware;
025
026/**
027 * A base class for implementation inheritance providing the core
028 * {@link Message} body handling features but letting the derived class deal
029 * with headers.
030 *
031 * Unless a specific provider wishes to do something particularly clever with
032 * headers you probably want to just derive from {@link DefaultMessage}
033 *
034 * @version 
035 */
036public abstract class MessageSupport implements Message, DataTypeAware {
037    private Exchange exchange;
038    private Object body;
039    private String messageId;
040    private DataType dataType;
041
042    @Override
043    public String toString() {
044        // do not output information about the message as it may contain sensitive information
045        return String.format("Message[%s]", messageId == null ? "" : messageId);
046    }
047
048    public Object getBody() {
049        if (body == null) {
050            body = createBody();
051        }
052        return body;
053    }
054
055    public <T> T getBody(Class<T> type) {
056        return getBody(type, getBody());
057    }
058
059    public Object getMandatoryBody() throws InvalidPayloadException {
060        Object answer = getBody();
061        if (answer == null) {
062            throw new InvalidPayloadException(getExchange(), Object.class, this);
063        }
064        return answer;
065    }
066
067    protected <T> T getBody(Class<T> type, Object body) {
068        // eager same instance type test to avoid the overhead of invoking the type converter
069        // if already same type
070        if (type.isInstance(body)) {
071            return type.cast(body);
072        }
073
074        Exchange e = getExchange();
075        if (e != null) {
076            TypeConverter converter = e.getContext().getTypeConverter();
077
078            // lets first try converting the body itself first
079            // as for some types like InputStream v Reader its more efficient to do the transformation
080            // from the body itself as its got efficient implementations of them, before trying the message
081            T answer = converter.convertTo(type, e, body);
082            if (answer != null) {
083                return answer;
084            }
085
086            // fallback and try the message itself (e.g. used in camel-http)
087            answer = converter.tryConvertTo(type, e, this);
088            if (answer != null) {
089                return answer;
090            }
091        }
092
093        // not possible to convert
094        return null;
095    }
096
097    public <T> T getMandatoryBody(Class<T> type) throws InvalidPayloadException {
098        // eager same instance type test to avoid the overhead of invoking the type converter
099        // if already same type
100        if (type.isInstance(body)) {
101            return type.cast(body);
102        }
103
104        Exchange e = getExchange();
105        if (e != null) {
106            TypeConverter converter = e.getContext().getTypeConverter();
107            try {
108                return converter.mandatoryConvertTo(type, e, getBody());
109            } catch (Exception cause) {
110                throw new InvalidPayloadException(e, type, this, cause);
111            }
112        }
113        throw new InvalidPayloadException(e, type, this);
114    }
115
116    public void setBody(Object body) {
117        this.body = body;
118        this.dataType = body != null ? new DataType(body.getClass()) : null;
119    }
120
121    public <T> void setBody(Object value, Class<T> type) {
122        Exchange e = getExchange();
123        if (e != null) {
124            T v = e.getContext().getTypeConverter().convertTo(type, e, value);
125            if (v != null) {
126                value = v;
127            }
128        }
129        setBody(value);
130    }
131
132    @Override
133    public void setBody(Object body, DataType type) {
134        this.body = body;
135        this.dataType = type;
136    }
137
138    @Override
139    public DataType getDataType() {
140        if (this.dataType == null) {
141            this.dataType = body != null ? new DataType(body.getClass()) : null;
142        }
143        return this.dataType;
144    }
145
146    @Override
147    public void setDataType(DataType type) {
148        this.dataType = type;
149    }
150
151    public Message copy() {
152        Message answer = newInstance();
153        answer.copyFrom(this);
154        return answer;
155    }
156
157    public void copyFrom(Message that) {
158        if (that == this) {
159            // the same instance so do not need to copy
160            return;
161        }
162        copyFromWithNewBody(that, that.getBody());
163        if (that instanceof DataTypeAware) {
164            setDataType(((DataTypeAware)that).getDataType());
165        }
166    }
167
168    public void copyFromWithNewBody(Message that, Object newBody) {
169        if (that == this) {
170            // the same instance so do not need to copy
171            return;
172        }
173
174        setMessageId(that.getMessageId());
175        setBody(newBody);
176        setFault(that.isFault());
177
178        // the headers may be the same instance if the end user has made some mistake
179        // and set the OUT message with the same header instance of the IN message etc
180        boolean sameHeadersInstance = false;
181        if (hasHeaders() && that.hasHeaders() && getHeaders() == that.getHeaders()) {
182            sameHeadersInstance = true;
183        }
184
185        if (!sameHeadersInstance) {
186            if (hasHeaders()) {
187                // okay its safe to clear the headers
188                getHeaders().clear();
189            }
190            if (that.hasHeaders()) {
191                getHeaders().putAll(that.getHeaders());
192            }
193        }
194
195        copyAttachments(that);
196    }
197
198    public Exchange getExchange() {
199        return exchange;
200    }
201
202    public void setExchange(Exchange exchange) {
203        this.exchange = exchange;
204    }
205    
206    public void copyAttachments(Message that) {
207        // the attachments may be the same instance if the end user has made some mistake
208        // and set the OUT message with the same attachment instance of the IN message etc
209        boolean sameAttachments = false;
210        if (hasAttachments() && that.hasAttachments() && getAttachmentObjects() == that.getAttachmentObjects()) {
211            sameAttachments = true;
212        }
213
214        if (!sameAttachments) {
215            if (hasAttachments()) {
216                // okay its safe to clear the attachments
217                getAttachmentObjects().clear();
218            }
219            if (that.hasAttachments()) {
220                getAttachmentObjects().putAll(that.getAttachmentObjects());
221            }
222        }
223    }
224
225    /**
226     * Returns a new instance
227     */
228    public abstract Message newInstance();
229
230    /**
231     * A factory method to allow a provider to lazily create the message body
232     * for inbound messages from other sources
233     *
234     * @return the value of the message body or null if there is no value
235     *         available
236     */
237    protected Object createBody() {
238        return null;
239    }
240
241    public String getMessageId() {
242        if (messageId == null) {
243            messageId = createMessageId();
244        }
245        return this.messageId;
246    }
247
248    public void setMessageId(String messageId) {
249        this.messageId = messageId;
250    }
251
252    /**
253     * Allow implementations to auto-create a messageId
254     */
255    protected String createMessageId() {
256        String uuid = null;
257        if (exchange != null) {
258            uuid = exchange.getContext().getUuidGenerator().generateUuid();
259        }
260        // fall back to the simple UUID generator
261        if (uuid == null) {
262            uuid = new SimpleUuidGenerator().generateUuid();
263        }
264        return uuid;
265    }
266}