Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

We had a need to submit request header information on every request to a specified system when we submit a REST call. We used the Springframework RestTemplate to perform our REST call. The RestTemplate provides a nice, easy way to modify all outbound requests via the ClientHttpRequestInterceptor interface.

The contents of the interceptor is:

package com.javaninja.core.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;

/**
 * Interceptor for the Rest calls.  The interceptor adds the System-Key that is required in order to authenticate.
 */
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
	private Logger logger = LoggerFactory.getLogger(getClass());

    @Value("#{properties.systemKey}")
	private String systemKey;

	/**
	 * The interceptor adds the SYSTEM-Key that they are looking for on their end.
	 *
	 * Intercept the given request, and return a response. The given {@link ClientHttpRequestExecution} allows the
	 * interceptor to pass on the request and response to the next entity in the chain.
	 * <p>
	 * <p>A typical implementation of this method would follow the following pattern:
	 * <ol>
	 *     <li>Examine the {@linkplain HttpRequest request} and body</li>
	 *     <li>Optionally {@linkplain org.springframework.http.client.support.HttpRequestWrapper wrap} the request to filter HTTP attributes.</li>
	 *     <li>Optionally modify the body of the request.</li>
	 * 	   <li><strong>Either</strong>
	 * 	   <ul>
	 * 	       <li>execute the request using {@link ClientHttpRequestExecution#execute(HttpRequest, byte[])},</li>
	 * 	       <strong>or</strong>
	 * 	       <li>do not execute the request to block the execution altogether.</li>
	 * 	   </ul>
	 * 	   <li>Optionally wrap the response to filter HTTP attributes.</li>
	 * </ol>
	 * @param request   the request, containing method, URI, and headers
	 * @param body      the body of the request
	 * @param execution the request execution
	 * @return the response
	 * @throws IOException in case of I/O errors
	 */
	@Override
	public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
	throws IOException {
		HttpHeaders headers = request.getHeaders();
		logger.debug("System-Key : {}", systemKey);
		headers.add("System-Key", systemKey);
		return execution.execute(request, body);
	}
}

Configuring the interceptor is fairly easy. It involves two steps. The first step is to declare the list of interceptors.

    <util:list id="interceptors">
          <bean id="vendorRestTemplateInterceptor" class="com.javaninja.core.spring.RestTemplateInterceptor"/>
    </util:list>

The next step is to associate the interceptors with the desired RestTemplate.

    <bean id="vendorRestTemplate" class="org.springframework.web.client.RestTemplate"
         p:interceptors-ref="interceptors"/>

Once you have that, you can use the RestTemplate as you normally would. Every outgoing request will have the basic authorization header added to the request automatically. As a note, in that application, we had a RestTemplate with an interceptor that was used to communicate with the vendor and another RestTemplate that did not have an interceptor that was used to communicate with an internal system.

December 9th, 2015

Posted In: Java, java ninja, Javaninja, Spring

Tags: , , , , , , ,

Leave a Comment

Usually service integrations are via posting JSON or XML. I had a need to integrate with a service that used an HTTP form post. Wait, what? How do you do that? Springframework provides a MultiValueMap interface that is posted as form data. LinkedMultiValueMap is an implementation of the MultiValueMap interface. The MultiValueMap is backed by a LinkedHashMap as opposed to a Map. Hence, it uses add instead of put.

Let’s see it in some code…

MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
// properties that identify our system, etc
map.add("username", username);
map.add("password", password);
map.add("ageToCheck", ageToCheck);
map.add("firstName", kycModel.getFirstName());
map.add("lastName", kycModel.getLastName());
map.add("address", kycModel.getAddress());
map.add("city", kycModel.getCity());
map.add("state", kycModel.getState());
map.add("zip", kycModel.getZip());
map.add("dobMonth", kycModel.getDobMonth());
map.add("dobDay", kycModel.getDobDay());
map.add("dobYear", kycModel.getDobYear());
map.add("ssnLast4", kycModel.getSsnLast4());

Response response = restTemplate.postForObject(url, map, Response.class);

From there on, the RestTemplate works as it normally does.

September 22nd, 2015

Posted In: Java, java ninja, Javaninja, Spring

Tags: , , , ,

Leave a Comment

The usual usage of a Springframework RestTemplate is to get back an object. Sometimes the object will contain a list. What needs to happen when you need to get back a list of Strings? RestTemplate accepts a parameter that tells it what the data it receives will be put in. I used a String array. It converts the JSON object into a String[] and away I go.

                    String[] pokerTableNames =
                        restTemplate.getForObject(pokerSettingsService.getValueByName(PokerSettings.IGP_ADAPTER_URL) + "/poker/tableNames",
                            String[].class);

February 20th, 2015

Posted In: Java, java ninja, Javaninja, json, Spring

Tags: , , , , , , ,

Leave a Comment

