View Javadoc

1   /*
2    * $Id$
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.cxf.support;
12  
13  import org.mule.api.MuleMessage;
14  import org.mule.api.transformer.TransformerException;
15  import org.mule.module.xml.transformer.DelayedResult;
16  import org.mule.transformer.types.DataTypeFactory;
17  import org.mule.transport.NullPayload;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.Comparator;
22  import java.util.LinkedList;
23  import java.util.List;
24  
25  import javax.xml.stream.XMLStreamException;
26  import javax.xml.stream.XMLStreamReader;
27  import javax.xml.stream.XMLStreamWriter;
28  import javax.xml.transform.sax.SAXResult;
29  
30  import javanet.staxutils.ContentHandlerToXMLStreamWriter;
31  import org.apache.cxf.databinding.stax.XMLStreamWriterCallback;
32  import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
33  import org.apache.cxf.interceptor.Fault;
34  import org.apache.cxf.message.Message;
35  import org.apache.cxf.message.MessageContentsList;
36  import org.apache.cxf.phase.Phase;
37  import org.apache.cxf.service.model.BindingOperationInfo;
38  import org.apache.cxf.service.model.MessagePartInfo;
39  import org.apache.cxf.staxutils.StaxUtils;
40  import org.xml.sax.SAXException;
41  
42  public class OutputPayloadInterceptor extends AbstractOutDatabindingInterceptor
43  {
44  
45      public OutputPayloadInterceptor()
46      {
47          super(Phase.PRE_LOGICAL);
48      }
49  
50      @SuppressWarnings("unchecked")
51      public void handleMessage(Message message) throws Fault
52      {
53          MessageContentsList objs = MessageContentsList.getContentsList(message);
54          if (objs == null || objs.size() == 0)
55          {
56              return;
57          }
58  
59          List<Object> originalParts = (List<Object>) objs.clone();
60  
61          objs.clear();
62  
63          for (Object o : originalParts)
64          {
65              if (o instanceof MuleMessage) 
66              {
67                  try
68                  {
69                      MuleMessage muleMsg = (MuleMessage) o;
70                      final Object payload = cleanUpPayload(muleMsg.getPayload());
71                      
72                      if (payload instanceof DelayedResult)
73                      {
74                          o = getDelayedResultCallback((DelayedResult)payload);
75                      }
76                      else if (payload instanceof XMLStreamReader)
77                      {
78                          o = new XMLStreamWriterCallback()
79                          {
80                              public void write(XMLStreamWriter writer) throws Fault, XMLStreamException
81                              {
82                                  XMLStreamReader xsr = (XMLStreamReader)payload;
83                                  StaxUtils.copy(xsr, writer);      
84                                  writer.flush();
85                                  xsr.close();
86                              }
87                          };
88                      } 
89                      else if (payload instanceof NullPayload)
90                      {
91                          break;
92                      }
93                      else
94                      {
95                          o = muleMsg.getPayload(DataTypeFactory.create(XMLStreamReader.class));
96                      }
97      
98                      objs.add(o);
99                  }
100                 catch (TransformerException e)
101                 {
102                     throw new Fault(e);
103                 } 
104             }
105             else
106             {
107                 // it's probably a null object
108                 objs.add(o);
109             }
110         }
111 
112         // For the server side response, ensure that the body object is in the correct
113         // location when running in proxy mode
114         if (!isRequestor(message))
115         {
116             BindingOperationInfo bop = message.getExchange().get(BindingOperationInfo.class);
117             if (bop != null)
118             {
119                 ensurePartIndexMatchListIndex(objs, bop.getOutput().getMessageParts());
120             }
121         }
122     }
123 
124     /**
125      * Ensures that each part's content is in the right place in the content list.
126      * <p/>
127      * This is required because in some scenarios there are parts that were removed from
128      * the part list. In that cases, the content list contains only the values for the
129      * remaining parts, but the part's indexes could be wrong. This method fixes that
130      * adding null values into the content list so the part's index matches the contentList
131      * index. (Related to: MULE-5113.)
132      */
133     protected void ensurePartIndexMatchListIndex(MessageContentsList contentList, List<MessagePartInfo> parts)
134     {
135 
136         // In some circumstances, parts is a {@link UnmodifiableList} instance, so a new copy
137         // is required in order to sort its content.
138         List<MessagePartInfo> sortedParts = new LinkedList<MessagePartInfo>();
139         sortedParts.addAll(parts);
140         sortPartsByIndex(sortedParts);
141 
142         int currentIndex = 0;
143 
144         for (MessagePartInfo part : sortedParts)
145         {
146             while (part.getIndex() > currentIndex)
147             {
148                 contentList.add(currentIndex++, null);
149             }
150 
151             // Skips the index for the current part because now is in the right place
152             currentIndex = part.getIndex() + 1;
153         }
154     }
155 
156     private void sortPartsByIndex(List<MessagePartInfo> parts)
157     {
158         Collections.sort(parts, new Comparator<MessagePartInfo>()
159         {
160 
161             public int compare(MessagePartInfo o1, MessagePartInfo o2)
162             {
163                 if (o1.getIndex() < o2.getIndex())
164                 {
165                     return -1;
166                 }
167                 else if (o1.getIndex() == o2.getIndex())
168                 {
169                     return 0;
170                 }
171                 else
172                 {
173                     return 1;
174                 }
175             }
176         });
177     }
178 
179     protected Object cleanUpPayload(final Object payload)
180     {
181         final Object cleanedUpPayload;
182         if (payload instanceof Object[])
183         {
184             final Object[] payloadArray = (Object[]) payload;
185             final List<Object> payloadList = new ArrayList<Object>(payloadArray.length);
186             for (Object object : payloadArray)
187             {
188                 if (object != null && object != MessageContentsList.REMOVED_MARKER)
189                 {
190                     payloadList.add(object);
191                 }
192             }
193             if (payloadList.size() == payloadArray.length)
194             {
195                 cleanedUpPayload = payload; // no cleanup was done
196             }
197             else
198             {
199                 cleanedUpPayload = payloadList.size() == 1 ? payloadList.get(0) : payloadList.toArray();
200             }
201         }
202         else
203         {
204             cleanedUpPayload = payload;
205         }
206         return cleanedUpPayload;
207     }
208 
209     protected Object getDelayedResultCallback(final DelayedResult r)
210     {
211         return new XMLStreamWriterCallback()
212         {
213             public void write(XMLStreamWriter writer) throws Fault, XMLStreamException
214             {
215                 ContentHandlerToXMLStreamWriter handler = new ContentHandlerToXMLStreamWriter(writer) {
216 
217                     @Override
218                     public void endDocument() throws SAXException
219                     {
220                     }
221 
222                     @Override
223                     public void processingInstruction(String target, String data) throws SAXException
224                     {
225                     }
226 
227                     @Override
228                     public void startDocument() throws SAXException
229                     {
230                     }
231 
232                     @Override
233                     public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
234                     {
235                     }
236                     
237                 };
238                 
239                 try
240                 {
241                     r.write(new SAXResult(handler));
242                 }
243                 catch (Exception e)
244                 {
245                     throw new Fault(e);
246                 }
247             }
248         };
249     }
250 
251 }