View Javadoc

1   /* 
2    * Copyright (c) 2007, Fraunhofer-Gesellschaft
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are
7    * met:
8    * 
9    * (1) Redistributions of source code must retain the above copyright
10   *     notice, this list of conditions and the disclaimer at the end.
11   *     Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in
13   *     the documentation and/or other materials provided with the
14   *     distribution.
15   * 
16   * (2) Neither the name of Fraunhofer nor the names of its
17   *     contributors may be used to endorse or promote products derived
18   *     from this software without specific prior written permission.
19   * 
20   * DISCLAIMER
21   * 
22   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33   *  
34   */
35  package org.ogf.graap.wsag.client.wsrf.impl;
36  
37  import java.io.ByteArrayInputStream;
38  import java.io.ByteArrayOutputStream;
39  import java.io.IOException;
40  import java.io.InputStream;
41  import java.math.BigInteger;
42  import java.net.URL;
43  import java.security.cert.X509Certificate;
44  import java.text.MessageFormat;
45  import java.util.Properties;
46  import java.util.Set;
47  import java.util.Vector;
48  
49  import javax.security.auth.login.LoginContext;
50  import javax.xml.namespace.QName;
51  import javax.xml.stream.XMLStreamException;
52  import javax.xml.stream.XMLStreamReader;
53  
54  import org.apache.axiom.om.OMElement;
55  import org.apache.axiom.om.util.StAXUtils;
56  import org.apache.axiom.soap.SOAPEnvelope;
57  import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
58  import org.apache.axis2.AxisFault;
59  import org.apache.axis2.client.OperationClient;
60  import org.apache.axis2.client.ServiceClient;
61  import org.apache.axis2.context.ConfigurationContext;
62  import org.apache.axis2.context.ConfigurationContextFactory;
63  import org.apache.axis2.context.MessageContext;
64  import org.apache.axis2.transport.http.HTTPConstants;
65  import org.apache.log4j.Logger;
66  import org.apache.muse.util.messages.Messages;
67  import org.apache.muse.util.messages.MessagesFactory;
68  import org.apache.muse.util.xml.XmlUtils;
69  import org.apache.muse.ws.addressing.EndpointReference;
70  import org.apache.muse.ws.addressing.soap.SimpleSoapClient;
71  import org.apache.muse.ws.addressing.soap.SoapConstants;
72  import org.apache.muse.ws.addressing.soap.SoapFault;
73  import org.apache.ws.security.WSConstants;
74  import org.apache.ws.security.WSSecurityEngineResult;
75  import org.apache.ws.security.WSSecurityException;
76  import org.apache.ws.security.components.crypto.Crypto;
77  import org.apache.ws.security.handler.WSHandlerConstants;
78  import org.apache.ws.security.handler.WSHandlerResult;
79  import org.apache.xmlbeans.XmlException;
80  import org.apache.xmlbeans.XmlObject;
81  import org.ogf.graap.wsag.api.configuration.WSAG4JConfiguration;
82  import org.ogf.graap.wsag.api.logging.LogMessage;
83  import org.ogf.graap.wsag.api.security.ISecurityProperties;
84  import org.ogf.graap.wsag.api.security.SecurityConstants;
85  import org.ogf.graap.wsag.client.wsrf.security.ClientSecurityHandler;
86  import org.ogf.graap.wsag4j.types.configuration.ConfigurationDocument;
87  import org.ogf.graap.wsag4j.types.configuration.ConfigurationType;
88  import org.ogf.graap.wsag4j.types.configuration.HandlerType;
89  import org.ogf.graap.wsag4j.types.configuration.WSRFClientConfigurationType;
90  import org.w3c.dom.Document;
91  import org.w3c.dom.Element;
92  
93  /**
94   * Basic implementation of a SOAP client. The client implements the capabilities to send SOAP messages to a
95   * WSAG4J server and performs basic security processing operations. The client implementation is NOT
96   * thread-safe. Multi-threading applications must use multiple client objects in order to perform multiple
97   * operations in parallel.
98   * 
99   * @author Oliver Waeldrich
100  * 
101  */
102 public class Axis2SoapClient extends SimpleSoapClient
103 {
104 
105     /**
106      * user provided WSAG4J WSRF client configuration file name, this will overwrite the default configuration
107      */
108     public static final String WSAG4J_WSRF_CLIENT_CONFIG_FILE = "/wsrf-client.config";
109 
110     /**
111      * default WSAG4J WSRF client configuration file name
112      */
113     public static final String WSAG4J_WSRF_CLIENT_CONFIG_FILE_DEFAULT = "/META-INF/wsrf-client.config";
114 
115     /**
116      * Default location of the Axis2 configuration files. The path must contain an Axis2 configuration file
117      * with the name <code>client.axis2.xml</code>. Implementations may change this property before the first
118      * invocation in order to provide an alternative configuration path.
119      */
120     private static String defaultAxis2ConfigurationPath = "/axis2-client";
121 
122     /**
123      * The SOAP client timeout in milliseconds.
124      */
125     // CHECKSTYLE:OFF - DEFAULT VARIABLE
126     public static int DEFAULT_TIME_OUT_IN_MILLI_SECONDS = 2 * 60 * 1000;
127 
128     // CHECKSTYLE:ON
129 
130     private static final Logger LOG = Logger.getLogger( Axis2SoapClient.class );
131 
132     //
133     // WSAG4J client configuration file
134     //
135     private static ConfigurationType config = null;
136 
137     //
138     // AXIS2 configuration context
139     //
140     private static ConfigurationContext configurationContext = null;
141 
142     /**
143      * Returns the current Axis2 configuration context used for the SOAP communication.
144      * 
145      * @return the configurationContext
146      */
147     public static ConfigurationContext getConfigurationContext()
148     {
149         return configurationContext;
150     }
151 
152     /**
153      * Enables implementations to provide an alternative Axis2 configuration context that will be used for
154      * communicating with the WSAG4J server.
155      * 
156      * @param configurationContext
157      *            the configurationContext to set
158      */
159     public static void setConfigurationContext( ConfigurationContext configurationContext )
160     {
161         Axis2SoapClient.configurationContext = configurationContext;
162     }
163 
164     //
165     // default definition of an empty SOAP header array
166     //
167     private static final Element[] EMPTY_ARRAY = new Element[0];
168 
169     //
170     // Used to lookup all exception messages
171     //
172     private static final Messages MESSAGES = MessagesFactory.get( SimpleSoapClient.class );
173 
174     //
175     // the properties are used for storing the results of the security processing
176     //
177     private Properties properties = new Properties();
178 
179     //
180     // the security properties
181     //
182     private final ISecurityProperties securityProperties;
183 
184     private Crypto crypto;
185 
186     /**
187      * Creates a new SOAP client with the given security properties.
188      * 
189      * @param securityProperties
190      *            the security properties to use
191      */
192     public Axis2SoapClient( Properties properties, ISecurityProperties securityProperties )
193     {
194         super();
195         this.securityProperties = securityProperties;
196         this.properties.putAll( properties );
197         initialize();
198     }
199 
200     /**
201      * initializes this client instance
202      */
203     protected void initialize()
204     {
205         //
206         // get the login context and read the crypto from the login context
207         //
208         LoginContext context = securityProperties.getLoginContext();
209 
210         if ( context != null )
211         {
212             Set<Crypto> userCrypto = context.getSubject().getPrivateCredentials( Crypto.class );
213             if ( !userCrypto.isEmpty() )
214             {
215                 crypto = userCrypto.iterator().next();
216             }
217         }
218 
219         //
220         // read the client configuration file on the first client invocation
221         //
222         synchronized ( Axis2SoapClient.class )
223         {
224 
225             try
226             {
227                 //
228                 // first try to lookup user provided client configuration, if this can't be found use the
229                 // default configuration
230                 //
231                 if ( config == null )
232                 {
233                     config = findWSAG4JConfiguration( WSAG4J_WSRF_CLIENT_CONFIG_FILE );
234 
235                     if ( config == null )
236                     {
237                         config = findWSAG4JConfiguration( WSAG4J_WSRF_CLIENT_CONFIG_FILE_DEFAULT );
238                     }
239                 }
240             }
241             catch ( IOException e )
242             {
243                 Object[] filler = new Object[] { WSAG4J_WSRF_CLIENT_CONFIG_FILE };
244                 String message =
245                     MessageFormat.format( "Failed to read wsag4j client configuration file {0}.", filler );
246                 throw new RuntimeException( message );
247             }
248 
249             try
250             {
251                 //
252                 // the repository URL needs an / at the end,
253                 // otherwise the repository is not loaded correctly
254                 // from the wsag4j-client-resources bundle
255                 //
256                 if ( configurationContext == null )
257                 {
258                     URL repoURL =
259                         Axis2SoapClient.class.getResource( getDefaultAxis2ConfigurationPath() + "/" );
260                     URL fileURL =
261                         Axis2SoapClient.class.getResource( getDefaultAxis2ConfigurationPath()
262                             + "/client.axis2.xml" );
263                     configurationContext =
264                         ConfigurationContextFactory.createConfigurationContextFromURIs( fileURL, repoURL );
265 
266                     /*
267                      * The following code block is commented out since we use still the default configuration
268                      * of the MultiThreadedHttpConnectionManager.
269                      * 
270                      * There was a problem that the connections are not returned to the connection pool of the
271                      * MultiThreadedHttpConnectionManager after a SOAP call. This issue was solved by calling
272                      * the oc.complete( currentContext ) method after the SOAP call ( see doAxisCall ->
273                      * finalize block )
274                      */
275 
276                     /*
277                      * MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager = new
278                      * MultiThreadedHttpConnectionManager();
279                      * 
280                      * HttpConnectionManagerParams params = new HttpConnectionManagerParams();
281                      * params.setDefaultMaxConnectionsPerHost(20);
282                      * multiThreadedHttpConnectionManager.setParams(params); HttpClient httpClient = new
283                      * HttpClient(multiThreadedHttpConnectionManager);
284                      * configurationContext.setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
285                      */
286                 }
287             }
288             catch ( Exception e )
289             {
290                 Object[] filler = new Object[] { e.getMessage() };
291                 String message =
292                     MessageFormat.format( "Failed to read axis2 client configuration. Error:  {0}.", filler );
293                 throw new RuntimeException( message, e );
294             }
295         }
296     }
297 
298     /**
299      * {@inheritDoc}
300      */
301     @Override
302     protected Element createMessage( EndpointReference source, EndpointReference destination, String action,
303                                      Element[] bodyElements, Element[] extraHeaders )
304     {
305         Element[] additionalHeaders = generateExtraHeaders();
306         if ( additionalHeaders == null )
307         {
308             additionalHeaders = EMPTY_ARRAY;
309         }
310 
311         Element[] headers;
312 
313         if ( extraHeaders.length == 0 )
314         {
315             // extra headers is usually empty
316             headers = additionalHeaders;
317         }
318         else
319         {
320             // merge the extra headers with the additional headers
321             Vector<Element> tmpHeaders = new Vector<Element>();
322             for ( int i = 0; i < extraHeaders.length; i++ )
323             {
324                 tmpHeaders.add( extraHeaders[i] );
325             }
326 
327             for ( int i = 0; i < additionalHeaders.length; i++ )
328             {
329                 tmpHeaders.add( additionalHeaders[i] );
330             }
331 
332             headers = tmpHeaders.toArray( new Element[tmpHeaders.size()] );
333         }
334 
335         // then we let our parent create the SOAP message
336         return super.createMessage( source, destination, action, bodyElements, headers );
337     }
338 
339     /**
340      * {@inheritDoc}
341      */
342     @Override
343     public Element[] send( EndpointReference src, EndpointReference dest, String wsaAction, Element[] body,
344                            Element[] extraHeaders )
345     {
346         if ( dest == null )
347         {
348             throw new NullPointerException( MESSAGES.get( "NullDestinationEPR" ) );
349         }
350 
351         if ( wsaAction == null )
352         {
353             throw new NullPointerException( MESSAGES.get( "NullActionURI" ) );
354         }
355 
356         if ( body == null )
357         {
358             body = EMPTY_ARRAY;
359         }
360 
361         if ( extraHeaders == null )
362         {
363             extraHeaders = EMPTY_ARRAY;
364         }
365 
366         //
367         // create the request message
368         //
369         Element soapRequest = createMessage( src, dest, wsaAction, body, extraHeaders );
370 
371         if ( isUsingTrace() )
372         {
373             trace( soapRequest, false );
374         }
375 
376         Element soapResponse = null;
377 
378         try
379         {
380 
381             //
382             // read in the response and build an XML document from it
383             //
384             soapResponse = doAxisCall( soapRequest, dest, wsaAction );
385         }
386 
387         //
388         // handle errors that are thrown by axis
389         // and create a Muse SOAP fault
390         //
391         catch ( AxisFault error )
392         {
393             SoapFault soapFault = new SoapFault( error.getMessage(), error );
394             if ( error.getFaultCode() != null )
395             {
396                 soapFault.setCode( error.getFaultCode() );
397             }
398 
399             if ( ( error.getFaultSubCodes() != null ) && ( error.getFaultSubCodes().size() > 0 ) )
400             {
401                 soapFault.setSubCode( (QName) error.getFaultSubCodes().get( 0 ) );
402             }
403 
404             try
405             {
406                 OMElement detail = error.getDetail();
407 
408                 ByteArrayOutputStream detailOut = new ByteArrayOutputStream();
409                 detail.serialize( detailOut );
410 
411                 ByteArrayInputStream detailIn = new ByteArrayInputStream( detailOut.toByteArray() );
412                 soapFault.setDetail( XmlUtils.createDocument( detailIn ).getDocumentElement() );
413             }
414             catch ( Exception e )
415             {
416                 return new Element[] { soapFault.toXML() };
417             }
418             return new Element[] { soapFault.toXML() };
419         }
420 
421         // handle any other runtime/IO exceptions as best we can
422         catch ( Throwable error )
423         {
424             SoapFault soapFault = new SoapFault( error.getMessage(), error );
425             return new Element[] { soapFault.toXML() };
426         }
427 
428         if ( isUsingTrace() )
429         {
430             trace( soapResponse, true );
431         }
432 
433         //
434         // return the elements inside the SOAP body
435         //
436         Element responseBody = XmlUtils.getElement( soapResponse, SoapConstants.BODY_QNAME );
437         return XmlUtils.getAllElements( responseBody );
438     }
439 
440     /**
441      * Executes a SOAP call by using the Axis 2 SOAP engine.
442      * 
443      * @param soapRequest
444      *            the SOAP request as a DOM element
445      * @param dest
446      *            the destination EPR
447      * @param wsaAction
448      *            the web service action
449      * 
450      * @return the SOAP response as a DOM element
451      * @throws AxisFault
452      *             indicates a SOAP fault during the request
453      */
454     protected Element doAxisCall( Element soapRequest, EndpointReference dest, String wsaAction )
455         throws AxisFault
456     {
457         //
458         // remove old security processing results from properties
459         //
460         properties.remove( SecurityConstants.X509_SERVER_CERTIFICATE );
461         properties.remove( SecurityConstants.X509_SERVER_CERTIFICATE_CHAIN );
462 
463         //
464         // prepare the AXIS call by saving the old AXIS2 MessageContext
465         // and creating a new MessageContext for the subsequent call.
466         // This MessageContext is used to pass the client crypto to our
467         // Merlin implementation.
468         //
469         MessageContext currentContext = new MessageContext();
470         MessageContext oldContext = MessageContext.getCurrentMessageContext();
471         MessageContext.setCurrentMessageContext( currentContext );
472 
473         ServiceClient client = new org.apache.axis2.client.ServiceClient( configurationContext, null );
474 
475         org.apache.axis2.addressing.EndpointReference to =
476             new org.apache.axis2.addressing.EndpointReference( dest.getAddress().toString() );
477         client.getOptions().setTo( to );
478 
479         // during the initialization of the service client, a set
480         // of operation clients was created. The following types are available:
481         // [ANON_OUT_IN_OP], [ANON_OUT_ONLY_OP], [ANON_ROBUST_OUT_ONLY_OP]
482         OperationClient oc = client.createClient( ServiceClient.ANON_OUT_IN_OP );
483 
484         try
485         {
486 
487             // serialize the message and read it via StAX
488             String serializedMessage = XmlUtils.toString( soapRequest, false, false );
489             InputStream in = new ByteArrayInputStream( serializedMessage.getBytes() );
490 
491             XMLStreamReader reader;
492 
493             try
494             {
495                 reader = StAXUtils.createXMLStreamReader( in );
496             }
497             catch ( XMLStreamException e )
498             {
499                 String message = "Error creating axis call from SOAP request";
500                 throw new AxisFault( message, e );
501             }
502             StAXSOAPModelBuilder builder = new StAXSOAPModelBuilder( reader );
503 
504             // since we have parsed a new created SOAP 1.2 request,
505             // the result of this process is a org.apache.axiom.soap.SOAPEnvelope
506             SOAPEnvelope envelope = (SOAPEnvelope) builder.getDocumentElement();
507 
508             // add the user signing crypto
509             currentContext.setProperty( SecurityConstants.CRYPTO_SIGN, getCrypto() );
510             currentContext.setProperty( HTTPConstants.AUTO_RELEASE_CONNECTION, new Boolean( true ) );
511             // currentContext.getOptions().setUseSeparateListener(true);
512 
513             currentContext.getOptions().setTimeOutInMilliSeconds( DEFAULT_TIME_OUT_IN_MILLI_SECONDS );
514 
515             currentContext.setEnvelope( envelope );
516 
517             oc.addMessageContext( currentContext );
518             oc.getOptions().setAction( wsaAction );
519 
520             // oc.getOptions().setProperty(HTTPConstants.SO_TIMEOUT,new Integer(600000));
521             // oc.getOptions().setProperty(HTTPConstants.CONNECTION_TIMEOUT,new Integer(600000));
522             // oc.getOptions().setProperty(HTTPConstants.REUSE_HTTP_CLIENT, new Boolean(true));
523 
524             //
525             // execute the operation client
526             //
527             if ( LOG.isTraceEnabled() )
528             {
529                 LOG.trace( MessageFormat.format( "starting call to ", new Object[] { dest.getAddress() } ) );
530             }
531 
532             oc.execute( true );
533 
534             if ( LOG.isTraceEnabled() )
535             {
536                 LOG.trace( MessageFormat.format( "received response from  ",
537                     new Object[] { dest.getAddress() } ) );
538             }
539 
540             //
541             // now we read the response from the MessageContext,
542             // parse the result and return
543             //
544             MessageContext returnMessageContext =
545                 oc.getMessageContext( org.apache.axis2.wsdl.WSDLConstants.MESSAGE_LABEL_IN_VALUE );
546 
547             SOAPEnvelope returnEnv = returnMessageContext.getEnvelope();
548 
549             XMLStreamReader resultReader = returnEnv.getXMLStreamReaderWithoutCaching();
550 
551             //
552             // Read security handler results from response message context
553             // and place them in the properties object.
554             //
555             processSecurityResults( returnMessageContext );
556 
557             return parseResponse( resultReader );
558 
559         }
560         finally
561         {
562             oc.complete( currentContext );
563             MessageContext.setCurrentMessageContext( oldContext );
564         }
565     }
566 
567     @SuppressWarnings( "unchecked" )
568     private void processSecurityResults( MessageContext context ) throws AxisFault
569     {
570         try
571         {
572             Vector<WSHandlerResult> handlerResults =
573                 (Vector<WSHandlerResult>) context.getProperty( WSHandlerConstants.RECV_RESULTS );
574 
575             //
576             // Only process security handler results if present
577             //
578             if ( handlerResults != null )
579             {
580 
581                 for ( int i = 0; i < handlerResults.size(); i++ )
582                 {
583                     WSHandlerResult currentResult = handlerResults.get( i );
584 
585                     Vector<WSSecurityEngineResult> wsSecEngineResults = currentResult.getResults();
586 
587                     for ( int j = 0; j < wsSecEngineResults.size(); j++ )
588                     {
589                         WSSecurityEngineResult wser = wsSecEngineResults.get( j );
590                         if ( ( (Integer) wser.get( WSSecurityEngineResult.TAG_ACTION ) ).intValue() == WSConstants.SIGN
591                             && wser.get( WSSecurityEngineResult.TAG_X509_CERTIFICATE ) != null )
592                         {
593 
594                             // put the server certificate into the client properties
595                             X509Certificate certificate =
596                                 (X509Certificate) wser.get( WSSecurityEngineResult.TAG_X509_CERTIFICATE );
597                             properties.put( SecurityConstants.X509_SERVER_CERTIFICATE, certificate );
598 
599                             String issuerString = certificate.getIssuerDN().getName();
600                             BigInteger issuerSerial = certificate.getSerialNumber();
601                             String alias = getCrypto().getAliasForX509Cert( issuerString, issuerSerial );
602 
603                             if ( alias != null )
604                             {
605                                 // Retrieve the certificate for the alias from the keystore
606                                 try
607                                 {
608                                     X509Certificate[] certs = getCrypto().getCertificates( alias );
609                                     properties.put( SecurityConstants.X509_SERVER_CERTIFICATE_CHAIN, certs );
610                                 }
611                                 catch ( WSSecurityException ex )
612                                 {
613                                     String msgError =
614                                         "Axis2SoapClient: Could not get certificates for alias " + alias;
615                                     throw new WSSecurityException( msgError, ex );
616                                 }
617                             }
618                         }
619 
620                         if ( ( (Integer) wser.get( WSSecurityEngineResult.TAG_ACTION ) ).intValue() == WSConstants.BST
621                             && wser.get( WSSecurityEngineResult.TAG_X509_CERTIFICATES ) != null )
622                         {
623 
624                             // put the server certificates into the client properties
625                             X509Certificate[] certificates =
626                                 (X509Certificate[]) wser.get( WSSecurityEngineResult.TAG_X509_CERTIFICATES );
627                             properties.put( SecurityConstants.X509_SERVER_CERTIFICATE_CHAIN, certificates );
628                         }
629                     }
630 
631                 }
632             }
633         }
634         catch ( Exception e )
635         {
636             throw new AxisFault( "Axis2SoapClient: Error while processing security handler results.", e );
637         }
638     }
639 
640     private Element parseResponse( XMLStreamReader reader ) throws AxisFault
641     {
642         try
643         {
644             XmlObject parsedResult = XmlObject.Factory.parse( reader );
645 
646             if ( parsedResult.getDomNode() instanceof Document )
647             {
648                 return XmlUtils.getFirstElement( parsedResult.getDomNode() );
649             }
650             if ( parsedResult.getDomNode() instanceof Element )
651             {
652                 return (Element) parsedResult.getDomNode();
653             }
654 
655             String message = "Response document is not of type document or element or null.";
656             throw new AxisFault( message );
657         }
658         catch ( Exception e )
659         {
660             String message = "Error in deserializing the Axis response.";
661             throw new AxisFault( message, e );
662         }
663     }
664 
665     /**
666      * 
667      * @return Returns a set of extra header elements that is included in the SOAP message.
668      * @throws SecurityException
669      */
670     protected Element[] generateExtraHeaders()
671     {
672 
673         Vector<Element> extraHeaders = new Vector<Element>();
674 
675         ClientSecurityHandler[] handler;
676         try
677         {
678             handler = getSecurityHandler();
679         }
680         catch ( Exception e )
681         {
682             LOG.error( e.getMessage(), e );
683             throw new RuntimeException( "failed to load security handler", e );
684         }
685 
686         for ( int i = 0; i < handler.length; i++ )
687         {
688             Element[] tmpHeaders = handler[i].createHeader( securityProperties );
689             for ( int j = 0; j < tmpHeaders.length; j++ )
690             {
691                 extraHeaders.add( tmpHeaders[j] );
692             }
693         }
694 
695         return extraHeaders.toArray( new Element[extraHeaders.size()] );
696     }
697 
698     /**
699      * @return the security handler of this client instance
700      */
701     protected ClientSecurityHandler[] getSecurityHandler()
702     {
703 
704         if ( config == null )
705         {
706             Object[] filler = new Object[] { WSAG4J_WSRF_CLIENT_CONFIG_FILE };
707             String message =
708                 MessageFormat.format( "The client configuration file {0} was not found.", filler );
709             LOG.warn( message );
710 
711             return new ClientSecurityHandler[0];
712         }
713 
714         WSRFClientConfigurationType wsrfConfig = config.getWSRFClientConfiguration();
715 
716         if ( wsrfConfig.isSetSecurityHandlerChain() )
717         {
718             HandlerType[] handler = wsrfConfig.getSecurityHandlerChain().getHandlerArray();
719             ClientSecurityHandler[] result = new ClientSecurityHandler[handler.length];
720 
721             for ( int i = 0; i < handler.length; i++ )
722             {
723                 String implementationClass = handler[i].getImplementationClass();
724                 String handlerName = handler[i].getHandlerName();
725 
726                 try
727                 {
728                     @SuppressWarnings( "unchecked" )
729                     Class<ClientSecurityHandler> implementation =
730                         (Class<ClientSecurityHandler>) Class.forName( implementationClass );
731                     result[i] = implementation.newInstance();
732                 }
733                 catch ( ClassNotFoundException e )
734                 {
735                     Object[] filler = new Object[] { implementationClass, handlerName };
736                     String message =
737                         MessageFormat.format(
738                             "The implementation class {0} for handler {1} could not be found.", filler );
739                     throw new RuntimeException( message );
740                 }
741                 catch ( IllegalAccessException e )
742                 {
743                     Object[] filler = new Object[] { implementationClass, handlerName, e.getMessage() };
744                     String msgError =
745                         "The implementation class {0} for handler {1} could not be accessed. [{2}]";
746                     String message = MessageFormat.format( msgError, filler );
747                     throw new RuntimeException( message );
748                 }
749                 catch ( InstantiationException e )
750                 {
751                     Object[] filler = new Object[] { implementationClass, handlerName, e.getMessage() };
752                     String msgError =
753                         "The implementation class {0} for handler {1} could not be instantiated. [{2}]";
754                     String message = MessageFormat.format( msgError, filler );
755                     throw new RuntimeException( message );
756                 }
757                 catch ( ClassCastException e )
758                 {
759                     Object[] filler =
760                         new Object[] { implementationClass, ClientSecurityHandler.class.getName() };
761                     String msgError = "The implementation class {0} does not implement the interface {1}.";
762                     String message = MessageFormat.format( msgError, filler );
763                     throw new RuntimeException( message );
764                 }
765             }
766 
767             return result;
768         }
769         else
770         {
771             return new ClientSecurityHandler[0];
772         }
773 
774     }
775 
776     /**
777      * @return the properties
778      */
779     public Properties getProperties()
780     {
781         return properties;
782     }
783 
784     /**
785      * @param properties
786      *            the properties to set
787      */
788     public void setProperties( Properties properties )
789     {
790         this.properties = properties;
791     }
792 
793     /**
794      * @return the crypto
795      */
796     public Crypto getCrypto()
797     {
798         return crypto;
799     }
800 
801     /**
802      * Returns the default location of the Axis2 configuration files. The path must be available in the
803      * classpath and must contain an Axis2 configuration file with the name <code>client.axis2.xml</code> and
804      * all required modules.
805      * 
806      * @return the Axis2 configuration path
807      */
808     public static String getDefaultAxis2ConfigurationPath()
809     {
810         return defaultAxis2ConfigurationPath;
811     }
812 
813     /**
814      * Sets the default location of the Axis2 configuration files. The path must be available in the classpath
815      * and must contain an Axis2 configuration file with the name <code>client.axis2.xml</code> and all
816      * required modules. Implementations may change the default path in order to provide an alternative
817      * configuration path and recreate the default Axis2 configuration context.
818      * 
819      * @param defaultAxis2ConfigurationPath
820      *            the Axis2 configuration path
821      * 
822      * @throws Exception
823      *             indicates that the creation of the new Axis2 configuration context failed
824      */
825     public static void setDefaultAxis2ConfigurationPath( String defaultAxis2ConfigurationPath )
826         throws Exception
827     {
828         try
829         {
830             URL repoURL = Axis2SoapClient.class.getResource( defaultAxis2ConfigurationPath + "/" );
831             URL fileURL =
832                 Axis2SoapClient.class.getResource( defaultAxis2ConfigurationPath + "/client.axis2.xml" );
833             configurationContext =
834                 ConfigurationContextFactory.createConfigurationContextFromURIs( fileURL, repoURL );
835 
836             Axis2SoapClient.defaultAxis2ConfigurationPath = defaultAxis2ConfigurationPath;
837         }
838         catch ( AxisFault e )
839         {
840             throw new Exception( "failed to create configuration context", e );
841         }
842     }
843 
844     /**
845      * @param fileName
846      *            The file name of the requested XML configuration file.
847      * @return The XML configuration file as an {@link org.apache.xmlbeans.XmlObject}.
848      * @throws IOException
849      *             the configuration file could not be read or a related error occurred
850      */
851     public static ConfigurationType findWSAG4JConfiguration( String fileName ) throws IOException
852     {
853         try
854         {
855 
856             InputStream resourceInput = WSAG4JConfiguration.findResource( fileName );
857 
858             ConfigurationType result = null;
859 
860             if ( resourceInput != null )
861             {
862                 try
863                 {
864                     result = ConfigurationDocument.Factory.parse( resourceInput ).getConfiguration();
865                 }
866                 catch ( IOException e )
867                 {
868                     String msgText = "Error reading the configuration file {0}. Error: {1}";
869                     String message = LogMessage.format( msgText, fileName, e.getMessage() );
870 
871                     LOG.error( message );
872                     throw new IOException( message );
873 
874                 }
875                 catch ( XmlException e )
876                 {
877                     String msgText = "Error reading the configuration file {0}. Description: {1}";
878                     String message = LogMessage.format( msgText, fileName, e.getMessage() );
879 
880                     LOG.error( message );
881                     throw new IOException( message );
882                 }
883             }
884 
885             return result;
886         }
887         catch ( Exception ex )
888         {
889             String msgText = "Failed to load configuration file [{0}]. Message: {1}";
890             String message = LogMessage.format( msgText, fileName, ex.getMessage() );
891             LOG.trace( message );
892 
893             return null;
894         }
895     }
896 }