View Javadoc
1   /*
2    * Copyright 2012 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 org.apache.cxf.message.Message;
19  import org.apache.cxf.message.MessageUtils;
20  import org.apache.cxf.transport.http.AbstractHTTPDestination;
21  import org.openehealth.ipf.commons.ihe.core.payload.PayloadLoggerBase;
22  import org.openehealth.ipf.commons.ihe.core.payload.PayloadLoggingContext;
23  import org.openehealth.ipf.commons.ihe.ws.InterceptorUtils;
24  
25  import javax.servlet.http.HttpServletResponse;
26  import java.util.List;
27  import java.util.Map;
28  
29  import static org.openehealth.ipf.commons.ihe.ws.cxf.payload.StringPayloadHolder.PayloadType.HTTP;
30  
31  
32  /**
33   * Base class for CXF interceptors which store incoming and outgoing HTTP payload
34   * into files with user-defined name patterns.
35   * <p/>
36   * It introduces an additional parameter available in SPEL file name expressions:
37   * <ul>
38   * <li><tt>partialResponse</tt>&nbsp;&mdash; returns <code>true</code>,
39   * when the message under consideration is a WS-Addressing partial response.
40   * </ul>
41   *
42   * @author Dmytro Rud
43   */
44  public class WsPayloadLoggerBase
45          extends PayloadLoggerBase<WsPayloadLoggerBase.WsPayloadLoggingContext> {
46  
47  
48      public void logPayload(Message message) {
49          Long sequenceId = InterceptorUtils.findContextualProperty(message, SEQUENCE_ID_PROPERTY_NAME);
50          if (sequenceId == null) {
51              sequenceId = getNextSequenceId();
52              message.getExchange().put(SEQUENCE_ID_PROPERTY_NAME, sequenceId);
53          }
54  
55          WsPayloadLoggingContext spelContext = new WsPayloadLoggingContext(
56                  sequenceId,
57                  Boolean.TRUE.equals(message.get(Message.PARTIAL_RESPONSE_MESSAGE)));
58  
59          boolean isOutbound = MessageUtils.isOutbound(message);
60  
61          doLogPayload(
62                  spelContext,
63                  (String) message.get(Message.ENCODING),
64                  isOutbound ? getOutboundMetadataPayload(message) : getInboundMetadataPayload(message),
65                  getHeadersPayload(message),
66                  isOutbound ? getOutboundBodyPayload(message) : getInboundBodyPayload(message));
67      }
68  
69  
70      /**
71       * Appends generic HTTP headers to the given String builder.
72       *
73       * @param message CXF message which contains the headers.
74       * @return message's HTTP headers as a String.
75       */
76      private static String getHeadersPayload(Message message) {
77          StringBuilder sb = new StringBuilder();
78  
79          Object encoding = message.get(Message.ENCODING);
80          if (encoding != null) {
81              sb.append("Character set: ").append(encoding).append('\n');
82          }
83          sb.append('\n');
84  
85          Map<String, List<String>> httpHeaders = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
86          if (httpHeaders != null) {
87              for (Map.Entry<String, List<String>> entry : httpHeaders.entrySet()) {
88                  for (String header : entry.getValue()) {
89                      sb.append(entry.getKey()).append(": ").append(header).append('\n');
90                  }
91              }
92              sb.append('\n');
93          }
94  
95          return sb.toString();
96      }
97  
98  
99      private static String getInboundMetadataPayload(Message message) {
100         StringBuilder sb = new StringBuilder();
101 
102         if (Boolean.TRUE.equals(message.get(Message.REQUESTOR_ROLE))) {
103             sb.append("HTTP response code: ").append(message.get(Message.RESPONSE_CODE)).append('\n');
104         } else {
105             sb.append("HTTP request: ")
106                     .append(message.get(Message.HTTP_REQUEST_METHOD))
107                     .append(' ')
108                     .append(message.get(Message.REQUEST_URL))
109                     .append('\n');
110         }
111 
112         return sb.toString();
113     }
114 
115 
116     private static String getInboundBodyPayload(Message message) {
117         StringPayloadHolder payloadHolder = message.getContent(StringPayloadHolder.class);
118         return (payloadHolder != null) ? payloadHolder.get(HTTP) : "";
119     }
120 
121 
122     private static String getOutboundMetadataPayload(Message message) {
123         StringBuilder sb = new StringBuilder();
124 
125         Object endpointAddress = message.get(Message.ENDPOINT_ADDRESS);
126         if (endpointAddress != null) {
127             sb.append("Target endpoint: ").append(endpointAddress).append('\n');
128         } else {
129             HttpServletResponse response =
130                     (HttpServletResponse) message.get(AbstractHTTPDestination.HTTP_RESPONSE);
131             sb.append("HTTP response code: ")
132                     .append((response != null) ? response.getStatus() : "unknown")
133                     .append('\n');
134         }
135 
136         return sb.toString();
137     }
138 
139 
140     private static String getOutboundBodyPayload(Message message) {
141         WrappedOutputStream wrapper = OutStreamSubstituteInterceptor.getStreamWrapper(message);
142         wrapper.deactivate();
143         return wrapper.getCollectedPayload();
144     }
145 
146 
147     /**
148      * SPEL evaluation context for patterns of file names where WS-based payload should be saved.
149      */
150     static class WsPayloadLoggingContext extends PayloadLoggingContext {
151         private final boolean partialResponse;
152 
153         WsPayloadLoggingContext(long sequenceId, boolean partialResponse) {
154             super(sequenceId);
155             this.partialResponse = partialResponse;
156         }
157 
158         public boolean isPartialResponse() {
159             return partialResponse;
160         }
161     }
162 
163 }