JaxWsClientInitializer.java

  1. /*
  2.  * #%L
  3.  * wcm.io
  4.  * %%
  5.  * Copyright (C) 2016 wcm.io
  6.  * %%
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *      http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * #L%
  19.  */
  20. package io.wcm.caravan.jaxws.consumer;

  21. import java.io.IOException;
  22. import java.security.GeneralSecurityException;

  23. import javax.net.ssl.KeyManagerFactory;
  24. import javax.net.ssl.TrustManagerFactory;
  25. import javax.xml.ws.BindingProvider;

  26. import org.apache.commons.lang3.StringUtils;
  27. import org.apache.commons.lang3.builder.EqualsBuilder;
  28. import org.apache.commons.lang3.builder.HashCodeBuilder;
  29. import org.apache.cxf.configuration.jsse.TLSClientParameters;
  30. import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
  31. import org.apache.cxf.endpoint.Client;
  32. import org.apache.cxf.frontend.ClientFactoryBean;
  33. import org.apache.cxf.frontend.ClientProxy;
  34. import org.apache.cxf.interceptor.Fault;
  35. import org.apache.cxf.jaxws.JaxWsClientProxy;
  36. import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
  37. import org.apache.cxf.message.Message;
  38. import org.apache.cxf.phase.AbstractPhaseInterceptor;
  39. import org.apache.cxf.phase.Phase;
  40. import org.apache.cxf.transport.http.HTTPConduit;
  41. import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
  42. import org.apache.cxf.transports.http.configuration.ProxyServerType;
  43. import org.apache.cxf.ws.addressing.AddressingProperties;
  44. import org.apache.cxf.ws.addressing.AttributedURIType;
  45. import org.apache.cxf.ws.addressing.JAXWSAConstants;
  46. import org.apache.cxf.ws.addressing.WSAddressingFeature;
  47. import org.osgi.annotation.versioning.ConsumerType;

  48. import io.wcm.caravan.jaxws.consumer.impl.CertificateLoader;
  49. import io.wcm.caravan.jaxws.consumer.impl.OsgiAwareJaxWsClientFactoryBean;

  50. /**
  51.  * Default JAX-WS SOAP client initializer
  52.  */
  53. @ConsumerType
  54. public class JaxWsClientInitializer {

  55.   // Use to disable jaxb default validation for CXF
  56.   private static final String JAXB_VALIDATION = "set-jaxb-validation-event-handler";
  57.   private static final String SCHEMA_VALIDATION = "schema-validation-enabled";

  58.   /**
  59.    * List of properties of this class that contain sensitive information which should not be logged.
  60.    */
  61.   public static final String[] SENSITIVE_PROPERTY_NAMES = new String[] {
  62.       "proxyPassword",
  63.       "certstorePassword",
  64.       "trustStorePassword"
  65.   };

  66.   private int connectTimeout;
  67.   private int socketTimeout;
  68.   private String httpUser;
  69.   private String httpPassword;
  70.   private String proxyHost;
  71.   private int proxyPort;
  72.   private String proxyUser;
  73.   private String proxyPassword;
  74.   private String sslContextType = CertificateLoader.SSL_CONTEXT_TYPE_DEFAULT;
  75.   private String keyManagerType = CertificateLoader.KEY_MANAGER_TYPE_DEFAULT;
  76.   private String keyStoreType = CertificateLoader.KEY_STORE_TYPE_DEFAULT;
  77.   private String keyStoreProvider;
  78.   private String keyStorePath;
  79.   private String keyStorePassword;
  80.   private String trustManagerType = CertificateLoader.TRUST_MANAGER_TYPE_DEFAULT;
  81.   private String trustStoreType = CertificateLoader.TRUST_STORE_TYPE_DEFAULT;
  82.   private String trustStoreProvider;
  83.   private String trustStorePath;
  84.   private String trustStorePassword;
  85.   private String wsAddressingToUri;
  86.   private boolean ignoreUnexpectedElements;
  87.   private boolean allowChunking = true;

  88.   private transient TLSClientParameters tlsClientParameters;

  89.   /**
  90.    * Initialize JAXWS proxy factory bean
  91.    * @param factory JAXWS Proxy factory bean
  92.    */
  93.   public void initializeFactory(JaxWsProxyFactoryBean factory) {

  94.     // set outgoing security (username/password)
  95.     if (StringUtils.isNotEmpty(getHttpUser())) {
  96.       factory.setUsername(getHttpUser());
  97.       factory.setPassword(getHttpPassword());
  98.     }

  99.     // enable WS-addressing
  100.     if (StringUtils.isNotEmpty(getWSAddressingToUri())) {
  101.       factory.getFeatures().add(new WSAddressingFeature());
  102.     }

  103.   }

  104.   /**
  105.    * Initialize SOAP client pFactory and create SOAP client object instance.
  106.    * @param factory JAXWS proxy pFactory bean (has to be already initialized)
  107.    * @return SOAP client object
  108.    */
  109.   public Object createClient(JaxWsProxyFactoryBean factory) {
  110.     try {

  111.       // create port object
  112.       Object portObject = createPortObject(factory);

  113.       // initialize endpiont client
  114.       Client endpointClient = ClientProxy.getClient(portObject);
  115.       initializeEndpointClient(endpointClient);

  116.       // set http settings
  117.       HTTPConduit httpConduit = (HTTPConduit)endpointClient.getConduit();
  118.       initializeHttpConduit(httpConduit);

  119.       // make sure request context is per thread local
  120.       // see http://cxf.apache.org/faq.html#FAQ-AreJAXWSclientproxiesthreadsafe%3F
  121.       ((BindingProvider)portObject).getRequestContext().put(JaxWsClientProxy.THREAD_LOCAL_REQUEST_CONTEXT, Boolean.TRUE);

  122.       // ignore unexpected elements and attributes on data binding
  123.       if (isIgnoreUnexpectedElements()) {
  124.         // disable schema validation to be upward-compatible to future schema changes
  125.         ((BindingProvider)portObject).getRequestContext().put(JAXB_VALIDATION, false);
  126.         ((BindingProvider)portObject).getRequestContext().put(SCHEMA_VALIDATION, false);
  127.       }

  128.       return portObject;
  129.     }
  130.     catch (Throwable ex) {
  131.       throw new JaxWsClientInitializeException("SOAP client initialization failed "
  132.           + "(" + factory.getServiceClass().getName() + ").", ex);
  133.     }
  134.   }

  135.   /**
  136.    * Create port object
  137.    * @param factory JAXWS proxy factory bean
  138.    * @return Port object
  139.    */
  140.   protected Object createPortObject(JaxWsProxyFactoryBean factory) {
  141.     return factory.create();
  142.   }

  143.   /**
  144.    * Initialize endpoint client
  145.    * @param endpointClient Endpoint client
  146.    */
  147.   protected void initializeEndpointClient(Client endpointClient) {

  148.     // set destination address for WS-addressing
  149.     if (StringUtils.isNotEmpty(getWSAddressingToUri())) {
  150.       endpointClient.getOutInterceptors().add(new AbstractPhaseInterceptor<Message>(Phase.SETUP) {

  151.         @Override
  152.         public void handleMessage(Message pMessage) throws Fault {
  153.           AttributedURIType uri = new AttributedURIType();
  154.           uri.setValue(getWSAddressingToUri());

  155.           AddressingProperties maps = new AddressingProperties();
  156.           maps.setTo(uri);

  157.           pMessage.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
  158.         }

  159.       });
  160.     }

  161.     // add interceptors
  162.     addInterceptors(endpointClient);

  163.   }

  164.   /**
  165.    * Add interceptors (e.g. for request/response logging)
  166.    * @param endpointClient Endpoint client
  167.    */
  168.   protected void addInterceptors(Client endpointClient) {
  169.     // can be overridden by subclasses
  170.   }

  171.   /**
  172.    * Initialize HTTP conduit
  173.    * @param httpConduit HTTP conduit
  174.    * @throws GeneralSecurityException security exception
  175.    * @throws IOException I/O exception
  176.    */
  177.   protected void initializeHttpConduit(HTTPConduit httpConduit) throws IOException, GeneralSecurityException {

  178.     HTTPClientPolicy clientPolicy = httpConduit.getClient();
  179.     clientPolicy.setAllowChunking(isAllowChunking());
  180.     if (getConnectTimeout() > 0) {
  181.       clientPolicy.setConnectionTimeout(getConnectTimeout());
  182.     }
  183.     if (getSocketTimeout() > 0) {
  184.       clientPolicy.setReceiveTimeout(getSocketTimeout());
  185.     }

  186.     // optionally enable proxy server
  187.     if (StringUtils.isNotEmpty(getProxyHost()) && getProxyPort() > 0) {
  188.       clientPolicy.setProxyServerType(ProxyServerType.HTTP);
  189.       clientPolicy.setProxyServer(getProxyHost());
  190.       clientPolicy.setProxyServerPort(getProxyPort());

  191.       // optionally define proxy authentication
  192.       if (StringUtils.isNotEmpty(getProxyUser())) {
  193.         ProxyAuthorizationPolicy proxyAuthentication = new ProxyAuthorizationPolicy();
  194.         proxyAuthentication.setUserName(getProxyUser());
  195.         proxyAuthentication.setPassword(getProxyPassword());
  196.         httpConduit.setProxyAuthorization(proxyAuthentication);
  197.       }
  198.     }

  199.     // setup TLS - enable certificate for WS access
  200.     if (CertificateLoader.isSslKeyManagerEnabled(this) || CertificateLoader.isSslTrustStoreEnbaled(this)) {
  201.       httpConduit.setTlsClientParameters(getTLSClientParameters());
  202.     }

  203.   }

  204.   /**
  205.    * Get JAX-WS client factory bean instance.
  206.    * @return Client factory bean.
  207.    */
  208.   public ClientFactoryBean createClientFactoryBean() {
  209.     return new OsgiAwareJaxWsClientFactoryBean();
  210.   }

  211.   /**
  212.    * @return Connection timeout in ms.
  213.    */
  214.   public final int getConnectTimeout() {
  215.     return this.connectTimeout;
  216.   }

  217.   /**
  218.    * @param value Connection timeout in ms.
  219.    */
  220.   public final void setConnectTimeout(int value) {
  221.     this.connectTimeout = value;
  222.   }

  223.   /**
  224.    * @return Response timeout in ms.
  225.    */
  226.   public final int getSocketTimeout() {
  227.     return this.socketTimeout;
  228.   }

  229.   /**
  230.    * @param value Response timeout in ms.
  231.    */
  232.   public final void setSocketTimeout(int value) {
  233.     this.socketTimeout = value;
  234.   }

  235.   /**
  236.    * @return Http basic authentication user.
  237.    */
  238.   public final String getHttpUser() {
  239.     return this.httpUser;
  240.   }

  241.   /**
  242.    * @param value Http basic authentication user.
  243.    */
  244.   public final void setHttpUser(String value) {
  245.     this.httpUser = value;
  246.   }

  247.   /**
  248.    * @return Http basic authentication password
  249.    */
  250.   public final String getHttpPassword() {
  251.     return this.httpPassword;
  252.   }

  253.   /**
  254.    * @param value Http basic authentication password
  255.    */
  256.   public final void setHttpPassword(String value) {
  257.     this.httpPassword = value;
  258.   }

  259.   /**
  260.    * @return Proxy host name
  261.    */
  262.   public final String getProxyHost() {
  263.     return this.proxyHost;
  264.   }

  265.   /**
  266.    * @param value Proxy host name
  267.    */
  268.   public final void setProxyHost(String value) {
  269.     this.proxyHost = value;
  270.   }

  271.   /**
  272.    * @return Proxy port
  273.    */
  274.   public final int getProxyPort() {
  275.     return this.proxyPort;
  276.   }

  277.   /**
  278.    * @param value Proxy port
  279.    */
  280.   public final void setProxyPort(int value) {
  281.     this.proxyPort = value;
  282.   }

  283.   /**
  284.    * @return Proxy user name
  285.    */
  286.   public final String getProxyUser() {
  287.     return this.proxyUser;
  288.   }

  289.   /**
  290.    * @param value Proxy user name
  291.    */
  292.   public final void setProxyUser(String value) {
  293.     this.proxyUser = value;
  294.   }

  295.   /**
  296.    * @return Proxy password
  297.    */
  298.   public final String getProxyPassword() {
  299.     return this.proxyPassword;
  300.   }

  301.   /**
  302.    * @param value Proxy password
  303.    */
  304.   public final void setProxyPassword(String value) {
  305.     this.proxyPassword = value;
  306.   }

  307.   /**
  308.    * @return SSL context type (default: TLS)
  309.    */
  310.   public final String getSslContextType() {
  311.     return this.sslContextType;
  312.   }

  313.   /**
  314.    * @param value SSL context type (default: TLS)
  315.    */
  316.   public final void setSslContextType(String value) {
  317.     this.sslContextType = value;
  318.     this.tlsClientParameters = null;
  319.   }

  320.   /**
  321.    * @return Key manager type (default: SunX509)
  322.    */
  323.   public final String getKeyManagerType() {
  324.     return this.keyManagerType;
  325.   }

  326.   /**
  327.    * @param value Key manager type (default: SunX509)
  328.    */
  329.   public final void setKeyManagerType(String value) {
  330.     this.keyManagerType = value;
  331.     this.tlsClientParameters = null;
  332.   }

  333.   /**
  334.    * @return Key store type (default: PKCS12)
  335.    */
  336.   public final String getKeyStoreType() {
  337.     return this.keyStoreType;
  338.   }

  339.   /**
  340.    * @param value Key store type (default: PKCS12)
  341.    */
  342.   public final void setKeyStoreType(String value) {
  343.     this.keyStoreType = value;
  344.     this.tlsClientParameters = null;
  345.   }

  346.   /**
  347.    * @return Key store provider (default: null = use first matching security provider)
  348.    */
  349.   public String getKeyStoreProvider() {
  350.     return this.keyStoreProvider;
  351.   }

  352.   /**
  353.    * @param value Key store provider (default: null = use first matching security provider)
  354.    */
  355.   public void setKeyStoreProvider(String value) {
  356.     this.keyStoreProvider = value;
  357.     this.tlsClientParameters = null;
  358.   }

  359.   /**
  360.    * @return Key store file path
  361.    */
  362.   public final String getKeyStorePath() {
  363.     return this.keyStorePath;
  364.   }

  365.   /**
  366.    * @param value Key store file path
  367.    */
  368.   public final void setKeyStorePath(String value) {
  369.     this.keyStorePath = value;
  370.     this.tlsClientParameters = null;
  371.   }

  372.   /**
  373.    * @return Key store password
  374.    */
  375.   public final String getKeyStorePassword() {
  376.     return this.keyStorePassword;
  377.   }

  378.   /**
  379.    * @param value Key store password
  380.    */
  381.   public final void setKeyStorePassword(String value) {
  382.     this.keyStorePassword = value;
  383.     this.tlsClientParameters = null;
  384.   }

  385.   /**
  386.    * @return Trust manager type (default: SunX509)
  387.    */
  388.   public final String getTrustManagerType() {
  389.     return this.trustManagerType;
  390.   }

  391.   /**
  392.    * @param value Trust manager type (default: SunX509)
  393.    */
  394.   public final void setTrustManagerType(String value) {
  395.     this.trustManagerType = value;
  396.     this.tlsClientParameters = null;
  397.   }

  398.   /**
  399.    * @return Trust store type (default: JKS)
  400.    */
  401.   public final String getTrustStoreType() {
  402.     return this.trustStoreType;
  403.   }

  404.   /**
  405.    * @param value Trust store type (default: JKS)
  406.    */
  407.   public final void setTrustStoreType(String value) {
  408.     this.trustStoreType = value;
  409.     this.tlsClientParameters = null;
  410.   }

  411.   /**
  412.    * @return Trust store provider (default: null = use first matching security provider)
  413.    */
  414.   public String getTrustStoreProvider() {
  415.     return this.trustStoreProvider;
  416.   }

  417.   /**
  418.    * @param value Trust store provider (default: null = use first matching security provider)
  419.    */
  420.   public void setTrustStoreProvider(String value) {
  421.     this.trustStoreProvider = value;
  422.     this.tlsClientParameters = null;
  423.   }

  424.   /**
  425.    * @return Trust store file path
  426.    */
  427.   public final String getTrustStorePath() {
  428.     return this.trustStorePath;
  429.   }

  430.   /**
  431.    * @param value Trust store file path
  432.    */
  433.   public final void setTrustStorePath(String value) {
  434.     this.trustStorePath = value;
  435.     this.tlsClientParameters = null;
  436.   }

  437.   /**
  438.    * @return Trust store password
  439.    */
  440.   public final String getTrustStorePassword() {
  441.     return this.trustStorePassword;
  442.   }

  443.   /**
  444.    * @param vaule Trust store password
  445.    */
  446.   public final void setTrustStorePassword(String vaule) {
  447.     this.trustStorePassword = vaule;
  448.     this.tlsClientParameters = null;
  449.   }

  450.   /**
  451.    * Create TLS client parameters based on given certstore path/password parameters.
  452.    * Caches the parameter in member variable of this factory.
  453.    * @return TLS client parameters
  454.    * @throws IOException I/O exception
  455.    * @throws GeneralSecurityException General security exception
  456.    */
  457.   public final TLSClientParameters getTLSClientParameters() throws IOException, GeneralSecurityException {
  458.     if (tlsClientParameters == null) {
  459.       TLSClientParameters tlsCP = new TLSClientParameters();

  460.       // initialize certstore
  461.       if (CertificateLoader.isSslTrustStoreEnbaled(this)) {
  462.         try {
  463.           KeyManagerFactory keyManagerFactory = CertificateLoader.getKeyManagerFactory(this);
  464.           tlsCP.setKeyManagers(keyManagerFactory.getKeyManagers());
  465.         }
  466.         catch (Throwable ex) {
  467.           throw new RuntimeException("Unable to initialize certificate store for SOAP endpoint.\n"
  468.               + "Please check configuration parameters 'certstorePath' and 'certstorePassword' in this config:\n"
  469.               + this.toString(), ex);
  470.         }
  471.       }

  472.       // initialize trustStore
  473.       if (CertificateLoader.isSslTrustStoreEnbaled(this)) {
  474.         try {
  475.           TrustManagerFactory trustManagerFactory = CertificateLoader.getTrustManagerFactory(this);
  476.           tlsCP.setTrustManagers(trustManagerFactory.getTrustManagers());
  477.         }
  478.         catch (Throwable ex) {
  479.           throw new RuntimeException("Unable to initialize trust store for SOAP endpoint.\n"
  480.               + "Please check configuration parameters 'trustStorePath' and 'trustStorePassword' in this config:\n"
  481.               + this.toString(), ex);
  482.         }
  483.       }

  484.       tlsClientParameters = tlsCP;
  485.     }
  486.     return tlsClientParameters;
  487.   }

  488.   /**
  489.    * @param value CXF TSL client parameters
  490.    */
  491.   public final void setTLSClientParameters(TLSClientParameters value) {
  492.     this.tlsClientParameters = value;
  493.   }

  494.   /**
  495.    * @return Addressing-To URI to be sent as WS-Adressing header
  496.    */
  497.   public final String getWSAddressingToUri() {
  498.     return this.wsAddressingToUri;
  499.   }

  500.   /**
  501.    * @param value Addressing-To URI to be sent as WS-Adressing header
  502.    */
  503.   public final void setWSAddressingToUri(String value) {
  504.     this.wsAddressingToUri = value;
  505.   }

  506.   /**
  507.    * @return If true compatibility mode for WSDL/schema changes is activated.
  508.    *         If the SOAP server returns unknown XML element they are ignored during validation.
  509.    */
  510.   public final boolean isIgnoreUnexpectedElements() {
  511.     return this.ignoreUnexpectedElements;
  512.   }

  513.   /**
  514.    * @param value If true compatibility mode for WSDL/schema changes is activated.
  515.    *          If the SOAP server returns unknown XML element they are ignored during validation.
  516.    */
  517.   public final void setIgnoreUnexpectedElements(boolean value) {
  518.     this.ignoreUnexpectedElements = value;
  519.   }

  520.   /**
  521.    * @return Allow HTTP 1.1 chunking
  522.    */
  523.   public final boolean isAllowChunking() {
  524.     return this.allowChunking;
  525.   }

  526.   /**
  527.    * @param value Allow HTTP 1.1 chunking
  528.    */
  529.   public final void setAllowChunking(boolean value) {
  530.     this.allowChunking = value;
  531.   }

  532.   @Override
  533.   public int hashCode() {
  534.     return HashCodeBuilder.reflectionHashCode(this, false);
  535.   }

  536.   @Override
  537.   public boolean equals(Object pObj) {
  538.     return EqualsBuilder.reflectionEquals(this, pObj, false);
  539.   }

  540. }