View Javadoc
1   /*
2    * Copyright 2009 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *     
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.openehealth.ipf.commons.ihe.ws.cxf.payload;
17  
18  import com.ctc.wstx.io.UTF8Writer;
19  import com.ctc.wstx.sw.BaseStreamWriter;
20  import com.ctc.wstx.sw.BufferingXmlWriter;
21  import org.apache.cxf.interceptor.StaxOutInterceptor;
22  import org.apache.cxf.io.CacheAndWriteOutputStream;
23  import org.apache.cxf.message.Message;
24  import org.apache.cxf.phase.AbstractPhaseInterceptor;
25  import org.apache.cxf.phase.Phase;
26  
27  import javax.xml.stream.XMLStreamWriter;
28  import java.io.OutputStream;
29  import java.lang.reflect.Field;
30  
31  /**
32   * CXF interceptor that substitutes message output stream 
33   * with a special wrapper that collects SOAP payload.
34   *   
35   * @author Dmytro Rud
36   */
37  public class OutStreamSubstituteInterceptor extends AbstractPhaseInterceptor<Message> {
38  
39      private static final Field MWRITER_FIELD;
40      private static final Field MOUT_WRITER_FIELD;
41      private static final Field MOUT_STREAM_FIELD;
42      static {
43          try {
44              MWRITER_FIELD = BaseStreamWriter.class.getDeclaredField("mWriter");
45              MWRITER_FIELD.setAccessible(true);
46              MOUT_WRITER_FIELD = BufferingXmlWriter.class.getDeclaredField("mOut");
47              MOUT_WRITER_FIELD.setAccessible(true);
48              MOUT_STREAM_FIELD = UTF8Writer.class.getDeclaredField("mOut");
49              MOUT_STREAM_FIELD.setAccessible(true);
50          } catch (NoSuchFieldException e) {
51              throw new IllegalStateException(e);
52          }
53      }
54  
55      public OutStreamSubstituteInterceptor() {
56          super(Phase.PRE_STREAM);
57          addAfter(StaxOutInterceptor.class.getName());
58      }
59      
60      @Override
61      public void handleMessage(Message message) {
62          try {
63              boolean success = false;
64              Object x = message.getContent(XMLStreamWriter.class);
65              if (x instanceof BaseStreamWriter) {
66                  x = MWRITER_FIELD.get(x);
67                  if (x instanceof BufferingXmlWriter) {
68                      x = MOUT_WRITER_FIELD.get(x);
69                      if (x instanceof UTF8Writer) {
70                          UTF8Writer writer = (UTF8Writer) x;
71                          x = MOUT_STREAM_FIELD.get(writer);
72                          if (x instanceof OutputStream) {
73                              OutputStream os = (OutputStream) x;
74                              WrappedOutputStream wrapper = new WrappedOutputStream(os, (String) message.get(Message.ENCODING));
75                              message.setContent(OutputStream.class, wrapper);
76                              MOUT_STREAM_FIELD.set(writer, wrapper);
77                              success = true;
78                          }
79                      }
80                  }
81              }
82              if (!success) {
83                  throw new IllegalStateException("Unable to wrap the output stream, check involved classes");
84              }
85          } catch (IllegalAccessException e) {
86              throw new IllegalStateException(e);
87          }
88      }
89  
90  
91      /**
92       * Retrieves the instance of stream wrapper installed by this interceptor.
93       * @param message
94       *      CXF message which contains output stream as one of content types.
95       * @return
96       *      an instance of {@link WrappedOutputStream}.
97       * @throws IllegalStateException
98       *      when the stream wrapper instance could not be retrieved.
99       */
100     public static WrappedOutputStream getStreamWrapper(Message message) {
101         OutputStream outputStream =  message.getContent(OutputStream.class);
102         if (outputStream instanceof CacheAndWriteOutputStream) {
103             // Extract what we need from the wrapper added by CXF. CXF sometimes adds the wrapper for diagnostics.
104             outputStream = ((CacheAndWriteOutputStream) outputStream).getFlowThroughStream();
105         }
106         if (outputStream instanceof WrappedOutputStream) {
107             return (WrappedOutputStream) outputStream;
108         } else {
109             throw new IllegalStateException("Message output stream is not of expected type");
110         }
111     }
112 
113 }