org.spring.data.gemfire.rest.GemFireRestInterfaceTest.java Source code

Java tutorial

Introduction

Here is the source code for org.spring.data.gemfire.rest.GemFireRestInterfaceTest.java

Source

/*
 * Copyright 2014-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.spring.data.gemfire.rest;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import javax.annotation.Resource;

import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionFactory;
import com.gemstone.gemfire.cache.RegionService;
import com.gemstone.gemfire.pdx.PdxInstance;
import com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer;

import org.codeprimate.io.FileSystemUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

/**
 * The GemFireRestInterfaceTest class is a test suite of test cases testing the contract and functionality of the
 * GemFire Developer REST API, mixing Java clients, this test GemFire's Cache Region API, along with
 * a REST-based client, also this test using Spring's RestTemplate, testing the proper interaction, especially
 * in the case of an application domain object type having a java.util.Date property.
 *
 * @author John Blum
 * @see org.junit.Test
 * @see org.junit.runner.RunWith
 * @see org.springframework.test.context.ContextConfiguration
 * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
 * @see org.springframework.web.client.RestTemplate
 * @see com.gemstone.gemfire.cache.Cache
 * @see com.gemstone.gemfire.cache.Region
 * @see com.gemstone.gemfire.pdx.PdxInstance
 * @see com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer
 * @since 1.0.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class GemFireRestInterfaceTest {

    protected static int DEFAULT_HTTP_SERVICE_PORT = 8189;

    protected static final String REST_API_SERVICE_ENDPOINT = "http://localhost:%1$d/gemfire-api/v1/%2$s/%3$s";
    protected static final String UTF_8 = "UTF-8";

    @Autowired
    private Cache gemfireCache;

    @Autowired
    private ObjectMapper objectMapper;

    @Resource(name = "gemfireProperties")
    private Properties gemfireProperties;

    @Resource(name = "People")
    private Region<String, Object> people;

    @Before
    public void setupGemFire() {
        if (gemfireCache == null) {
            gemfireProperties = (gemfireProperties != null ? gemfireProperties : new Properties());

            gemfireCache = new CacheFactory()
                    //.setPdxSerializer(new ReflectionBasedAutoSerializer(Person.class.getPackage().getName().concat(".*")))
                    .setPdxSerializer(
                            new ReflectionBasedAutoSerializer(Person.class.getName().replaceAll("\\$", ".")))
                    .setPdxReadSerialized(true).setPdxIgnoreUnreadFields(false)
                    .set("name", getClass().getSimpleName()).set("mcast-port", "0").set("log-level", "config")
                    .set("http-service-bind-address", "localhost")
                    .set("http-service-port", String.valueOf(getHttpServicePort()))
                    //.set("http-service-ssl-enabled", "false")
                    .set("start-dev-rest-api", "true").create();

            RegionFactory<String, Object> peopleRegionFactory = gemfireCache.createRegionFactory();

            peopleRegionFactory.setDataPolicy(DataPolicy.PARTITION);
            peopleRegionFactory.setKeyConstraint(String.class);
            peopleRegionFactory.setValueConstraint(Object.class);

            people = peopleRegionFactory.create("People");
        }
    }

    @After
    public void tearDown() {
        gemfireCache.close();
    }

    protected synchronized int getHttpServicePort() {
        try {
            return Integer.parseInt(StringUtils.trimWhitespace(gemfireProperties.getProperty("http-service-port")));
        } catch (NumberFormatException ignore) {
            int httpServicePort = getHttpServicePort(DEFAULT_HTTP_SERVICE_PORT);
            gemfireProperties.setProperty("http-service-port", String.valueOf(httpServicePort));
            return httpServicePort;
        }
    }

    private int getHttpServicePort(final int defaultPort) {
        int httpServicePort = (1024 + new Random(System.currentTimeMillis()).nextInt(65535 - 1024));
        return (httpServicePort > 1024 && httpServicePort < 65536 ? httpServicePort : defaultPort);
    }

    protected ObjectMapper getObjectMapper() {
        if (objectMapper == null) {
            Jackson2ObjectMapperFactoryBean objectMapperFactoryBean = new Jackson2ObjectMapperFactoryBean();

            objectMapperFactoryBean.setFailOnEmptyBeans(true);
            objectMapperFactoryBean.setFeaturesToEnable(Feature.ALLOW_COMMENTS);
            objectMapperFactoryBean.setFeaturesToEnable(Feature.ALLOW_SINGLE_QUOTES);
            objectMapperFactoryBean.setFeaturesToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
            objectMapperFactoryBean.setFeaturesToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
            objectMapperFactoryBean.setIndentOutput(true);
            objectMapperFactoryBean.setSimpleDateFormat("MM/dd/yyyy");
            objectMapperFactoryBean.afterPropertiesSet();

            objectMapper = objectMapperFactoryBean.getObject();
        }

        return objectMapper;
    }

    protected Region<String, Object> getPeopleRegion() {
        assertNotNull("The 'People' Region was not properly initialized!", people);
        return people;
    }

    protected String getAdhocQueryRestApiEndpoint(final String query) {
        return getAdhocQueryRestApiEndpoint(getHttpServicePort(), query);
    }

    protected String getAdhocQueryRestApiEndpoint(final int httpServicePort, final String query) {
        return String.format(REST_API_SERVICE_ENDPOINT, httpServicePort, "queries",
                String.format("adhoc?q=%1$s", query));
    }

    protected String getRegionGetRestApiEndpoint(final Region<?, ?> region, final String key) {
        return getRegionGetRestApiEndpoint(getHttpServicePort(), region, key);
    }

    protected String getRegionGetRestApiEndpoint(final int httpServicePort, final Region<?, ?> region,
            final String key) {
        return String.format(REST_API_SERVICE_ENDPOINT, httpServicePort, region.getName(), key);
    }

    protected Date createDate(final int year, final int month, final int dayOfMonth) {
        Calendar dateTime = Calendar.getInstance();
        dateTime.clear();
        dateTime.set(Calendar.YEAR, year);
        dateTime.set(Calendar.MONTH, month);
        dateTime.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        return dateTime.getTime();
    }

    protected Person createPerson(final String firstName, final String lastName) {
        return createPerson(firstName, lastName, null);
    }

    protected Person createPerson(final String firstName, final String lastName, final Date birthDate) {
        return new Person(firstName, lastName, birthDate);
    }

    protected RestTemplate createRestTemplate() {
        MappingJackson2HttpMessageConverter httpMessageConverter = new MappingJackson2HttpMessageConverter();

        httpMessageConverter.setObjectMapper(getObjectMapper());

        return setErrorHandler(
                new RestTemplate(Collections.<HttpMessageConverter<?>>singletonList(httpMessageConverter)));
    }

    @SuppressWarnings("deprecation")
    private RestTemplate setErrorHandler(final RestTemplate restTemplate) {
        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            private final Set<HttpStatus> errorStatuses = new HashSet<>();

            /* non-static */ {
                errorStatuses.add(HttpStatus.BAD_REQUEST);
                errorStatuses.add(HttpStatus.UNAUTHORIZED);
                errorStatuses.add(HttpStatus.FORBIDDEN);
                errorStatuses.add(HttpStatus.NOT_FOUND);
                errorStatuses.add(HttpStatus.METHOD_NOT_ALLOWED);
                errorStatuses.add(HttpStatus.NOT_ACCEPTABLE);
                errorStatuses.add(HttpStatus.REQUEST_TIMEOUT);
                errorStatuses.add(HttpStatus.CONFLICT);
                errorStatuses.add(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
                errorStatuses.add(HttpStatus.REQUEST_URI_TOO_LONG);
                errorStatuses.add(HttpStatus.UNSUPPORTED_MEDIA_TYPE);
                errorStatuses.add(HttpStatus.TOO_MANY_REQUESTS);
                errorStatuses.add(HttpStatus.INTERNAL_SERVER_ERROR);
                errorStatuses.add(HttpStatus.NOT_IMPLEMENTED);
                errorStatuses.add(HttpStatus.BAD_GATEWAY);
                errorStatuses.add(HttpStatus.SERVICE_UNAVAILABLE);
            }

            @Override
            public boolean hasError(final ClientHttpResponse response) throws IOException {
                return errorStatuses.contains(response.getStatusCode());
            }

            @Override
            public void handleError(final ClientHttpResponse response) throws IOException {
                System.err.printf("%1$d - %2$s%n", response.getRawStatusCode(), response.getStatusText());
                System.err.println(readBody(response));
            }

            private String readBody(final ClientHttpResponse response) throws IOException {
                BufferedReader responseBodyReader = null;

                try {
                    responseBodyReader = new BufferedReader(new InputStreamReader(response.getBody()));

                    StringBuilder buffer = new StringBuilder();
                    String line;

                    while ((line = responseBodyReader.readLine()) != null) {
                        buffer.append(line).append(System.getProperty("line.separator"));
                    }

                    return buffer.toString().trim();
                } finally {
                    FileSystemUtils.close(responseBodyReader);
                }
            }
        });

        return restTemplate;
    }

    @Test
    public void testRegionObjectWithDatePropertyAccessedWithRestApi() throws Exception {
        String key = "1";
        Person jonDoe = createPerson("Jon", "Doe", createDate(1977, Calendar.OCTOBER, 31));

        assertTrue(getPeopleRegion().isEmpty());

        getPeopleRegion().put(key, jonDoe);

        assertFalse(getPeopleRegion().isEmpty());
        assertEquals(1, getPeopleRegion().size());
        assertTrue(getPeopleRegion().containsKey(key));

        Object jonDoeRef = getPeopleRegion().get(key);

        assertTrue(jonDoeRef instanceof PdxInstance);
        assertEquals(jonDoe.getClass().getName(), ((PdxInstance) jonDoeRef).getClassName());
        assertEquals(jonDoe.getFirstName(), ((PdxInstance) jonDoeRef).getField("firstName"));
        assertEquals(jonDoe.getLastName(), ((PdxInstance) jonDoeRef).getField("lastName"));
        assertEquals(jonDoe.getBirthDate(), ((PdxInstance) jonDoeRef).getField("birthDate"));

        RestTemplate restTemplate = createRestTemplate();

        Person jonDoeResource = restTemplate.getForObject(getRegionGetRestApiEndpoint(getPeopleRegion(), key),
                Person.class);

        assertNotNull(jonDoeResource);
        assertNotSame(jonDoe, jonDoeResource);
        assertEquals(jonDoe, jonDoeResource);

        /*
        Object result = runQueryUsingApi(getPeopleRegion().getRegionService(), String.format("SELECT * FROM %1$s",
          getPeopleRegion().getFullPath()));
            
        System.out.printf("(OQL Query using API) Person is (%1$s)%n", result);
        */

        String url = getAdhocQueryRestApiEndpoint(
                String.format("SELECT * FROM %1$s", getPeopleRegion().getFullPath()));

        System.out.printf("URL (%1$s)%n", url);

        List<?> queryResults = restTemplate.getForObject(url, List.class);

        assertNotNull(queryResults);
        assertFalse(queryResults.isEmpty());
        assertEquals(1, queryResults.size());

        jonDoeResource = objectMapper.convertValue(queryResults.get(0), Person.class);

        assertNotNull(jonDoeResource);
        assertNotSame(jonDoe, jonDoeResource);
        assertEquals(jonDoe, jonDoeResource);
    }

    private Object runQueryUsingApi(final RegionService regionService, final String queryString) throws Exception {
        return regionService.getQueryService().newQuery(queryString).execute();
    }

    //@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
    //@JsonIgnoreProperties(ignoreUnknown = true)
    //@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@type")
    public static class Person {

        protected static final String DEFAULT_BIRTH_DATE_FORMAT_PATTERN = "MM/dd/yyyy";

        private Date birthDate;

        private String firstName;
        private String lastName;

        public Person() {
        }

        public Person(final String firstName, final String lastName) {
            this(firstName, lastName, null);
        }

        public Person(final String firstName, final String lastName, final Date birthDate) {
            setFirstName(firstName);
            setLastName(lastName);
            setBirthDate(birthDate);
        }

        public Date getBirthDate() {
            return birthDate;
        }

        public void setBirthDate(final Date birthDate) {
            Assert.isTrue(birthDate == null || birthDate.compareTo(Calendar.getInstance().getTime()) <= 0,
                    "A Person's date of birth cannot be after today!");
            this.birthDate = birthDate;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(final String firstName) {
            Assert.hasText(firstName, "The Person must have a first name!");
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(final String lastName) {
            Assert.hasText(firstName, "The Person must have a last name!");
            this.lastName = lastName;
        }

        protected final String format(final Date dateTime) {
            return format(dateTime, DEFAULT_BIRTH_DATE_FORMAT_PATTERN);
        }

        protected String format(final Date dateTime, final String dateFormatPattern) {
            return (dateTime == null ? null
                    : new SimpleDateFormat(StringUtils.hasText(dateFormatPattern) ? dateFormatPattern
                            : DEFAULT_BIRTH_DATE_FORMAT_PATTERN).format(dateTime));
        }

        @Override
        public boolean equals(final Object obj) {
            if (obj == this) {
                return true;
            }

            if (!(obj instanceof Person)) {
                return false;
            }

            Person that = (Person) obj;

            return ObjectUtils.nullSafeEquals(this.getFirstName(), that.getFirstName())
                    && ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName())
                    && ObjectUtils.nullSafeEquals(this.getBirthDate(), that.getBirthDate());
        }

        @Override
        public int hashCode() {
            int hashValue = 17;
            hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getFirstName());
            hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getLastName());
            hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getBirthDate());
            return hashValue;
        }

        @Override
        public String toString() {
            return String.format("{ @type = %1$s, firstName = %2$s, lastName = %3$s, birthDate = %4$s }",
                    getClass().getName(), getFirstName(), getLastName(), format(getBirthDate()));
        }
    }

}