Using the Springframework RestTemplate is usually fairly straight forward. In this case, it didn’t work. I had a URL that would work when I pasted it into the web browser and would also work in the Chrome Advanced Rest Client. Each time I tried to call it with a Spring Rest Template, I got a HTTP 406.

    /**
     * Gets the poker client content objects from dotCMS.
     * @return poker clients model object
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public PokerClientsModel getPokerClients() throws InvocationTargetException, IllegalAccessException {

        String url = settingRepository.findSettingByIdSetting(SettingEnum.HOST_URL.key()).getValue() +
            "/api/content/render/false/query/+structureName:PokerClient%20+" +
            "(conhost:915f2552-793a-4f17-96d0-f0955fcc698f%20conhost:SYSTEM_HOST)" +
            "%20+languageId:1*%20+deleted:false%20%20+working:true/orderby/modDate%20desc";

        DotCmsPokerClientResponse dotCmsPokerClientResponse = restTemplate.getForObject(url,
                                                                                        DotCmsPokerClientResponse.class);
        PokerClientsModel pokerClientsModel = new PokerClientsModel();
        PokerClient pokerClient;
        for (DotCmsPokerClient dotCmsPokerClient : dotCmsPokerClientResponse.getDotCmsPokerClients()) {
            pokerClient = new PokerClient();
            BeanUtils.copyProperties(pokerClient, dotCmsPokerClient);
            pokerClientsModel.getPokerClients().add(pokerClient);
        }

        return pokerClientsModel;

    }

That just plain didn’t work.

Method threw 'org.springframework.web.client.HttpClientErrorException' exception.
406
Not Acceptable

 size = 8
UTF-8
406 Not Acceptable
org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable

 size = 0

I thought about what the browser could be sending that I wasn’t. It hit me that the browser may be sending the content type as text/plain. It is a bit of a pain in the rear-end to send a content type on a get request. I eventually got to set the content type by using the Http Entity.

    /**
     * Gets the poker client content objects from dotCMS.
     * @return poker clients model object
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public PokerClientsModel getPokerClients() throws InvocationTargetException, IllegalAccessException {

        String url = settingRepository.findSettingByIdSetting(SettingEnum.HOST_URL.key()).getValue() +
            "/api/content/render/false/query/+structureName:PokerClient%20+" +
            "(conhost:915f2552-793a-4f17-96d0-f0955fcc698f%20conhost:SYSTEM_HOST)" +
            "%20+languageId:1*%20+deleted:false%20%20+working:true/orderby/modDate%20desc";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.TEXT_PLAIN);

        HttpEntity entity = new HttpEntity(headers);
        HttpEntity<String> dotCmsPokerClientResponse = restTemplate.exchange(url,
                                                                             HttpMethod.GET,
                                                                             entity,
                                                                             String.class);
        return pokerClientsModel;
    }

This did work better. No more HTTP 406, but the return object contained a blank contentlets array.

<200 OK,{"contentlets":[]},{Date=[Mon, 02 Feb 2015 15:26:09 GMT], Server=[Apache-Coyote/1.1], Content-Type=, Set-Cookie=[JSESSIONID=8CDC0C67730932FAEBBB729FBA5E2BBA; Path=/, ROUTEID=.1; path=/], Vary=[Accept-Encoding], Keep-Alive=[timeout=5, max=100], Connection=[Keep-Alive], Transfer-Encoding=[chunked]}>

Frustration set in and I was starting to get desperate. I got the idea of using HttpClient directly to make the call.

    /**
     * Gets the poker client content objects from dotCMS.
     * @return poker clients model object
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public PokerClientsModel getPokerClients() throws InvocationTargetException, IllegalAccessException {

        String url = settingRepository.findSettingByIdSetting(SettingEnum.HOST_URL.key()).getValue() +
            "/api/content/render/false/query/+structureName:PokerClient%20+" +
            "(conhost:915f2552-793a-4f17-96d0-f0955fcc698f%20conhost:SYSTEM_HOST)" +
            "%20+languageId:1*%20+deleted:false%20%20+working:true/orderby/modDate%20desc";

        return get(url);
    }

    /**
     * Performs a HTTP get of the specified URL.
     * @param url the desired URL.
     * @return Http response body as a string
     */
    protected String get(String url) {
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(url);
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        String responseBody = null;
        try {
            responseBody = httpClient.execute(httpGet, responseHandler);
        } catch (IOException e) {
            logger.error("IOException", e);
        } finally {
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            httpClient.getConnectionManager().shutdown();
        }
        return responseBody;
    }

Success!!!!!

{"contentlets":[{"stInode":"d5af8654-aa65-4f2f-b2cd-bc6bf54753d5","owner":"norris.shelton","lastReview":"2015-01-30 16:21:56.226","modUser":"norris.shelton","identifier":"42ee0e8c-bd3c-4bae-ac51-b7837dc8e832","version":"000.000.000","clientType":"android","sortOrder":0,"modDate":"2015-01-30 16:21:56.238","host":"915f2552-793a-4f17-96d0-f0955fcc698f","whatsNew":"Initial Android Version","languageId":1,"inode":"ef33dbd6-2e47-44f7-a47f-daaa5c52e74b","folder":"SYSTEM_FOLDER"},{"stInode":"d5af8654-aa65-4f2f-b2cd-bc6bf54753d5","owner":"norris.shelton","lastReview":"2015-01-30 16:21:41.265","modUser":"norris.shelton","identifier":"b670a674-fb54-425a-93b8-432c8443e02d","version":"000.000.000","clientType":"ios","sortOrder":0,"modDate":"2015-01-30 16:21:41.278","host":"915f2552-793a-4f17-96d0-f0955fcc698f","whatsNew":"Initial IOS version","languageId":1,"inode":"929fb049-1c85-4376-bc9f-143981466a0a","folder":"SYSTEM_FOLDER"},{"stInode":"d5af8654-aa65-4f2f-b2cd-bc6bf54753d5","owner":"norris.shelton","lastReview":"2015-01-30 16:21:29.787","modUser":"norris.shelton","identifier":"df0ce56f-8e43-4169-9632-eb05fddd442f","version":"000.000.000","clientType":"flash","sortOrder":0,"modDate":"2015-01-30 16:21:29.8","host":"915f2552-793a-4f17-96d0-f0955fcc698f","whatsNew":"Initial Flash version","languageId":1,"inode":"8ff7fe3f-8064-42cf-8d9a-eaf6f65b52f9","folder":"SYSTEM_FOLDER"}]}

February 2nd, 2015

Posted In: Java, java ninja, Javaninja

Tags: , , , , , , , ,

Leave a Comment

LinkedIn Auto Publish Powered By : XYZScripts.com