View Javadoc
1   /*
2    * Copyright 2010 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.audit;
17  
18  import org.openehealth.ipf.commons.audit.AuditContext;
19  import org.openehealth.ipf.commons.audit.codes.EventOutcomeIndicator;
20  import org.openehealth.ipf.commons.ihe.core.atna.AuditStrategy;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  import org.apache.cxf.binding.soap.SoapMessage;
24  import org.apache.cxf.headers.Header;
25  import org.apache.cxf.interceptor.ServiceInvokerInterceptor;
26  import org.apache.cxf.message.Exchange;
27  import org.apache.cxf.message.MessageUtils;
28  import org.apache.cxf.phase.Phase;
29  import org.apache.cxf.ws.addressing.VersionTransformer;
30  import org.openehealth.ipf.commons.ihe.ws.correlation.AsynchronyCorrelator;
31  import org.openehealth.ipf.commons.ihe.ws.cxf.payload.InPayloadInjectorInterceptor;
32  import org.w3c.dom.Element;
33  
34  
35  /**
36   * CXF interceptor for ATNA auditing in WS-based IHE transactions with
37   * WSA asynchrony support.  Handles <b>response</b> messages.
38   * Usable on client, server, and asynchronous response receiver sides.
39   * 
40   * @author Dmytro Rud
41   */
42  public class AuditResponseInterceptor<T extends WsAuditDataset> extends AbstractAuditInterceptor<T> {
43      private static final transient Logger LOG = LoggerFactory.getLogger(AuditResponseInterceptor.class);
44  
45      private final AsynchronyCorrelator<T> correlator;
46      private final boolean asyncReceiver;
47      private final boolean serverSide;
48      
49      /**
50       * Constructor.
51       * 
52       * @param auditStrategy
53       *      an audit strategy instance.
54       * @param serverSide
55       *      whether this interceptor is being used on the server side 
56       *      (<code>true</code>) or on the client side (<code>false</code>).
57       *      Server side is where the response is generated.
58       * @param correlator
59       *      correlator for asynchronous messages (<code>null</code> on server side).
60       * @param asyncReceiver
61       *      <code>true</code> when this interceptor is installed
62       *      on the asynchronous receiver side. 
63       */
64      public AuditResponseInterceptor(
65              AuditStrategy<T> auditStrategy,
66              AuditContext auditContext,
67              boolean serverSide,
68              AsynchronyCorrelator<T> correlator,
69              boolean asyncReceiver) 
70      {
71          super(isClient(asyncReceiver, serverSide) ? Phase.INVOKE : Phase.PREPARE_SEND, auditStrategy, auditContext);
72          if (isClient(asyncReceiver, serverSide)) {
73              addAfter(InPayloadInjectorInterceptor.class.getName());
74              addBefore(ServiceInvokerInterceptor.class.getName());
75          }
76          this.correlator = correlator;
77          this.serverSide = serverSide;
78          this.asyncReceiver = asyncReceiver;
79      }
80      
81      
82      /**
83       * Returns <code>true</code> when the combination of parameters does mean
84       * that this interceptor instance is deployed on the client side, i.e.
85       * either on producer or on asynchronous response receiver.
86       */
87      private static boolean isClient(boolean asyncReceiver, boolean serverSide) {
88          return (asyncReceiver || (! serverSide));
89      }
90      
91  
92      @Override
93      protected void process(SoapMessage message) {
94          if (isGET(message)) {
95              return;
96          }
97  
98          // partial responses are for us out of interest
99          if (MessageUtils.isPartialResponse(message)) {
100             return;
101         }
102 
103         // check whether the response is relevant for ATNA audit finalization
104         Object response = extractPojo(message);
105         AuditStrategy<T> auditStrategy = getAuditStrategy();
106         if (! auditStrategy.isAuditableResponse(response)) {
107             return;
108         }
109 
110         T auditDataset = null;
111 
112         // try to get the audit dataset from the asynchrony correlator --
113         // will work only when we are on asynchronous receiver, and the WSA
114         // RelatesTo header has been properly initialized, and the dataset
115         // has not been purged from the asynchrony correlator yet.
116         if (asyncReceiver) {
117             String messageId = null;
118             for (Header header : message.getHeaders()) {
119                 if ("RelatesTo".equals(header.getName().getLocalPart())
120                         && VersionTransformer.isSupported(header.getName().getNamespaceURI()))
121                 {
122                     messageId = ((Element) header.getObject()).getTextContent();
123                     break;
124                 }
125             }
126             if (messageId != null) {
127                 auditDataset = correlator.getAuditDataset(messageId);
128                 // message.getExchange().put(CXF_EXCHANGE_KEY, auditDataset);
129             } else {
130                 LOG.error("Cannot determine WSA message ID");
131             }
132         }
133         if (auditDataset == null) {
134             auditDataset = getAuditDataset(message);
135         }
136 
137         // extract user ID from WSA "To" header (not "ReplyTo" due to direction inversion!)
138         extractUserIdFromWSAddressing(
139                 message,
140                 isClient(asyncReceiver, serverSide),
141                 serverSide, 
142                 auditDataset);
143 
144         // check whether the response POJO is available and
145         // perform transaction-specific enrichment of the audit dataset
146         Exchange exchange = message.getExchange();
147         if ((message == exchange.getInFaultMessage())
148                 || (message == exchange.getOutFaultMessage())
149                 || (response == null))
150         {
151             auditDataset.setEventOutcomeIndicator(EventOutcomeIndicator.SeriousFailure);
152         } else {
153             auditStrategy.enrichAuditDatasetFromResponse(auditDataset, response, getAuditContext());
154         }
155         
156         // perform transaction-specific auditing
157         auditStrategy.doAudit(getAuditContext(), auditDataset);
158     }
159     
160 }