View Javadoc

1   /*
2    * Copyright  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  package org.apache.ws.addressing.handler;
18  
19  import org.apache.axis.message.addressing.Action;
20  import org.apache.axis.message.addressing.AddressingHeaders;
21  import org.apache.axis.message.addressing.AttributedURI;
22  import org.apache.axis.message.addressing.Constants;
23  import org.apache.axis.message.addressing.EndpointReference;
24  import org.apache.axis.message.addressing.MessageID;
25  import org.apache.axis.message.addressing.To;
26  import org.apache.axis.message.addressing.util.AddressingUtils;
27  import org.apache.axis.types.URI;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  import javax.xml.namespace.QName;
32  import javax.xml.rpc.Call;
33  import javax.xml.rpc.JAXRPCException;
34  import javax.xml.rpc.ServiceException;
35  import javax.xml.rpc.ServiceFactory;
36  import javax.xml.rpc.handler.MessageContext;
37  import javax.xml.rpc.handler.soap.SOAPMessageContext;
38  import javax.xml.soap.Node;
39  import javax.xml.soap.SOAPBody;
40  import javax.xml.soap.SOAPConnection;
41  import javax.xml.soap.SOAPConnectionFactory;
42  import javax.xml.soap.SOAPElement;
43  import javax.xml.soap.SOAPMessage;
44  import javax.xml.transform.TransformerFactory;
45  import java.net.MalformedURLException;
46  import java.net.URL;
47  import java.util.ArrayList;
48  import java.util.Iterator;
49  import java.util.List;
50  
51  /***
52   * A server-side JAX-RPC {@link javax.xml.rpc.handler.Handler} that extracts
53   * WS-Addressing headers from incoming SOAP requests and inserts them into
54   * outgoing SOAP responses.
55   *
56   * @author Davanum Srinivas (dims@yahoo.com)
57   * @author Ian P. Springer <ian_springer@hp.com>
58   */
59  public class ServerSideAddressingHandler extends AbstractAddressingHandler
60  {
61  
62     private static final Log LOG =
63           LogFactory.getLog( ServerSideAddressingHandler.class.getName() );
64  
65     /***
66      * thread local that gives each thread its own TransformerFactory (since it is not thread-safe)
67      */
68     public static ThreadLocal TRANSFORMER_FACTORY =
69           new ThreadLocal()
70           {
71              protected synchronized Object initialValue()
72              {
73                 return TransformerFactory.newInstance();
74              }
75           };
76  
77     public boolean handleRequest( MessageContext msgContext )
78     {
79        SOAPMessageContext soapMsgContext = (SOAPMessageContext) msgContext;
80        try
81        {
82           SOAPMessage msg = soapMsgContext.getMessage();
83           if ( msg == null )
84           {
85              return CONTINUE_HANDLER_CHAIN_PROCESSING;
86           }
87           AddressingHeaders headers =
88                 new AddressingHeaders( msg.getSOAPPart().getEnvelope(),
89                       getActor(),
90                       true,
91                       isRemoveHeadersEnabled(),
92                       false,
93                       getReferencePropertyQNames() );
94  
95           if (headers.getTo()==null)
96           {
97              // not a WS-A request; let it pass thru w/out doing anything
98              return CONTINUE_HANDLER_CHAIN_PROCESSING;
99           }
100          if (headers.getAction()==null)
101          {
102             // should we throw a SOAPFaultException here?
103             LOG.debug("WS-A request to endpoint " + headers.getTo() + " is missing the required wsa:Action header.");
104          }
105          msgContext.setProperty( Constants.ENV_ADDRESSING_REQUEST_HEADERS,
106                headers );
107 
108          // set the target service based on To header if it hasn't already been
109          // determined.  NOTE: May want to add an option to override later.
110          setTargetService( soapMsgContext, headers );
111       }
112       catch ( Exception e )
113       {
114          if ( LOG.isDebugEnabled() )
115          {
116             e.printStackTrace();
117          }
118          throw new JAXRPCException( "unexpected error in handleRequest()", e );
119       }
120       return CONTINUE_HANDLER_CHAIN_PROCESSING;
121    }
122 
123    public boolean handleResponse( MessageContext msgContext )
124    {
125       SOAPMessageContext soapMsgContext = (SOAPMessageContext) msgContext;
126       try
127       {
128          SOAPMessage msg = soapMsgContext.getMessage();
129          if ( msg == null )
130          {
131             return CONTINUE_HANDLER_CHAIN_PROCESSING;
132          }
133 
134          AddressingHeaders reqHeaders =
135                (AddressingHeaders) msgContext.getProperty( Constants.ENV_ADDRESSING_REQUEST_HEADERS );
136 
137          if ( reqHeaders == null )
138          {
139             // not a WS-A response; let it pass thru w/out doing anything
140             return CONTINUE_HANDLER_CHAIN_PROCESSING;
141          }
142          AddressingHeaders resHeaders =
143                AddressingUtils.getResponseHeaders( msgContext );
144          resHeaders.setSetMustUnderstand( isMustUnderstandEnabled( msgContext ) );
145          processFromHeader( reqHeaders, resHeaders );
146          processActionHeader( reqHeaders, resHeaders );
147          processToHeader( reqHeaders, resHeaders );
148          processRelatesToHeader( reqHeaders, resHeaders );
149          processMessageIdHeader( resHeaders );
150          resHeaders.toEnvelope( msg.getSOAPPart().getEnvelope(), getActor() );
151          processReplyToHeader( reqHeaders, soapMsgContext );
152       }
153       catch ( Exception e )
154       {
155          if ( LOG.isDebugEnabled() )
156          {
157             e.printStackTrace();
158          }
159          throw new JAXRPCException( "unexpected error in handleResponse()", e );
160       }
161       return CONTINUE_HANDLER_CHAIN_PROCESSING;
162    }
163 
164    public boolean handleFault( MessageContext msgContext )
165    {
166       SOAPMessageContext soapMsgContext = (SOAPMessageContext) msgContext;
167       try
168       {
169          AddressingHeaders reqHeaders =
170                (AddressingHeaders) msgContext.getProperty( Constants.ENV_ADDRESSING_REQUEST_HEADERS );
171          if ( reqHeaders == null )
172          {
173             // not a WS-A fault; let it pass thru w/out doing anything
174             return CONTINUE_HANDLER_CHAIN_PROCESSING;
175          }
176          AddressingHeaders resHeaders =
177                AddressingUtils.getResponseHeaders( msgContext );
178          processFromHeader( reqHeaders, resHeaders );
179          // set action for fault only with 04 spec; 03 spec unclear on that issue
180          if ( ! reqHeaders.getNamespaceURI().equals( Constants.NS_URI_ADDRESSING_2003_03 ) )
181          {
182             resHeaders.setAction( new Action( new URI( Constants.FAULT_ACTION ) ) );
183          }
184          addRelatesToHeader( reqHeaders, resHeaders );
185          addMessageIdHeader( resHeaders );
186          addAddressingHeadersToSOAPEnvelope( soapMsgContext, resHeaders );
187          processFaultToHeader( reqHeaders, soapMsgContext );
188       }
189       catch ( Exception e )
190       {
191          if ( LOG.isDebugEnabled() )
192          {
193             e.printStackTrace();
194          }
195          throw new JAXRPCException( "unexpected error in handleFault()", e );
196       }
197       return CONTINUE_HANDLER_CHAIN_PROCESSING;
198    }
199 
200    /***
201     * Forward the SOAP message contained in the specified SOAP message context
202     * on to the specified endpoint reference.
203     *
204     * @param soapMsgContext
205     * @param endpointRef
206     */
207    protected void forwardMessage( SOAPMessageContext soapMsgContext, EndpointReference endpointRef )
208    {
209       try
210       {
211          SOAPConnection soapConn = SOAPConnectionFactory.newInstance().createConnection();
212          soapConn.call( soapMsgContext.getMessage(), toURL( endpointRef.getAddress() ) );
213          soapConn.close();
214          SOAPBody responseBody = soapMsgContext.getMessage().getSOAPPart().getEnvelope().getBody();
215          removeAllChildElements( responseBody );
216       }
217       catch ( Exception e )
218       {
219          throw new JAXRPCException( "Failed to forward SOAP message." );
220       }
221    }
222 
223    private URL toURL( AttributedURI addr )
224          throws MalformedURLException
225    {
226       return new URL( addr.toString() );
227    }
228 
229    /***
230     * Override this method if you need something other than the default Service.<br>
231     * The service returned by this method is used in creating the new Call object.
232     * Something like: <br>
233     * <pre>
234     * Service service = getService(msgContext);
235     * Call call = service.createCall()
236     * call.setTargetEndpointAddress(toEndPointReference.getAddress().toString());
237     * </pre>
238     *
239     * @param msgContext
240     *
241     * @return
242     */
243    protected javax.xml.rpc.Service getService( MessageContext msgContext ) throws ServiceException
244    {
245       return ServiceFactory.newInstance().createService( new QName( "" ) );
246    }
247 
248    /***
249     * Override this method to prepare the new call, for instance to add
250     * properties from the old MessageContext that may be needed by other
251     * handlers.
252     *
253     * @param call       Call object about to be invoked
254     * @param oldContext MessageContext of the original request/response.
255     */
256    protected void configureCall( Call call, MessageContext oldContext )
257    {
258       return;  // intentionally empty
259    }
260 
261    /***
262     * Can be overridden by subclasses to customize how the wsa:to header is interpreted.
263     */
264    protected String getTargetServiceName( AddressingHeaders headers )
265          throws Exception
266    {
267       To toURI = headers.getTo();
268       if ( toURI == null )
269       {
270          return null;
271       }
272       String to = toURI.getPath();
273       if ( to == null )
274       {
275          return null;
276       }
277       // set the target service
278       return ( to.substring( to.lastIndexOf( '/' ) + 1 ) );
279    }
280 
281    /***
282     * Platform-specific subclasses can optionally implement this method.
283     *
284     * @param soapMsgContext
285     * @param headers
286     *
287     * @throws Exception
288     */
289    protected void setTargetService( SOAPMessageContext soapMsgContext, AddressingHeaders headers )
290          throws Exception
291    {
292       return;  // intentionally empty
293    }
294 
295    private void processFromHeader( AddressingHeaders requestAddrHeaders, AddressingHeaders responseAddrHeaders )
296    {
297       EndpointReference fromEPR = responseAddrHeaders.getFrom();
298       if ( fromEPR == null )
299       {
300          To toURI = requestAddrHeaders.getTo();
301          if ( toURI != null )
302          {
303             fromEPR = new EndpointReference( toURI );
304             fromEPR.setProperties( requestAddrHeaders.getReferenceProperties() );
305             responseAddrHeaders.setFrom( fromEPR );
306          }
307       }
308    }
309 
310    private void processActionHeader( AddressingHeaders requestAddrHeaders, AddressingHeaders responseAddrHeaders )
311          throws URI.MalformedURIException
312    {
313       Action action = responseAddrHeaders.getAction();
314       if ( action == null )
315       {
316          // not set - try request headers
317          action = requestAddrHeaders.getAction();
318          if ( action != null )
319          {
320             responseAddrHeaders.setAction( new Action( new URI( action.toString() +
321                   "Response" ) ) );
322          }
323       }
324    }
325 
326    private void processToHeader( AddressingHeaders requestAddrHeaders, AddressingHeaders responseAddrHeaders )
327          throws URI.MalformedURIException
328    {
329       if ( responseAddrHeaders.getFrom() == null && requestAddrHeaders.getFrom() != null )
330       {
331          responseAddrHeaders.setTo( requestAddrHeaders.getFrom().getAddress() );
332       }
333       else
334       {
335          responseAddrHeaders.setTo( new To( Constants.NS_URI_ANONYMOUS ) );
336       }
337    }
338 
339    private void processRelatesToHeader( AddressingHeaders requestAddrHeaders, AddressingHeaders responseAddrHeaders )
340          throws URI.MalformedURIException
341    {
342       MessageID msgID = requestAddrHeaders.getMessageID();
343       if ( msgID != null )
344       {
345          responseAddrHeaders.addRelatesTo( msgID.toString(),
346                Constants.QNAME_RESPONSE );
347       }
348    }
349 
350    private void processMessageIdHeader( AddressingHeaders responseAddrHeaders )
351          throws URI.MalformedURIException
352    {
353       responseAddrHeaders.setMessageID( createMessageID() );
354    }
355 
356    private void processReplyToHeader( AddressingHeaders requestAddrHeaders, SOAPMessageContext soapMsgContext )
357          throws Exception
358    {
359       EndpointReference replyTo = requestAddrHeaders.getReplyTo();
360       if ( replyTo != null )
361       {
362          AttributedURI address = replyTo.getAddress();
363          if ( address != null )
364          {
365             String uri = address.toString();
366             if ( uri != null && !uri.equals( Constants.NS_URI_ANONYMOUS ) )
367             {
368                forwardMessage( soapMsgContext, replyTo );
369             }
370          }
371       }
372    }
373 
374    private void addAddressingHeadersToSOAPEnvelope( SOAPMessageContext soapMsgContext, AddressingHeaders resHeaders )
375          throws Exception
376    {
377       SOAPMessage msg = soapMsgContext.getMessage();
378       if ( msg == null )
379       {
380          throw new JAXRPCException( "Unable to obtain response message from SOAP message context." );
381       }
382       resHeaders.toEnvelope( msg.getSOAPPart().getEnvelope() );
383    }
384 
385    private void processFaultToHeader( AddressingHeaders reqHeaders, SOAPMessageContext soapMsgContext )
386          throws Exception
387    {
388       EndpointReference faultTo = reqHeaders.getFaultTo();
389       if ( faultTo != null )
390       {
391          AttributedURI address = faultTo.getAddress();
392          if ( address != null && address.toString() != null )
393          {
394             forwardMessage( soapMsgContext, faultTo );
395          }
396       }
397    }
398 
399    private void addRelatesToHeader( AddressingHeaders reqHeaders, AddressingHeaders resHeaders )
400          throws URI.MalformedURIException
401    {
402       MessageID msgID = reqHeaders.getMessageID();
403       if ( msgID != null )
404       {
405          resHeaders.addRelatesTo( msgID.toString(),
406                Constants.QNAME_RESPONSE );
407       }
408    }
409 
410    private void addMessageIdHeader( AddressingHeaders resHeaders )
411          throws URI.MalformedURIException
412    {
413       MessageID msgID = new MessageID( new URI( "uuid:" + generateUUId() ) );
414       resHeaders.setMessageID( msgID );
415    }
416 
417    /***
418     * Removes all child elements from the specified {@link javax.xml.soap.SOAPElement}.
419     * NOTE: w
420     */
421    protected static void removeAllChildElements( SOAPElement soapElem )
422    {
423       Iterator iter = soapElem.getChildElements();
424       // NOTE: Convert iterator to list to avoid ConcurrentModificationExceptions
425       // caused by modifying items in an iterator during iteration
426       List children = toList( iter );
427       for ( int i = 0; i < children.size(); i++ )
428       {
429          Node child = (Node) children.get( i );
430          if ( child.getParentElement() != null )
431          {
432             child.detachNode();
433             child.recycleNode();
434          }
435       }
436    }
437 
438    private static List toList( Iterator iter )
439    {
440       List list = new ArrayList();
441       while ( iter.hasNext() )
442       {
443          list.add( iter.next() );
444       }
445       return list;
446    }
447 
448 }
449