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
191        copyFromWithNewBody(that, that.getBody());
192    }
193
194    @Override
195    public void copyFromWithNewBody(Message that, Object newBody) {
196        if (that == this) {
197            // the same instance so do not need to copy
198            return;
199        }
200
201        // must copy over CamelContext
202        if (that instanceof CamelContextAware) {
203            setCamelContext(((CamelContextAware) that).getCamelContext());
204        }
205        // should likely not set DataType as the new body may be a different type than the original body
206
207        setMessageId(that.getMessageId());
208        setBody(newBody);
209        setFault(that.isFault());
210
211        // the headers may be the same instance if the end user has made some mistake
212        // and set the OUT message with the same header instance of the IN message etc
213        boolean sameHeadersInstance = false;
214        if (hasHeaders() && that.hasHeaders() && getHeaders() == that.getHeaders()) {
215            sameHeadersInstance = true;
216        }
217
218        if (!sameHeadersInstance) {
219            if (hasHeaders()) {
220                // okay its safe to clear the headers
221                getHeaders().clear();
222            }
223            if (that.hasHeaders()) {
224                getHeaders().putAll(that.getHeaders());
225            }
226        }
227
228        copyAttachments(that);
229    }
230
231    @Override
232    public Exchange getExchange() {
233        return exchange;
234    }
235
236    public void setExchange(Exchange exchange) {
237        this.exchange = exchange;
238    }
239
240    @Override
241    public CamelContext getCamelContext() {
242        return camelContext;
243    }
244
245    @Override
246    public void setCamelContext(CamelContext camelContext) {
247        this.camelContext = camelContext;
248    }
249
250    @Override
251    public void copyAttachments(Message that) {
252        // the attachments may be the same instance if the end user has made some mistake
253        // and set the OUT message with the same attachment instance of the IN message etc
254        boolean sameAttachments = false;
255        if (hasAttachments() && that.hasAttachments() && getAttachmentObjects() == that.getAttachmentObjects()) {
256            sameAttachments = true;
257        }
258
259        if (!sameAttachments) {
260            if (hasAttachments()) {
261                // okay its safe to clear the attachments
262                getAttachmentObjects().clear();
263            }
264            if (that.hasAttachments()) {
265                getAttachmentObjects().putAll(that.getAttachmentObjects());
266            }
267        }
268    }
269
270    /**
271     * Returns a new instance
272     */
273    public abstract Message newInstance();
274
275    /**
276     * A factory method to allow a provider to lazily create the message body
277     * for inbound messages from other sources
278     *
279     * @return the value of the message body or null if there is no value
280     *         available
281     */
282    protected Object createBody() {
283        return null;
284    }
285
286    @Override
287    public String getMessageId() {
288        if (messageId == null) {
289            messageId = createMessageId();
290        }
291        return this.messageId;
292    }
293
294    @Override
295    public void setMessageId(String messageId) {
296        this.messageId = messageId;
297    }
298
299    /**
300     * Allow implementations to auto-create a messageId
301     */
302    protected String createMessageId() {
303        String uuid = null;
304        if (exchange != null) {
305            uuid = exchange.getContext().getUuidGenerator().generateUuid();
306        }
307        // fall back to the simple UUID generator
308        if (uuid == null) {
309            uuid = new SimpleUuidGenerator().generateUuid();
310        }
311        return uuid;
312    }
313}