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    }