View Javadoc

1   /*
2    * Copyright  1999-2004 The Apache Software Foundation.
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   */
17  
18  package org.apache.axis.message.addressing.handler;
19  
20  import org.apache.axis.AxisFault;
21  import org.apache.axis.Message;
22  import org.apache.axis.MessageContext;
23  import org.apache.axis.client.Call;
24  import org.apache.axis.client.Service;
25  import org.apache.axis.message.SOAPEnvelope;
26  import org.apache.axis.message.SOAPBodyElement;
27  import org.apache.axis.message.RPCElement;
28  import org.apache.axis.description.OperationDesc;
29  import org.apache.axis.components.uuid.UUIDGen;
30  import org.apache.axis.components.uuid.UUIDGenFactory;
31  import org.apache.axis.handlers.BasicHandler;
32  import org.apache.axis.message.addressing.Action;
33  import org.apache.axis.message.addressing.AttributedURI;
34  import org.apache.axis.message.addressing.AddressingHeaders;
35  import org.apache.axis.message.addressing.Constants;
36  import org.apache.axis.message.addressing.EndpointReference;
37  import org.apache.axis.message.addressing.EndpointReferenceType;
38  import org.apache.axis.message.addressing.MessageID;
39  import org.apache.axis.message.addressing.To;
40  import org.apache.axis.message.addressing.util.AddressingUtils;
41  import org.apache.axis.types.URI;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  import javax.xml.namespace.QName;
47  import java.util.ArrayList;
48  import java.util.List;
49  import java.util.StringTokenizer;
50  
51  /***
52   * Class AddressingHandler
53   *
54   * @author Davanum Srinivas (dims@yahoo.com)
55   */
56  public class AddressingHandler extends BasicHandler {
57  
58      protected static Log log =
59          LogFactory.getLog(AddressingHandler.class.getName());
60  
61      // let's reuse uuid generator
62      private static UUIDGen uuidGen = UUIDGenFactory.getUUIDGen();
63  
64      // A list of QNames of reference properties specified by the
65      // deployer that need to be handled.
66      // If the ignore flag (above) is set to false, and this list is
67      // null - we process all headers
68      private List refPropQNames;
69      
70      private String actor;
71  
72      private boolean removeHeaders = false;
73      
74      public AddressingHandler() {}
75      
76      /***
77       * Initialize the addressing handler.
78       */
79      public void init() {
80          super.init();
81          initializeReferencePropertyNames();
82          initializeRemoveHeaders();
83          initializeActor();
84      }
85      
86      protected void initializeActor() {
87          actor = (String) getOption("actor");
88      }
89      
90      private void initializeRemoveHeaders() {
91          String property = (String) getOption("removeHeaders");
92          this.removeHeaders = "true".equalsIgnoreCase(property);
93      }
94  
95      /***
96       * Method invoke
97       *
98       * @param msgContext
99       * @throws AxisFault
100      */
101     public void invoke(MessageContext msgContext) throws AxisFault {
102         boolean setMustUnderstand = 
103             msgContext.isPropertyTrue(Constants.ENV_ADDRESSING_SET_MUST_UNDERSTAND);
104 
105         try {
106             if (msgContext.isClient()) {
107                 if (!msgContext.getPastPivot()) {
108                     processClientRequest(msgContext, setMustUnderstand);
109                 } else {
110                     processClientResponse(msgContext);
111                 }
112             } else {
113                 if (!msgContext.getPastPivot()) {
114                     processServerRequest(msgContext);
115                 } else {
116                     processServerResponse(msgContext, setMustUnderstand);
117                 }
118             }
119         } catch (Exception e) {
120             log.error("Exception in AddressingHandler", e);
121             throw AxisFault.makeFault(e);
122         }
123     }
124 
125     public void onFault(MessageContext msgContext) {
126         if (msgContext.isClient()) {
127             return;
128         }
129         try {
130             processFault(msgContext);
131         } catch (Exception e) {
132             log.error("Exception in AddressingHandler", e);
133         }
134     }
135 
136     protected void processFault(MessageContext msgContext)
137         throws Exception {
138         AddressingHeaders reqHeaders =
139             (AddressingHeaders) msgContext.getProperty(
140                   Constants.ENV_ADDRESSING_REQUEST_HEADERS
141              );
142 
143         if (reqHeaders == null) {
144             // error?
145             return;
146         }
147 
148         AddressingHeaders resHeaders = 
149             AddressingUtils.getResponseHeaders(msgContext);
150 
151         // set From
152         EndpointReference fromEPR = resHeaders.getFrom();
153         if (fromEPR == null) {
154             To toURI = reqHeaders.getTo();
155             if (toURI != null) {
156                 fromEPR = new EndpointReference(toURI);
157                 fromEPR.setProperties(reqHeaders.getReferenceProperties());
158                 resHeaders.setFrom(fromEPR);
159             }
160         }
161 
162         // set action for fault only with 04 spec. 03 spec unclear on that issue
163         if (Constants.NS_URI_ADDRESSING_DEFAULT.equals(Constants.NS_URI_ADDRESSING_2004_03)) {
164             resHeaders.setAction(new Action(new URI(Constants.FAULT_ACTION)));
165         }
166 
167         MessageID msgID = null;
168 
169         // process RelatesTo
170         msgID = reqHeaders.getMessageID();
171         if (msgID != null) {
172             resHeaders.addRelatesTo(msgID.toString(),
173                                     Constants.QNAME_RESPONSE);
174         }
175 
176         // process MessageID
177         msgID = new MessageID(new URI("uuid:" + uuidGen.nextUUID()));
178         resHeaders.setMessageID(msgID);
179 
180         // now put all headers into soap env.
181         Message msg = msgContext.getCurrentMessage();
182         if (msg == null) {
183             return;
184         }
185 
186         resHeaders.toEnvelope(msg.getSOAPEnvelope());
187 
188         // process FaultTo
189         EndpointReferenceType faultTo = reqHeaders.getFaultTo();
190         if (faultTo != null) {
191             AttributedURI address = faultTo.getAddress();
192             if (address != null) {
193                 String uri = address.toString();
194                 if (uri != null) {
195                     // send the msg to fault to
196                     forwardMessage(faultTo, msg);
197                     // Somehow make the response empty, or create a new empty
198                     // response
199                     msgContext.setCurrentMessage(null);
200                 }
201             }
202         }
203     }
204 
205     /***
206      * Method processClientRequest
207      *
208      * @param msgContext
209      */
210     protected void processClientRequest(MessageContext msgContext,
211                                         boolean setMustUnderstand)
212         throws Exception {
213 
214         AddressingHeaders sharedHeaders =
215             (AddressingHeaders) msgContext.getProperty(
216                   Constants.ENV_ADDRESSING_SHARED_HEADERS
217             );
218 
219         AddressingHeaders headers = 
220             AddressingUtils.getRequestHeaders(msgContext);
221 
222         headers.setSetMustUnderstand(setMustUnderstand);
223 
224         // set MessageID 
225         if (headers.getMessageID() == null) {
226             MessageID id = 
227                 new MessageID(new URI("uuid:" + uuidGen.nextUUID()));
228             headers.setMessageID(id);
229         }
230 
231         // set To
232         To to = headers.getTo();
233         if (to == null) {
234             if (sharedHeaders != null) {
235                 // To is always set in shared headers
236                 headers.setTo(sharedHeaders.getTo());
237             } else {
238                 headers.setTo(new To(msgContext.getStrProp(MessageContext.TRANS_URL)));
239             }
240         }
241         
242         // set Action
243         String action = msgContext.getSOAPActionURI();
244         if (action != null) {
245             headers.setAction(new Action(new URI(action)));
246         } else if(headers.getAction() != null) {
247             msgContext.setUseSOAPAction(true);
248             // Make SOAP action match
249             msgContext.setSOAPActionURI(headers.getAction().toString());
250         }
251         
252         // set From
253         if (headers.getFrom() == null) {
254             String from = 
255                 msgContext.getStrProp(Constants.ENV_ADDRESSING_FROM_URI);
256             if (from != null) {
257                 headers.setFrom(new EndpointReference(from));
258             } else {
259                 headers.setFrom(new EndpointReference(
260                                      Constants.NS_URI_ANONYMOUS));
261             }
262         }
263 
264         // set ReplyTo
265         if (msgContext.isPropertyTrue(Constants.ENV_ADDRESSING_SEND_REPLYTO)) {
266             if (headers.getReplyTo() == null) {
267                 String replyTo = msgContext.getStrProp(Constants.ENV_ADDRESSING_REPLYTO_URI);
268                 if (replyTo != null) {
269                     headers.setReplyTo(new EndpointReference(replyTo));
270                 } else {
271                     headers.setReplyTo(headers.getFrom());
272                 }
273             }
274         }
275         
276         // set FaultTo
277         if (headers.getFaultTo() == null) {
278             String faultTo = 
279                 msgContext.getStrProp(Constants.ENV_ADDRESSING_FAULTTO_URI);
280             if (faultTo != null) {
281                 headers.setFaultTo(new EndpointReference(faultTo));
282             }
283         }
284 
285         if (sharedHeaders != null) {
286             headers.setReferenceProperties(sharedHeaders.getReferenceProperties());
287         }
288         Message msg = msgContext.getRequestMessage();
289         headers.toEnvelope(msg.getSOAPEnvelope(), this.actor);
290     }
291 
292     /***
293      * Method processClientResponse
294      *
295      * @param msgContext
296      */
297     protected void processClientResponse(MessageContext msgContext)
298         throws Exception {
299         // if no response - do nothing
300         Message msg = msgContext.getResponseMessage();
301         if (msg == null) {
302             return;
303         }
304 
305         AddressingHeaders headers = 
306             new AddressingHeaders(msg.getSOAPEnvelope(), 
307                                   this.actor, 
308                                   true,
309                                   this.removeHeaders, 
310                                   false, 
311                                   this.refPropQNames);
312         msgContext.setProperty(Constants.ENV_ADDRESSING_RESPONSE_HEADERS,
313                                headers);
314     }
315 
316     /***
317      * Method processServerRequest
318      *
319      * @param msgContext
320      * @throws Exception
321      */
322     protected void processServerRequest(MessageContext msgContext)
323         throws Exception {
324         Message msg = msgContext.getRequestMessage();
325         if (msg == null) {
326             return;
327         }
328         AddressingHeaders headers =
329             new AddressingHeaders(msg.getSOAPEnvelope(),
330                                   this.actor, 
331                                   true,
332                                   this.removeHeaders,
333                                   false,
334                                   this.refPropQNames);
335 
336         if (headers.getTo()==null)
337         {
338             // not a WS-A request; let it pass thru w/out doing anything
339             return;
340         }
341         if (headers.getAction()==null)
342         {
343             // should we throw a SOAPFaultException here?
344             log.debug("WS-A request to endpoint " + headers.getTo() + " is missing the required wsa:Action header.");
345         }
346 
347         msgContext.setProperty(Constants.ENV_ADDRESSING_REQUEST_HEADERS,
348                                headers);
349 
350         // set the target service based on To header if it hasn't already
351         // been determined.  NOTE: May want to add an option to override
352         // later.
353         if (msgContext.getService() == null && 
354             msgContext.getTargetService() == null) {
355             setTargetService(msgContext, headers);
356             
357             if (msgContext.getService() != null) {
358                 resetOperations(msgContext);
359             }
360         }
361     }
362     
363     protected void resetOperations(MessageContext msgContext) 
364         throws AxisFault {
365         resetContextOperations(msgContext);
366     }
367     
368     // Reinitializes the RPCElement with right operations
369     // so that things are nicely deserialized
370     public static void resetContextOperations(MessageContext msgContext) 
371         throws AxisFault {
372         Message msg = msgContext.getCurrentMessage();
373         if (msg == null) {
374             return;
375         }
376         SOAPEnvelope env = msg.getSOAPEnvelope();
377         if (env == null) {
378             return;
379         }
380         SOAPBodyElement bodyElement = env.getFirstBody();
381         if (bodyElement != null && bodyElement instanceof RPCElement) {
382             RPCElement element = (RPCElement)bodyElement;
383             // update the operations in RPCElement
384             element.updateOperationsByQName();
385             OperationDesc [] operations = element.getOperations();
386             // and set operation if appropriate
387             if (operations == null) {
388                 element.updateOperationsByName();
389             } else if (operations.length == 1) {
390                 msgContext.setOperation(operations[0]);
391             }
392         } else {
393             msg.getSOAPPartAsString();
394         }
395     }
396     
397     /***
398      * Can be overridden by subclasses to customize
399      * how the wsa:to header is interpreted
400      */
401     protected void setTargetService(MessageContext msgContext,
402                                     AddressingHeaders headers)
403         throws Exception {
404         To toURI = headers.getTo();
405         if (toURI == null) {
406             return;
407         }
408         String to = toURI.getPath();
409         if (to == null) {
410             return;
411         }
412         // set the target service
413         int i = to.lastIndexOf('/');
414         msgContext.setTargetService(to.substring(i + 1));
415     }
416 
417     /***
418      * Method processServerResponse
419      *
420      * @param msgContext
421      */
422     protected void processServerResponse(MessageContext msgContext,
423                                          boolean setMustUnderstand)
424         throws Exception {
425         // if no response - do nothing
426         Message msg = msgContext.getResponseMessage();
427         if (msg == null) {
428             return;
429         }
430 
431         AddressingHeaders reqHeaders =
432             (AddressingHeaders) msgContext.getProperty(
433                   Constants.ENV_ADDRESSING_REQUEST_HEADERS
434              );
435 
436         if (reqHeaders == null) {
437             // error?
438             return;
439         }
440 
441         AddressingHeaders resHeaders = 
442             AddressingUtils.getResponseHeaders(msgContext);
443 
444         resHeaders.setSetMustUnderstand(setMustUnderstand);
445 
446         // set From
447         EndpointReference fromEPR = resHeaders.getFrom();
448         if (fromEPR == null) {
449             To toURI = reqHeaders.getTo();
450             if (toURI != null) {
451                 fromEPR = new EndpointReference(toURI);
452                 fromEPR.setProperties(reqHeaders.getReferenceProperties());
453                 resHeaders.setFrom(fromEPR);
454             }
455         }
456 
457         // set Action
458         Action action = resHeaders.getAction();
459         if (action == null) {
460             // not set - try request headers
461             action = reqHeaders.getAction();
462             if (action != null) {
463                 resHeaders.setAction(new Action(new URI(action.toString() +
464                                                         "Response")));
465             }
466         }
467 
468         if (resHeaders.getFrom() == null && reqHeaders.getFrom() != null) {
469             resHeaders.setTo(reqHeaders.getFrom().getAddress());
470         } else {
471             resHeaders.setTo(new To(Constants.NS_URI_ANONYMOUS));
472         }
473 
474         MessageID msgID = null;
475 
476         // process RelatesTo
477         msgID = reqHeaders.getMessageID();
478         if (msgID != null) {
479             resHeaders.addRelatesTo(msgID.toString(),
480                                     Constants.QNAME_RESPONSE);
481         }
482 
483         // process MessageID
484         msgID = new MessageID(new URI("uuid:" + uuidGen.nextUUID()));
485         resHeaders.setMessageID(msgID);
486 
487         resHeaders.toEnvelope(msg.getSOAPEnvelope(), this.actor);
488 
489         // process ReplyTo
490         EndpointReferenceType replyTo = reqHeaders.getReplyTo();
491         if (replyTo != null) {
492             AttributedURI address = replyTo.getAddress();
493             if (address != null) {
494                 String uri = address.toString();
495                 if (uri != null && !uri.equals(Constants.NS_URI_ANONYMOUS)) {
496                     // send the msg to reply to
497                     forwardMessage(replyTo, msg);
498                     // Somehow make the response empty, or create a new empty
499                     // response
500                     msgContext.setResponseMessage(null);
501                 }
502             }
503         }
504     }
505 
506     protected void forwardMessage(EndpointReferenceType epr,
507                                   Message msg)
508         throws Exception {
509         AttributedURI address = epr.getAddress();
510 
511         AddressingHeaders headers = null;
512         MessageContext msgContext = msg.getMessageContext();
513         if (msgContext != null) {
514             headers = (AddressingHeaders) msgContext.getProperty(Constants.ENV_ADDRESSING_RESPONSE_HEADERS);
515         }
516         if (headers == null)
517         {
518             headers = new AddressingHeaders();
519         }
520         
521         headers.setTo(address);
522         headers.setReferenceProperties(epr.getProperties());
523 
524         Service service = getService(msgContext);
525 
526         Call c = (Call) service.createCall();
527         c.setTargetEndpointAddress(address.toString());
528         c.setRequestMessage(msg);
529         c.setProperty(Constants.ENV_ADDRESSING_REQUEST_HEADERS, headers);
530         configureCall(c, msgContext);
531         c.invoke();
532     }
533     
534     /***
535      * Override this method if you need something other than the default Service.<br>
536      * The service returned by this method is used in creating the new Call object.
537      * Something like: <br>
538      * <pre>
539      * Service service = getService(msgContext);
540      * Call call = service.createCall()
541      * call.setTargetEndpointAddress(toEndPointReference.getAddress().toString());
542      * </pre>
543      * 
544      * @param msgContext
545      * @return
546      */
547     protected Service getService(MessageContext msgContext) {
548         return new Service();
549     }
550     
551     /***
552      * Override this method to prepare the new call, for instance to add
553      * properties from the old MessageContext that may be needed by other
554      * handlers.
555      * 
556      * @param call Call object about to be invoked
557      * @param oldContext MessageContext of the original request/response.
558      * 
559      */
560     protected void configureCall(Call call, MessageContext oldContext) {
561     }
562 
563     /***
564      * Retrieve QNames for reference properties from deployment
565      * and initialize the refPropQNames list which is the list
566      * of reference properties that this handler should care
567      * about.
568      */
569     private void initializeReferencePropertyNames() {
570         // check if the user wants to process all headers, this means
571         // the user wants to treat non ws-addr headers as reference props
572         // If this option is not enabled, the non ws-addr headers will not
573         // be deserialized.
574         String refPropNames = (String) getOption("referencePropertyNames");
575         if (refPropNames == null) {
576             this.refPropQNames = new ArrayList();
577         } else if (refPropNames.equals("*")) {
578             this.refPropQNames = null;
579         } else {
580             this.refPropQNames = new ArrayList();
581             StringTokenizer tkn = new StringTokenizer(refPropNames, ",");
582             while (tkn.hasMoreTokens()) {
583                 String qnameString = tkn.nextToken().trim();
584                 try {
585                     QName qname = QName.valueOf(qnameString);
586                     refPropQNames.add(qname);
587                 } catch (Exception e) {
588                     // Ignore QNames which were written incorrectly
589                     // if their parsing results in errors
590                 }
591             }
592         }
593     }
594     
595 }