Dienstag, 15. Mai 2018

Java: How to do SSL, Basic Authentication and log Request Response in XML/JSON with Jersey JAX WS RS Client

//Example with XML, SSL, Basic Auth


import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.WebTarget;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;

import com.mycompany.exception.MyException;

/**
 *         Purpose of this class is to use jersey/jaxb related connection to an external API via REST WS.
 */
public class RestClient {

    private static final Logger LOG = LogManager.getLogger(RestClient.class);


    private int connectTimeout = 30000;
    private int readTimeout = 30000;
   
private String uri;   
    protected WebTarget service;
    protected MyConfiguration config;
    private Client client;

     public void initialize() throws MyException {
        if (uri == null || uri.trim().isEmpty()) {
            uri = "https://myapi";
        }

        if (!(uri != null && !uri.trim().isEmpty())) {
            throw new MyException("[x] uri wrong: " + uri);
        }

        ClientBuilder builder = ClientBuilder.newBuilder();
        String proxy = "172.00.00.00:1234";
        if (proxy != null) {
            ClientConfig clientConfig = new ClientConfig();
            clientConfig.property(ClientProperties.PROXY_URI, proxy);
            builder = builder.withConfig(clientConfig);
        }
        builder = builder.sslContext(initializeHttpClientWithKeyStoreAndCert());

        client = builder.build();
        client.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout);
        client.property(ClientProperties.READ_TIMEOUT, readTimeout);

        client.register((ClientResponseFilter) (requestContext, responseContext) -> responseContext.getHeaders()
                .putSingle("Content-Type", "application/xml"));

        client.register(new EntityLoggingFilter());

        service = client.target(uri);
        LOG.debug("RestClient initialized.");
    }

    private TrustManager[] getTrustManager() throws Exception {
        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("com/mycompany/my_keystore.jks")) {
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(inputStream, "somepassword".toCharArray());
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            return trustManagers;
        }
    }

    private SSLContext initializeHttpClientWithKeyStoreAndCert() throws MyException {
        try {

            SSLContext sslContext = SSLContext.getInstance("SSL");
            try {
                sslContext.init(null, getTrustManager(), null);
            } catch (Exception e) {
                throw new MyException("[x] Failed to create SSL Context. ", e);
            }

            return sslContext;

        } catch (NoSuchAlgorithmException e) {
            LOG.error(e);
            throw new MyException(e.getClass().getSimpleName() + " while Creating HttpClient: " + e.getMessage(), e);
        }
    }

}



==============  POST a request POJO =====================

    public MyResponsePOJO postRequest(MyRequestPojo myrequestPojo) {
       

        // credentials shown for teaching purpose. Usually they are stored in a properties file or config.
        String basicAuthUser = "someUser";
        String basicAuthPass = "somePassword";
        String base64Creds = Base64.getEncoder()

                                                        .encodeToString((basicAuthUser + ":" + basicAuthPass).getBytes("UTF-8"));

        Response response = service.path("mypath").request().accept("application/xml")

                                                     .header("Content-type", "application/xml")
                                                     .header("Authorization", "Basic " + base64Creds).post(Entity.xml(myRequestPojo));

       return response.readEntity(MyResponsePOJO.class);
      
    }



============== LOG response and request bodies in XML or JSON with the RequestResponseLoggingFilter =======

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;

import javax.annotation.Priority;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;

/**
 * @author tim 

 * 
 * Purpose is to log request and response payload as XML.
 */

@Provider
@Priority(Integer.MIN_VALUE)
public class RequestResponseLoggingFilter implements ClientRequestFilter, ClientResponseFilter, WriterInterceptor {

    private static final Logger LOG = Logger.getLogger(RequestResponseLoggingFilter.class.getName());

    private final int maximumSizeForEntity = 1024 * 8;

    private class StreamLogger extends FilterOutputStream {

        private final StringBuilder builder = new StringBuilder();
        private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        StreamLogger(OutputStream outputStream) {
            super(outputStream);
        }

        public StringBuilder writeEntity(Charset charset) {
            final byte[] pojo = byteArrayOutputStream.toByteArray();
            builder.append(new String(pojo, 0, pojo.length, charset));
            if (pojo.length > maximumSizeForEntity) {
                builder.append("...more...");
            }
            builder.append('\n');
            return builder;
        }

        @Override
        public void write(final int i) throws IOException {
            if (byteArrayOutputStream.size() <= maximumSizeForEntity) {
                byteArrayOutputStream.write(i);
            }
            out.write(i);
        }
    }

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        if (requestContext.hasEntity()) {
            final OutputStream stream = new StreamLogger(requestContext.getEntityStream());
            requestContext.setEntityStream(stream);
            requestContext.setProperty("EntityLoggingFilter.entityStream", stream);
        } else {
            // nop
        }
    }

    @Override
    public void filter(ClientRequestContext requestCtx, ClientResponseContext responseCtx) throws IOException {
        final StringBuilder stringBuilder = new StringBuilder();
        if (responseCtx.hasEntity()) {
            responseCtx.setEntityStream(logIncoming(stringBuilder, responseCtx.getEntityStream(), StandardCharsets.UTF_8));
            LOG.info(stringBuilder.toString());
        }

    }

    @Override
    public void aroundWriteTo(WriterInterceptorContext ctx) throws IOException, WebApplicationException {
        final StreamLogger stream = (StreamLogger) ctx.getProperty("EntityLoggingFilter.entityStream");
        ctx.proceed();
        if (stream != null) {
            LOG.info(stream.writeEntity(StandardCharsets.UTF_8).toString());
        } else {
            // nop
        }
    }

    private InputStream logIncoming(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {
        if (!stream.markSupported()) {
            stream = new BufferedInputStream(stream);
        }
        stream.mark(maximumSizeForEntity + 1);
        final byte[] pojo = new byte[maximumSizeForEntity + 1];
        final int entitySize = stream.read(pojo);
        b.append(new String(pojo, 0, Math.min(entitySize, maximumSizeForEntity), charset));
        if (entitySize > maximumSizeForEntity) {
            b.append("...more...");
        } else {
            // nop
        }
        b.append('\n');
        stream.reset();
        return stream;
    }
}


================ DEPENDENCIES ================================

org="org.apache.logging.log4j" name="log4j" version = 2.5
org="org.glassfish.jersey" name="jersey" version =2.25.1
org="org.eclipse.persistence" name="eclipse-persistence" version = 2.6.0


<!-- required for Eclipse moxy, jaxb alternative) -->
org="javax.validation" name="validation-api" version = 1.1.0.Final





Keine Kommentare:

Kommentar veröffentlichen

NEW BLOG! http://cleancode.consulting/

Dear Reader, This Blog is closed and remains as an archive. Please find our new Blog at  http://cleancode.consulting/