Hi Jean,

So the 1st part is 100% correct way to create a thread safe client proxy, but 
with the 2nd one
we have an issue well documented here [1]. Since you only modify headers, the 
thread safe proxy 
should work, but you probably could avoid using the WebClient part (just use 
JAXRSClientFactoryBean)
directly:

   final JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean();
   factory.setServiceClass(SodexoApi.class);
   factory.setProviders(providers);
   factory.getOutInterceptors().add(new LoggingOutInterceptor());
   factory.getInInterceptors().add(new LoggingInInterceptor());
   factory.setThreadSafe(true)

   SodexoApi api = factory.create(SodexoApi.class);
   ClientConfiguration config = WebClient.getConfig(api);
   addTLSClientParameters(config.getHttpConduit());


Hope it helps!

[1] 
https://cxf.apache.org/docs/jax-rs-client-api.html#JAXRSClientAPI-ThreadSafety

 
Best Regards,
    Andriy Redko


JPU>  Apache CXF JAX-RS threadsafe clients

JPU> Hi Andriy,

JPU> I am struggling to understand threadsafety when creating/using JAX-RS
JPU> clients.

JPU> My intention is to:

JPU> 1.      Create a client once as I think it is heavy loaded:

JPU> o       We need to add providers and interceptors

JPU> o       We need to set TLS-client parameters

JPU> 2.      Then (re-)use this client in a threadsafe way for multiple
JPU> (possibly concurrent) invocations of service methods

JPU> So step ‘1.’ I have done by creating a static  threadsafe proxy using the
JPU> JAXRSClientFactory class.

JPU> Now the problem is step ‘2.’ How do I invoke this (static) client to make
JPU> sure that:

JPU> ·       Stuff under’1.’ is reused

JPU> ·       Reset and add headers specific for this invocation

JPU> ·       all is threadsafe

JPU> Currently, in my service method I create a client as follows:

JPU> SodexoApi* clientProxy* = 
JAXRSClientFactory.*fromClient*(WebClient.*client*
JPU> (*getClientProxy*()), SodexoApi.*class*);

JPU> Here getClientProxy() is the static proxy I created, covering for step ‘1.’
JPU> Stuff, that I want to re-use to create a proxy for the SodexoApi interface.

JPU> But I am doubting whether this is the correct way.

JPU> For the moment I don’t think I’ve a problem due to the fact that the
JPU> service methods are defined as ‘synchronized’ but that comes with a
JPU> performance penalty.

JPU> Below, a stripped version of the code I am using, to help you understanding
JPU> what I am trying to do:

