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