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.servicemix.jbi.messaging;
018
019 import java.io.ByteArrayOutputStream;
020 import java.io.Externalizable;
021 import java.io.IOException;
022 import java.io.ObjectInput;
023 import java.io.ObjectOutput;
024 import java.util.Collections;
025 import java.util.HashMap;
026 import java.util.Iterator;
027 import java.util.Map;
028 import java.util.Set;
029
030 import javax.activation.DataHandler;
031 import javax.activation.DataSource;
032 import javax.jbi.messaging.Fault;
033 import javax.jbi.messaging.MessageExchange;
034 import javax.jbi.messaging.MessagingException;
035 import javax.jbi.messaging.NormalizedMessage;
036 import javax.security.auth.Subject;
037 import javax.xml.transform.Source;
038 import javax.xml.transform.TransformerException;
039 import javax.xml.transform.sax.SAXSource;
040 import javax.xml.transform.stream.StreamSource;
041
042 import org.apache.servicemix.client.Message;
043 import org.apache.servicemix.jbi.RuntimeJBIException;
044 import org.apache.servicemix.jbi.jaxp.BytesSource;
045 import org.apache.servicemix.jbi.jaxp.ResourceSource;
046 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
047 import org.apache.servicemix.jbi.jaxp.StringSource;
048 import org.apache.servicemix.jbi.util.ByteArrayDataSource;
049 import org.apache.servicemix.jbi.util.FileUtil;
050
051 /**
052 * Represents a JBI NormalizedMessage.
053 *
054 * @version $Revision: 694632 $
055 */
056 public class NormalizedMessageImpl implements NormalizedMessage, Externalizable, Message {
057
058 private static final long serialVersionUID = 9179194301410526549L;
059
060 private static final SourceTransformer TRANSFORMER = new SourceTransformer();
061
062 protected transient MessageExchangeImpl exchange;
063
064 private transient Source content;
065
066 private transient Object body;
067
068 private Subject securitySubject;
069
070 private Map properties;
071
072 private Map attachments;
073
074 /**
075 * Constructor
076 *
077 */
078 public NormalizedMessageImpl() {
079 }
080
081 /**
082 * Constructor
083 *
084 * @param exchange
085 */
086 public NormalizedMessageImpl(MessageExchangeImpl exchange) {
087 this.exchange = exchange;
088 }
089
090 /**
091 * @return the content of the message
092 */
093 public Source getContent() {
094 if (content == null && body != null) {
095 try {
096 getMarshaler().marshal(exchange, this, body);
097 } catch (MessagingException e) {
098 throw new RuntimeJBIException(e);
099 }
100 }
101 return content;
102 }
103
104 /**
105 * set the content fo the message
106 *
107 * @param source
108 */
109 public void setContent(Source source) {
110 this.content = source;
111 }
112
113 /**
114 * @return the security subject from the message
115 */
116 public Subject getSecuritySubject() {
117 return securitySubject;
118 }
119
120 /**
121 * set the security subject
122 *
123 * @param securitySubject
124 */
125 public void setSecuritySubject(Subject securitySubject) {
126 this.securitySubject = securitySubject;
127 }
128
129 /**
130 * get a named property
131 *
132 * @param name
133 * @return a property from the message
134 */
135 public Object getProperty(String name) {
136 if (properties != null) {
137 return properties.get(name);
138 }
139 return null;
140 }
141
142 /**
143 * @return an iterator of property names
144 */
145 public Set getPropertyNames() {
146 if (properties != null) {
147 return Collections.unmodifiableSet(properties.keySet());
148 }
149 return Collections.EMPTY_SET;
150 }
151
152 /**
153 * set a property
154 *
155 * @param name
156 * @param value
157 */
158 public void setProperty(String name, Object value) {
159 if (value == null) {
160 if (properties != null) {
161 properties.remove(name);
162 }
163 } else {
164 getProperties().put(name, value);
165 }
166 }
167
168 /**
169 * Add an attachment
170 *
171 * @param id
172 * @param handler
173 */
174 public void addAttachment(String id, DataHandler handler) {
175 getAttachments().put(id, handler.getDataSource());
176 }
177
178 /**
179 * Get a named attachement
180 *
181 * @param id the id of the stored attachment
182 * @return the specified attachment or null if no attachment found for id
183 */
184 public DataHandler getAttachment(String id) {
185 if (attachments != null && attachments.get(id) != null) {
186 return new DataHandler((DataSource) attachments.get(id));
187 }
188 return null;
189 }
190
191 /**
192 * @return a list of identifiers for atachments
193 */
194 public Iterator listAttachments() {
195 if (attachments != null) {
196 return attachments.keySet().iterator();
197 }
198 return Collections.EMPTY_LIST.iterator();
199 }
200
201 /**
202 * remove an identified attachment
203 *
204 * @param id
205 */
206 public void removeAttachment(String id) {
207 if (attachments != null) {
208 attachments.remove(id);
209 }
210 }
211
212 /**
213 * Returns a list of identifiers for each attachment to the message.
214 *
215 * @return iterator over String attachment identifiers
216 */
217 public Set getAttachmentNames() {
218 if (attachments != null) {
219 return Collections.unmodifiableSet(attachments.keySet());
220 }
221 return Collections.EMPTY_SET;
222 }
223
224 public String toString() {
225 return super.toString() + "{properties: " + getProperties() + "}";
226 }
227
228 // Scripting helper methods to add expressive power
229 // when using languages like Groovy, Velocity etc
230 // -------------------------------------------------------------------------
231
232 public Object getBody() throws MessagingException {
233 if (body == null) {
234 body = getMarshaler().unmarshal(exchange, this);
235 }
236 return body;
237 }
238
239 public Object getBody(PojoMarshaler marshaler) throws MessagingException {
240 return marshaler.unmarshal(exchange, this);
241 }
242
243 public Object getBody(org.apache.servicemix.jbi.marshaler.PojoMarshaler marshaler) throws MessagingException {
244 return marshaler.unmarshal(exchange, this);
245 }
246
247 public void setBody(Object body) throws MessagingException {
248 this.body = body;
249 }
250
251 public String getBodyText() throws TransformerException {
252 return TRANSFORMER.toString(getContent());
253 }
254
255 public void setBodyText(String xml) {
256 setContent(new StringSource(xml));
257 }
258
259 public PojoMarshaler getMarshaler() {
260 return exchange.getMarshaler();
261 }
262
263 public MessageExchange getExchange() {
264 return exchange;
265 }
266
267 public Fault createFault() throws MessagingException {
268 return getExchange().createFault();
269 }
270
271 // Implementation methods
272 // -------------------------------------------------------------------------
273 protected Map getProperties() {
274 if (properties == null) {
275 properties = createPropertiesMap();
276 }
277 return properties;
278 }
279
280 protected Map getAttachments() {
281 if (attachments == null) {
282 attachments = createAttachmentsMap();
283 }
284 return attachments;
285 }
286
287 protected void setAttachments(Map attachments) {
288 this.attachments = attachments;
289 }
290
291 protected void setProperties(Map properties) {
292 this.properties = properties;
293 }
294
295 protected Map createPropertiesMap() {
296 // Normalized exchanges do not need to be thread-safe
297 return new HashMap();
298 }
299
300 protected Map createAttachmentsMap() {
301 // Normalized exchanges do not need to be thread-safe
302 return new HashMap();
303 }
304
305 /**
306 * Write to a Stream
307 *
308 * @param out
309 * @throws IOException
310 */
311 public void writeExternal(ObjectOutput out) throws IOException {
312 try {
313 convertAttachments();
314 out.writeObject(attachments);
315 out.writeObject(properties);
316 String src = TRANSFORMER.toString(content);
317 out.writeObject(src);
318 // We have read the source
319 // so now, ensure that it can be re-read
320 if ((content instanceof StreamSource || content instanceof SAXSource) && !(content instanceof StringSource)
321 && !(content instanceof BytesSource) && !(content instanceof ResourceSource)) {
322 content = new StringSource(src);
323 }
324 out.writeObject(securitySubject);
325 } catch (TransformerException e) {
326 throw (IOException) new IOException("Could not transform content to string").initCause(e);
327 }
328 }
329
330
331
332 private void convertAttachments() throws IOException {
333 if (attachments != null) {
334 Map newAttachments = createAttachmentsMap();
335 for (Iterator it = attachments.keySet().iterator(); it.hasNext();) {
336 String name = (String) it.next();
337 DataSource ds = (DataSource) attachments.get(name);
338 if (ds instanceof ByteArrayDataSource) {
339 newAttachments.put(name, ds);
340 } else {
341 ByteArrayOutputStream baos = new ByteArrayOutputStream();
342 FileUtil.copyInputStream(ds.getInputStream(), baos);
343 ByteArrayDataSource bads = new ByteArrayDataSource(baos.toByteArray(), ds.getContentType());
344 bads.setName(ds.getName());
345 newAttachments.put(name, bads);
346 }
347 }
348 attachments = newAttachments;
349 }
350 }
351
352 /**
353 * Read from a stream
354 *
355 * @param in
356 * @throws IOException
357 * @throws ClassNotFoundException
358 */
359 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
360 attachments = (Map) in.readObject();
361 properties = (Map) in.readObject();
362 String src = (String) in.readObject();
363 if (src != null) {
364 content = new StringSource(src);
365 }
366 securitySubject = (Subject) in.readObject();
367 }
368
369 }