4. REST Client

Spring Configuration

The REST client configuration creates a RestTemplate and leverages the same marshallers that the server uses. The default configuration uses the JAXB marshaller, but if the Spring Profile for JSON is activated the JSON marshaller will be used for client requests.

META-INF/spring/client/rest-client-context.xml
                    
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p" 
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util
                           http://www.springframework.org/schema/util/spring-util.xsd">

    <import resource="classpath:/META-INF/spring/marshaller/jaxb2-marshaller-context.xml"/>
    
    <context:component-scan base-package="org.springbyexample.contact.web.client" />
    
    <bean id="httpClient" class="org.apache.http.impl.client.DefaultHttpClient">
        <constructor-arg>
            <bean class="org.apache.http.impl.conn.PoolingClientConnectionManager"/>
        </constructor-arg>
    </bean>
    
    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"
          p:messageConverters-ref="messageConvertersList">
        <constructor-arg>
            <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
                <constructor-arg ref="httpClient"/>
            </bean>
        </constructor-arg>
    </bean>

    <util:list id="messageConvertersList">
        <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"
              p:supportedMediaTypes="application/xml">
            <property name="marshaller" ref="marshaller" />
            <property name="unmarshaller" ref="marshaller" />
        </bean>
    </util:list>
        
    <beans profile="rest-json">
        <import resource="classpath:/META-INF/spring/mvc/rest-json-converter-context.xml"/>
        <import resource="classpath:/META-INF/spring/mvc/rest-json-type-converter-context.xml"/>
    
        <!-- since id is the same as XML list above, essentially overriding the other list -->
        <util:list id="messageConvertersList">
            <ref bean="mappingJacksonHttpMessageConverterWithType"/>
        </util:list>
    </beans>

</beans>
                    
                

Client Code

Example 5. RestClient in Spring by Example REST Module

The RestClient configures the RestTemplate with the default credentials, and also can create URLs from a URI.

                    
@Component
public class RestClient {

    final Logger logger = LoggerFactory.getLogger(getClass());

    private final RestTemplate template;
    private final RestClientProperties clientProperties;
    private final DefaultHttpClient httpClient;

    @Autowired
    public RestClient(RestTemplate template, RestClientProperties clientProperties,
                      DefaultHttpClient httpClient) {
        this.template = template;
        this.clientProperties = clientProperties;
        this.httpClient = httpClient;
    }

    @PostConstruct
    public void init() {
        setCredentials(clientProperties.getUsername(), clientProperties.getPassword());
    }

    /**
     * Gets rest template.
     */
    public RestTemplate getRestTemplate() {
        return template;
    }

    /**
     * Creates URL based on the URI passed in.
     */
    public String createUrl(String uri) {
        StringBuilder sb = new StringBuilder();

        sb.append(clientProperties.getUrl());
        sb.append(clientProperties.getApiPath());
        sb.append(uri);

        logger.debug("URL is '{}'.", sb.toString());

        return sb.toString();
    }

    /**
     * Set default credentials on HTTP client.
     */
    public void setCredentials(String userName, String password) {
        UsernamePasswordCredentials creds =
                new UsernamePasswordCredentials(clientProperties.getUsername(), clientProperties.getPassword());
        AuthScope authScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM);

        httpClient.getCredentialsProvider().setCredentials(authScope, creds);
    }

}
                    
                

Example 6. AbstractPersistenceFindClient

