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