JPU> public class SodexoApiClientImpl {

JPU>         private static final Logger LOG =
JPU> Logger.getLogger(SodexoApiClientImpl.class);



JPU>         /** custom HEADER field name for X-KMO-OPERATION-ID */

JPU>         public static final String HEADER_OPERATION_ID =
JPU> "X-KMO-OPERATION-ID";

JPU>         /** A threadsafe proxy to serve as {@link WebClient} for the {@link
JPU> SodexoApi} interface*/

JPU>         private static SodexoApi clientProxy;

JPU>         protected static synchronized SodexoApi getClientProxy() throws
JPU> GeneralSecurityException {

JPU>                 if (clientProxy == null) {

JPU>                         //Get the base address of the service endpoint

JPU>                         String baseAddress =
JPU> 
Configuration.getInstance().getItem(EmittentProperties.emmitent_service_endpoint);

JPU>                         clientProxy = getThreadsafeProxy(baseAddress);

JPU>                 }

JPU>                 return clientProxy;

JPU>         }

JPU>         /**

JPU>          * Create a proxy for the {@link SodexoApi} service endpoint

JPU>          *

JPU>          * @param baseAddress - The base URI of the SDX REST API endpoint.

JPU>          * @return The {@link SodexoApi} service endpoint proxy

JPU>          *

JPU>          * @throws GeneralSecurityException - when retrieving certificate
JPU> info fails

JPU>          */

JPU>         private static SodexoApi getThreadsafeProxy(String baseAddress)
JPU> throws GeneralSecurityException {

JPU>                 JacksonJsonProvider provider = new JacksonJsonProvider(new
JPU> CustomObjectMapper());

JPU>                 List<JacksonJsonProvider> providers = new
JPU> ArrayList<JacksonJsonProvider>();

JPU>                 providers.add(provider);

JPU>                 SodexoApi api = JAXRSClientFactory.create(baseAddress,
JPU> SodexoApi.class, providers,true);

JPU>                 Client client = WebClient.client(api);

JPU>                 ClientConfiguration config = WebClient.getConfig(client);

JPU>                 config.getOutInterceptors().add(new
JPU> LoggingOutInterceptor());

JPU>                 config.getInInterceptors().add(new LoggingInInterceptor());

JPU>                 addTLSClientParameters(config.getHttpConduit());

JPU>                 return api;

JPU>         }

JPU>         /**

JPU>          * Add {@link TLSClientParameters} to the {@link HTTPConduit}.

JPU>          * <p>In the Devoteam test environement the SSL certificates of
JPU> both KMOP and KmopEmittent

JPU>          * are self-signed with a CN not referring to their hostname.
JPU> Therefore in these environments

JPU>          * the check to verifiy whether the hostname matches the CN must be
JPU> disabled.</p>

JPU>          *

JPU>          * @param conduit The {@link HTTPConduit} handling the https
JPU> transport protocol.

JPU>          *

JPU>          * @throws GeneralSecurityException - when retrieving certificate
JPU> info fails

JPU>          */

JPU>         private static void addTLSClientParameters(HTTPConduit conduit)
JPU> throws GeneralSecurityException {

JPU>                 TLSClientParameters params =
JPU> conduit.getTlsClientParameters();

JPU>                 if (params == null) {

JPU>                         params = new TLSClientParameters();

JPU>                         conduit.setTlsClientParameters(params);

JPU>                 }

JPU>                 //1.0 set the trust Manager (the server certificate should
JPU> be included in the default ca-certs trust keystore

JPU>                 X509TrustManager tm =
JPU> TrustManagerUtils.getValidateServerCertificateTrustManager();

JPU>                 params.setTrustManagers(new TrustManager[] { tm });



JPU>                 //2.0 in case of m-TLS set the keyManager if required

JPU>                 try {

JPU>                         final String keystoreType =
JPU> 
Configuration.getInstance().getItem(KmoPortefeuilleProperties.kmopKeystoreType);

JPU>                         final String keystore =
JPU> 
Configuration.getInstance().getItem(KmoPortefeuilleProperties.kmopEncKeystore);

JPU>                         final String keystorePass =
JPU> 
Configuration.getInstance().getItem(KmoPortefeuilleProperties.kmopEncKeystorePass);

JPU>                         final String keyAlias =
JPU> 
Configuration.getInstance().getItem(KmoPortefeuilleProperties.kmopEncKeyAlias);

JPU>                         final String keyPass =
JPU> 
Configuration.getInstance().getItem(KmoPortefeuilleProperties.kmopEncKeyPass);

JPU>                         KeyStore ks = KeystoreUtils.loadKS(keystoreType,
JPU> keystore, keystorePass);

JPU>                         KeyManager km =
JPU> KeyManagerUtils.createClientKeyManager(ks, keyAlias, keyPass);

JPU>                         params.setKeyManagers(new KeyManager[] {km});

JPU>                 } catch (PropertyNotFoundException pe) {

JPU>                         //if any of the properties do not exist then either
JPU> m-TLS does not apply or there is a property misconfiguration

JPU>                         LOG.warn("Couldn't configure KeyManagers for the
JPU> client! This either indicates that m-TLS doesnt' apply or a property
JPU> misconifguration.");

JPU>                         LOG.warn(pe.getMessage(),pe);

JPU>                 } catch ( GeneralSecurityException e) {

JPU>                         LOG.error("Couldn't configure KeyManagers on the
JPU> HTTPConduit of the JAX-RS webclient: "+e.getMessage(),e);

JPU>                         throw e;

JPU>                 }

JPU>         }

JPU>         /**

JPU>          * Sends a CloseAccount service request to the peer endpoint
JPU> service.

JPU>          *

JPU>          * @param operationId - The unique {@link UUID identifier} for the
JPU> request message

JPU>          * @param authorisationId Long - The unique identifier of the
JPU> account at the peer side

JPU>          * @param accountKey The {@link AccountKey} request entity

JPU>          * @return An {@link Response} response object.

JPU>          */

JPU>         public synchronized Response closeAccount(UUID operationId, Long
JPU> authorisationId, AccountKey accountKey) {

JPU>                 try {

JPU>                         //Get a threadsafe SodexoAPI client proxy instance

JPU>                        * //UJ: Is this the correct way?*

JPU>                         SodexoApi clientProxy =
JPU> JAXRSClientFactory.fromClient(WebClient.client(getClientProxy()),
JPU> SodexoApi.class);



JPU>                         //Set the Bearer authorization header

JPU>                         String issuer =
JPU> 
Configuration.getInstance().getItem(EmittentProperties.emit_security_bearer_issuer);

JPU>                         String audience =
JPU> 
Configuration.getInstance().getItem(EmittentProperties.emit_security_bearer_audience);

JPU>                         String jws =
JPU> 
BearerUtils.createJwtSignedJose(BearerUtils.SDX_HEADER_TYPE,issuer,audience,
JPU> operationId.toString(),(PrivateKey)
JPU> BearerUtils.getKey(KeyName.kmopSignPrivKey));

JPU>                         WebClient.client(clientProxy)

JPU>                                         .reset()

JPU>                                         .header(HttpHeaders.AUTHORIZATION,
JPU> "Bearer " + jws);

JPU>                         return
JPU> clientProxy.closeAccount(operationId.toString(), authorisationId,
JPU> accountKey);

JPU>                 } catch (GeneralSecurityException e) {

JPU>                         StringBuilder bldr = new StringBuilder("Internal
JPU> error processing customerFund request (")


JPU> .append("operationId=").append(operationId.toString())


JPU> .append(",authorisationId=").append(authorisationId)

JPU>                                         .append("):
JPU> ").append(e.getMessage());

JPU>                         LOG.error(bldr.toString(), e);

JPU>                         return
JPU> 
Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();

JPU>                 }

JPU>         }

JPU> }

Reply via email to