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.eip.patterns;
018    
019    import javax.jbi.messaging.ExchangeStatus;
020    import javax.jbi.messaging.InOnly;
021    import javax.jbi.messaging.InOut;
022    import javax.jbi.messaging.MessageExchange;
023    import javax.jbi.messaging.MessagingException;
024    import javax.jbi.messaging.NormalizedMessage;
025    import javax.jbi.messaging.RobustInOnly;
026    import javax.xml.namespace.QName;
027    import javax.xml.parsers.ParserConfigurationException;
028    import javax.xml.transform.Source;
029    import javax.xml.transform.dom.DOMSource;
030    
031    import org.w3c.dom.Document;
032    import org.w3c.dom.Element;
033    import org.w3c.dom.Node;
034    
035    import org.apache.servicemix.common.util.MessageUtil;
036    import org.apache.servicemix.eip.EIPEndpoint;
037    import org.apache.servicemix.eip.support.ExchangeTarget;
038    import org.apache.servicemix.jbi.jaxp.SourceTransformer;
039    
040    /**
041     * Implementation of the 
042     * <a href="http://www.enterpriseintegrationpatterns.com/DataEnricher.html">'Content-Enricher'</a> 
043     * Pattern. 
044     *  
045     * @org.apache.xbean.XBean element="content-enricher"
046     *                  description="A Content Enricher"
047     */
048    public class ContentEnricher extends EIPEndpoint {
049    
050        /**
051         * The address of the target endpoint
052         */
053        private ExchangeTarget target;
054    
055        /**
056         * the target to enrich the request
057         */
058        private ExchangeTarget enricherTarget;
059    
060        /**
061         * the QName of the resulting root node
062         */
063        private QName enricherElementName = new QName("enricher");
064    
065        /**
066         * the QName of the element which contains the 'IN Message'
067         * within the response message
068         */
069        private QName requestElementName = new QName("request");
070    
071        /**
072         * the QName of the element which contains the message 
073         * which was produced by the enricherTarget within the 
074         * response message
075         */
076        private QName resultElementName = new QName("result");
077    
078        /**
079         * Should message properties be copied ?
080         */
081        private boolean copyProperties;
082    
083        /**
084         * Should message attachments be copied ?
085         */
086        private boolean copyAttachments;
087    
088        /**
089         * returns the QName of the resulting root node
090         * @return QName of the resulting root node
091         */
092        public QName getEnricherElementName() {
093            return enricherElementName;
094        }
095    
096        /**
097         * Sets the QName of the resulting root node
098         * @param enricherElementName QName of the resulting root node
099         */
100        public void setEnricherElementName(QName enricherElementName) {
101            this.enricherElementName = enricherElementName;
102        }
103    
104        /**
105         * Returns the QName of the element which contains the 'IN Message'
106         * within the response message
107         * 
108         * @return QName 
109         */
110        public QName getRequestElementName() {
111            return requestElementName;
112        }
113    
114        /**
115         * Sets the QName of the element which contains the 'IN Message'
116         * within the response message
117         * 
118         * @param requestElementName QName
119         */
120        public void setRequestElementName(QName requestElementName) {
121            this.requestElementName = requestElementName;
122        }
123    
124        /**
125         * Returns the QName of the element which contains the message 
126         * which was produced by the enricherTarget within the 
127         * response message
128         * 
129         * @return QName
130         */
131        public QName getResultElementName() {
132            return resultElementName;
133        }
134    
135        /**
136         * Sets the QName of the element which contains the message 
137         * which was produced by the enricherTarget within the 
138         * response message
139         * 
140         * @param resultElementName QName
141         */
142        public void setResultElementName(QName resultElementName) {
143            this.resultElementName = resultElementName;
144        }
145    
146        public boolean isCopyProperties() {
147            return copyProperties;
148        }
149    
150        /**
151         * If this is set to <code>true</code>, message properties from the incoming exchange and the enricher exchange will be copied
152         * to the outgoing message exchange.  The default value is <code>false</code> (do not copy message properties).
153         *
154         * @param copyProperties 
155         */
156        public void setCopyProperties(boolean copyProperties) {
157            this.copyProperties = copyProperties;
158        }
159    
160        public boolean isCopyAttachments() {
161            return copyAttachments;
162        }
163    
164        /**
165         * If this is set to <code>true</code>, message attachments from the incoming exchange and the enricher exchange will be copied
166         * to the outgoing message exchange.  The default value is <code>false</code> (do not copy message atachments).
167         *
168         * @param copyAttachments
169         */
170        public void setCopyAttachments(boolean copyAttachments) {
171            this.copyAttachments = copyAttachments;
172        }
173    
174        protected void processAsync(MessageExchange exchange) throws Exception {
175            throw new IllegalStateException();
176        }
177    
178        protected void processSync(MessageExchange exchange) throws Exception {
179            throw new IllegalStateException();
180        }
181    
182        public void process(MessageExchange exchange) throws Exception {
183    
184            if (!(exchange instanceof InOnly) && !(exchange instanceof RobustInOnly)) {
185                fail(exchange, new UnsupportedOperationException("Use an InOnly or RobustInOnly MEP"));
186            }
187    
188            // Skip done exchanges
189            if (exchange.getStatus() == ExchangeStatus.DONE) {
190                return;
191                // Handle error exchanges
192            } else if (exchange.getStatus() == ExchangeStatus.ERROR) {
193                return;
194            }
195    
196            InOut enricherTargetME = getExchangeFactory().createInOutExchange();
197            enricherTarget.configureTarget(enricherTargetME, getContext());
198            MessageUtil.transferInToIn(exchange, enricherTargetME);
199    
200            sendSync(enricherTargetME);
201    
202            if (enricherTargetME.getStatus() == ExchangeStatus.ERROR) {
203                fail(exchange, enricherTargetME.getError());
204                return;
205            }
206    
207            Document document = combineToDOMDocument(exchange.getMessage("in"), enricherTargetME.getMessage("out"));
208    
209            done(enricherTargetME);
210    
211            MessageExchange outExchange = getExchangeFactory().createInOnlyExchange();
212            NormalizedMessage out = outExchange.createMessage();
213            target.configureTarget(outExchange, getContext());
214            out.setContent(new DOMSource(document));
215    
216            outExchange.setMessage(out, "in");
217    
218            if (copyProperties || copyAttachments) {
219                copyPropertiesAndAttachments(exchange.getMessage("in"), outExchange.getMessage("in"));
220                copyPropertiesAndAttachments(enricherTargetME.getMessage("out"), outExchange.getMessage("in"));
221            }
222            
223            sendSync(outExchange);
224            done(exchange);
225        }
226    
227        /**
228         * Combines two NormalizedMessages to one DOM Document. The
229         * element Names are specified via the following properties:
230         * enricherElementName, requestElementName, resultElementName
231         * 
232         * Example:
233         *    Content of Message1 :
234         *    
235         *        <hello/>
236         *        
237         *    Content of Message 2:
238         *    
239         *        <message2/>
240         * 
241         *    Result of this method a DOM Document containing the following:
242         *    
243         *         <enricher>
244         *           <request>
245         *             <hello/>
246         *           </request>
247         *           <result>
248         *             <message2/>
249         *           </result>
250         *         </enricher>
251         * 
252         */
253        private Document combineToDOMDocument(NormalizedMessage requestMessage, NormalizedMessage targetResultMessage)
254            throws Exception, ParserConfigurationException {
255    
256            Node originalDocumentNode = getDOMNode(requestMessage.getContent());
257            Node targetResultNode = getDOMNode(targetResultMessage.getContent());
258    
259            Document document = new SourceTransformer().createDocument();
260            Element enricherElement = createChildElement(enricherElementName, document);
261            Element requestElement = createChildElement(requestElementName, document);
262    
263            Node node = document.importNode(originalDocumentNode, true);
264            requestElement.appendChild(node);
265            enricherElement.appendChild(requestElement);
266            document.appendChild(enricherElement);
267    
268            Element resultElement = createChildElement(resultElementName, document);
269    
270            Node node2 = document.importNode(targetResultNode, true);
271    
272            resultElement.appendChild(node2);
273            enricherElement.appendChild(resultElement);
274            return document;
275    
276        }
277    
278        private Element createChildElement(QName name, Document document) {
279            Element elem;
280            if ("".equals(name.getNamespaceURI())) {
281                elem = document.createElement(name.getLocalPart());
282            } else {
283                elem = document.createElementNS(name.getNamespaceURI(), name.getPrefix() + ":" + name.getLocalPart());
284            }
285            return elem;
286        }
287    
288        private Node getDOMNode(Source source) throws Exception {
289            SourceTransformer sourceTransformer = new SourceTransformer();
290            Node node = sourceTransformer.toDOMNode(source);
291            if (node.getNodeType() == Node.DOCUMENT_NODE) {
292                node = ((Document) node).getDocumentElement();
293            }
294            return node;
295        }
296    
297        public void setTarget(ExchangeTarget target) {
298            this.target = target;
299        }
300    
301        public void setEnricherTarget(ExchangeTarget enricherTarget) {
302            this.enricherTarget = enricherTarget;
303        }
304    
305    
306        /**
307         * Copies properties and attachments from one message to another
308         * depending on the endpoint configuration
309         *
310         * @param from the message containing the properties and attachments
311         * @param to the destination message where the properties and attachments are set
312         */
313        private void copyPropertiesAndAttachments(NormalizedMessage from, NormalizedMessage to) throws MessagingException {
314            if (copyProperties) {
315                copyProperties(from, to);
316            }
317            if (copyAttachments) {
318                copyAttachments(from, to);
319            }
320        }
321    
322    }