Abstract persistence client base for read-only operations.

                    
public abstract class AbstractPersistenceFindClient<R extends EntityResponseResult, FR extends EntityFindResponseResult>
        implements PersistenceFindMarshallingService<R, FR> {

    final Logger logger = LoggerFactory.getLogger(getClass());

    protected final RestClient client;

    private final String findByIdRequest;
    private final String findPaginatedRequest;
    private final String findRequest;
    protected final Class<R> responseClazz;
    protected final Class<FR> findResponseClazz;


    public AbstractPersistenceFindClient(RestClient client,
                                         String findByIdRequest, String findPaginatedRequest, String findRequest,
                                         Class<R> responseClazz, Class<FR> findResponseClazz) {
        this.client = client;
        this.findByIdRequest = findByIdRequest;
        this.findPaginatedRequest = findPaginatedRequest;
        this.findRequest = findRequest;
        this.responseClazz = responseClazz;
        this.findResponseClazz = findResponseClazz;
    }

    @Override
    public R findById(Integer id) {
        R response = null;

        String url = client.createUrl(findByIdRequest);

        logger.debug("REST client findById.  id={}  url='{}'", id, url);

        response = client.getRestTemplate().getForObject(url, responseClazz, createPkVars(id));

        return response;
    }

    @Override
    public FR find(int page, int pageSize) {
        FR response = null;

        String url = client.createUrl(findPaginatedRequest);

        logger.debug("REST client paginated find.  page={}  pageSize={}  url='{}'",
                     new Object[] { page, pageSize, url});

        response = client.getRestTemplate().getForObject(url, findResponseClazz, createPageVars(page, pageSize));

        return response;
    }

    @Override
    public FR find() {
        FR response = null;

        String url = client.createUrl(findRequest);

        logger.debug("REST client find. url='{}'", url);

        response = client.getRestTemplate().getForObject(url, findResponseClazz);

        return response;
    }

    /**
     * Create primary key request variables.
     */
    public Map<String, Long> createPkVars(long id) {
        return Collections.singletonMap(ID_VAR, id);
    }

    /**
     * Create page vars for a paginated request.
     */
    public Map<String, Integer> createPageVars(int page, int pageSize) {
        Map<String, Integer> result = new HashMap<String, Integer>();

        result.put(PAGE_VAR, page);
        result.put(PAGE_SIZE_VAR, pageSize);

        return result;
    }

}
                    
                

Example 7. AbstractPersistenceClient

Abstract persistence client base for persistent operations.

                    
public abstract class AbstractPersistenceClient<R extends EntityResponseResult, FR extends EntityFindResponseResult, S extends PkEntityBase>
        extends AbstractPersistenceFindClient<R, FR>
        implements PersistenceMarshallingService<R, FR, S> {

    private final String saveRequest;
    private final String updateRequest;
    private final String deletePkRequest;
    private final String deleteRequest;

    public AbstractPersistenceClient(RestClient client,
                                     String findByIdRequest, String findPaginatedRequest, String findRequest,
                                     String saveRequest, String updateRequest,
                                     String deletePkRequest, String deleteRequest,
                                     Class<R> responseClazz, Class<FR> findResponseClazz) {
        super(client,
              findByIdRequest, findPaginatedRequest, findRequest,
              responseClazz, findResponseClazz);

        this.saveRequest = saveRequest;
        this.updateRequest = updateRequest;
        this.deletePkRequest = deletePkRequest;
        this.deleteRequest = deleteRequest;
    }

    @Override
    public R create(S request) {
        R response = null;

        String url = client.createUrl(saveRequest);

        logger.debug("REST client save.  id={}  url='{}'", request.getId(), url);

        response =  client.getRestTemplate().postForObject(url, request, responseClazz);

        return response;
    }

    @Override
    public R update(S request) {
        R response = null;

        String url = client.createUrl(updateRequest);

        logger.debug("REST client update.  id={}  url='{}'", request.getId(), url);

        Map<String, Long> vars = createPkVars(request.getId());

        response = client.getRestTemplate().exchange(url, HttpMethod.PUT, new HttpEntity(request), responseClazz, vars).getBody();

        return response;
    }

    public R delete(Integer id) {
        R response = null;

        String url = client.createUrl(deletePkRequest);

        logger.debug("REST client delete.  id={}  url='{}'", id, url);

        Map<String, Long> vars = createPkVars(id);

        response = client.getRestTemplate().exchange(url, HttpMethod.DELETE, null, responseClazz, vars).getBody();

        return response;
    }

    @Override
    public R delete(S request) {
        throw new UnsupportedOperationException("Issue with DELETE and posting body.");

        ...
    }

}
                    
                

Example 8. PersonClient

The abstract base classes handle everything once all the URIs and expected responses are correctly set in the constructor.

                    
@Component
public class PersonClient extends AbstractPersistenceClient<PersonResponse, PersonFindResponse, Person>
        implements PersonMarshallingService {

    @Autowired
    public PersonClient(RestClient client) {
        super(client,
              FIND_BY_ID_REQUEST, FIND_PAGINATED_REQUEST, FIND_REQUEST,
              SAVE_REQUEST, UPDATE_REQUEST, DELETE_PK_REQUEST, DELETE_REQUEST,
              PersonResponse.class, PersonFindResponse.class);
    }

}