1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
95
96
97
98
99
100
101
102 public class Axis2SoapClient extends SimpleSoapClient
103 {
104
105
106
107
108 public static final String WSAG4J_WSRF_CLIENT_CONFIG_FILE = "/wsrf-client.config";
109
110
111
112
113 public static final String WSAG4J_WSRF_CLIENT_CONFIG_FILE_DEFAULT = "/META-INF/wsrf-client.config";
114
115
116
117
118
119
120 private static String defaultAxis2ConfigurationPath = "/axis2-client";
121
122
123
124
125
126 public static int DEFAULT_TIME_OUT_IN_MILLI_SECONDS = 2 * 60 * 1000;
127
128
129
130 private static final Logger LOG = Logger.getLogger( Axis2SoapClient.class );
131
132
133
134
135 private static ConfigurationType config = null;
136
137
138
139
140 private static ConfigurationContext configurationContext = null;
141
142
143
144
145
146
147 public static ConfigurationContext getConfigurationContext()
148 {
149 return configurationContext;
150 }
151
152
153
154
155
156
157
158
159 public static void setConfigurationContext( ConfigurationContext configurationContext )
160 {
161 Axis2SoapClient.configurationContext = configurationContext;
162 }
163
164
165
166
167 private static final Element[] EMPTY_ARRAY = new Element[0];
168
169
170
171
172 private static final Messages MESSAGES = MessagesFactory.get( SimpleSoapClient.class );
173
174
175
176
177 private Properties properties = new Properties();
178
179
180
181
182 private final ISecurityProperties securityProperties;
183
184 private Crypto crypto;
185
186
187
188
189
190
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
202
203 protected void initialize()
204 {
205
206
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
221
222 synchronized ( Axis2SoapClient.class )
223 {
224
225 try
226 {
227
228
229
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
253
254
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
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
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
316 headers = additionalHeaders;
317 }
318 else
319 {
320
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
336 return super.createMessage( source, destination, action, bodyElements, headers );
337 }
338
339
340
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
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
383
384 soapResponse = doAxisCall( soapRequest, dest, wsaAction );
385 }
386
387
388
389
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
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
435
436 Element responseBody = XmlUtils.getElement( soapResponse, SoapConstants.BODY_QNAME );
437 return XmlUtils.getAllElements( responseBody );
438 }
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454 protected Element doAxisCall( Element soapRequest, EndpointReference dest, String wsaAction )
455 throws AxisFault
456 {
457
458
459
460 properties.remove( SecurityConstants.X509_SERVER_CERTIFICATE );
461 properties.remove( SecurityConstants.X509_SERVER_CERTIFICATE_CHAIN );
462
463
464
465
466
467
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
480
481
482 OperationClient oc = client.createClient( ServiceClient.ANON_OUT_IN_OP );
483
484 try
485 {
486
487
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
505
506 SOAPEnvelope envelope = (SOAPEnvelope) builder.getDocumentElement();
507
508
509 currentContext.setProperty( SecurityConstants.CRYPTO_SIGN, getCrypto() );
510 currentContext.setProperty( HTTPConstants.AUTO_RELEASE_CONNECTION, new Boolean( true ) );
511
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
521
522
523
524
525
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
542
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
553
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
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
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
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
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
668
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
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
778
779 public Properties getProperties()
780 {
781 return properties;
782 }
783
784
785
786
787
788 public void setProperties( Properties properties )
789 {
790 this.properties = properties;
791 }
792
793
794
795
796 public Crypto getCrypto()
797 {
798 return crypto;
799 }
800
801
802
803
804
805
806
807
808 public static String getDefaultAxis2ConfigurationPath()
809 {
810 return defaultAxis2ConfigurationPath;
811 }
812
813
814
815
816
817
818
819
820
821
822
823
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
846
847
848
849
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 }