org.jboss.aerogear.android.pipe.test.rest.RestAdapterTest.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.aerogear.android.pipe.test.rest.RestAdapterTest.java

Source

/**
 * JBoss, Home of Professional Open Source
 * Copyright Red Hat, Inc., and individual contributors.
 *
 * 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.jboss.aerogear.android.pipe.test.rest;

import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import junit.framework.Assert;

import org.jboss.aerogear.android.core.Callback;
import org.jboss.aerogear.android.core.Provider;
import org.jboss.aerogear.android.core.ReadFilter;
import org.jboss.aerogear.android.core.RecordId;
import org.jboss.aerogear.android.pipe.http.HeaderAndBody;
import org.jboss.aerogear.android.pipe.http.HttpProvider;
import org.jboss.aerogear.android.pipe.http.HttpProviderFactory;
import org.jboss.aerogear.android.pipe.rest.RestAdapter;
import org.jboss.aerogear.android.pipe.rest.RestfulPipeConfiguration;
import org.jboss.aerogear.android.pipe.test.helper.Data;
import org.jboss.aerogear.android.pipe.rest.gson.GsonRequestBuilder;
import org.jboss.aerogear.android.pipe.rest.gson.GsonResponseParser;
import org.jboss.aerogear.android.pipe.test.util.UnitTestUtils;
import org.jboss.aerogear.android.pipe.test.http.HttpStubProvider;
import org.jboss.aerogear.android.pipe.paging.WrappingPagedList;
import org.jboss.aerogear.android.pipe.Pipe;
import org.jboss.aerogear.android.pipe.paging.PageConfig;
import org.jboss.aerogear.android.pipe.paging.PagedList;
import org.json.JSONObject;

import android.graphics.Point;
import android.test.AndroidTestCase;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import static junit.framework.Assert.assertEquals;
import org.jboss.aerogear.android.pipe.module.ModuleFields;
import org.jboss.aerogear.android.pipe.module.PipeModule;
import org.jboss.aerogear.android.pipe.MarshallingConfig;
import org.jboss.aerogear.android.pipe.PipeManager;
import org.json.JSONArray;

public class RestAdapterTest extends AndroidTestCase {

    private static final String TAG = RestAdapterTest.class.getSimpleName();
    private static final String SERIALIZED_POINTS = "{\"points\":[{\"x\":0,\"y\":0},{\"x\":1,\"y\":2},{\"x\":2,\"y\":4},{\"x\":3,\"y\":6},{\"x\":4,\"y\":8},{\"x\":5,\"y\":10},{\"x\":6,\"y\":12},{\"x\":7,\"y\":14},{\"x\":8,\"y\":16},{\"x\":9,\"y\":18}],\"id\":\"1\"}";
    private static final String POINTS_ARRAY = "[{\"x\":0,\"y\":0},{\"x\":1,\"y\":2},{\"x\":2,\"y\":4},{\"x\":3,\"y\":6},{\"x\":4,\"y\":8},{\"x\":5,\"y\":10},{\"x\":6,\"y\":12},{\"x\":7,\"y\":14},{\"x\":8,\"y\":16},{\"x\":9,\"y\":18}]";
    private URL url;
    private final Provider<HttpProvider> stubHttpProviderFactory = new Provider<HttpProvider>() {
        @Override
        public HttpProvider get(Object... in) {
            return new HttpStubProvider((URL) in[0]);
        }
    };

    @Override
    public void setUp() throws MalformedURLException, Exception {
        super.setUp();
        url = new URL("http://server.com/context/");
    }

    public void testPipeURLProperty() throws Exception {
        Pipe<Data> restPipe = new RestAdapter<Data>(Data.class, url);
        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");
        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", stubHttpProviderFactory);
        assertEquals("verifying the given URL", "http://server.com/context/", restPipe.getUrl().toString());
    }

    public void testPipeFactoryPipeConfigEncoding() {
        try {
            RestfulPipeConfiguration config = PipeManager.config("data", RestfulPipeConfiguration.class)
                    .withUrl(url);
            config.getRequestBuilder().getMarshallingConfig().setEncoding(Charset.forName("UTF-16"));
            assertEquals(Charset.forName("UTF-16"),
                    config.getRequestBuilder().getMarshallingConfig().getEncoding());
            config.getRequestBuilder().getMarshallingConfig().setEncoding((Charset) null);
        } catch (IllegalArgumentException ignore) {
            return;
        }
        fail("Expected IllegalArgumentException");
    }

    public void testPipeFactoryPipeConfigGson()
            throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());
        GsonResponseParser<ListClassId> responseParser = new GsonResponseParser<ListClassId>(builder.create());

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url).responseParser(responseParser);

        Pipe<ListClassId> restPipe = config.forClass(ListClassId.class);
        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");
        Field gsonField = restRunner.getClass().getDeclaredField("responseParser");
        gsonField.setAccessible(true);
        GsonResponseParser gson = (GsonResponseParser) gsonField.get(restRunner);

        assertEquals(responseParser, gson);

    }

    public void testEncoding() throws Exception {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());
        final Charset utf_16 = Charset.forName("UTF-16");

        final HttpStubProvider provider = new HttpStubProvider(url,
                new HeaderAndBody(SERIALIZED_POINTS.getBytes(utf_16), new HashMap<String, Object>()));

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .requestBuilder(new GsonRequestBuilder(builder.create()));

        MarshallingConfig marshallingConfig = config.getRequestBuilder().getMarshallingConfig();
        marshallingConfig.setEncoding(utf_16);
        marshallingConfig = config.getResponseParser().getMarshallingConfig();
        marshallingConfig.setEncoding(utf_16);

        RestAdapter<ListClassId> restPipe = new RestAdapter<ListClassId>(ListClassId.class, url, config);

        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");

        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                return provider;
            }
        });

        runRead(restPipe);

    }

    public void testConfigSetEncoding() throws Exception {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());
        final Charset utf_16 = Charset.forName("UTF-16");

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class);
        config.withUrl(url).requestBuilder(new GsonRequestBuilder(builder.create())).getRequestBuilder()
                .getMarshallingConfig().setEncoding(utf_16);

        RestAdapter<ListClassId> restPipe = (RestAdapter<ListClassId>) config.forClass(ListClassId.class);

        assertEquals(utf_16, restPipe.getRequestBuilder().getMarshallingConfig().getEncoding());

    }

    public void testSingleObjectRead() throws Exception {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());
        HeaderAndBody response = new HeaderAndBody(SERIALIZED_POINTS.getBytes(), new HashMap<String, Object>());
        final HttpStubProvider provider = new HttpStubProvider(url, response);

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.requestBuilder(new GsonRequestBuilder(builder.create()));

        RestAdapter<ListClassId> restPipe = new RestAdapter<ListClassId>(ListClassId.class, url, config);
        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");
        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                return provider;
            }
        });
        List<ListClassId> result = runRead(restPipe);

        List<Point> returnedPoints = result.get(0).points;
        assertEquals(10, returnedPoints.size());

    }

    public void testSingleObjectReadWithNestedResult() throws Exception {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());
        HeaderAndBody response = new HeaderAndBody(
                ("{\"result\":{\"points\":" + SERIALIZED_POINTS + "}}").getBytes(), new HashMap<String, Object>());
        final HttpStubProvider provider = new HttpStubProvider(url, response);

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.requestBuilder(new GsonRequestBuilder(builder.create()));
        config.getResponseParser().getMarshallingConfig().setDataRoot("result.points");

        RestAdapter<ListClassId> restPipe = new RestAdapter<ListClassId>(ListClassId.class, url, config);

        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");
        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                return provider;
            }
        });
        List<ListClassId> result = runRead(restPipe);

        List<Point> returnedPoints = result.get(0).points;
        assertEquals(10, returnedPoints.size());

    }

    public void testReadArray() throws Exception {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());
        HeaderAndBody response = new HeaderAndBody((POINTS_ARRAY).getBytes(), new HashMap<String, Object>());
        final HttpStubProvider provider = new HttpStubProvider(url, response);

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.requestBuilder(new GsonRequestBuilder(builder.create()));
        config.getRequestBuilder().getMarshallingConfig().setDataRoot("");

        RestAdapter<Point> restPipe = new RestAdapter<Point>(Point.class, url, config);

        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");
        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                return provider;
            }
        });
        List<Point> result = runRead(restPipe);

        assertEquals(10, result.size());

    }

    public void testGsonBuilderProperty() throws Exception {
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());

        final ByteArrayOutputStream request = new ByteArrayOutputStream();

        final HttpStubProvider provider = new HttpStubProvider(url) {
            @Override
            public HeaderAndBody put(String id, byte[] data) {
                try {
                    request.write(data);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
                return new HeaderAndBody(data, new HashMap<String, Object>());
            }

            @Override
            public HeaderAndBody post(byte[] data) {
                try {
                    request.write(data);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
                return new HeaderAndBody(data, new HashMap<String, Object>());
            }
        };

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.responseParser(new GsonResponseParser(builder.create()));

        Pipe<ListClassId> restPipe = new RestAdapter<ListClassId>(ListClassId.class, url, config);
        Object restRunner = UnitTestUtils.getPrivateField(restPipe, "restRunner");

        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                return provider;
            }
        });

        final CountDownLatch latch = new CountDownLatch(1);
        final ListClassId listClass = new ListClassId(true);
        final List<Point> returnedPoints = new ArrayList<Point>(10);

        restPipe.save(listClass, new Callback<ListClassId>() {
            @Override
            public void onSuccess(ListClassId data) {
                returnedPoints.addAll(data.points);
                latch.countDown();
            }

            @Override
            public void onFailure(Exception e) {
                throw new RuntimeException(e);
            }
        });

        latch.await(2, TimeUnit.SECONDS);

        int expectedId = new JSONObject(SERIALIZED_POINTS).getInt("id");
        JSONArray expectedPoints = new JSONObject(SERIALIZED_POINTS).getJSONArray("points");
        int requestId = new JSONObject(new String(request.toByteArray())).getInt("id");
        JSONArray requestPoints = new JSONObject(new String(request.toByteArray())).getJSONArray("points");
        assertEquals(expectedId, requestId);
        assertEquals(expectedPoints.toString(), requestPoints.toString());
        assertEquals(listClass.points, returnedPoints);
    }

    public void runReadWithFilterUsingUri() throws Exception {

        final CountDownLatch latch = new CountDownLatch(1);

        HttpProviderFactory factory = mock(HttpProviderFactory.class);
        when(factory.get(anyObject())).thenReturn(mock(HttpProvider.class));

        RestAdapter<Data> adapter = new RestAdapter<Data>(Data.class, url);
        Object restRunner = UnitTestUtils.getPrivateField(adapter, "restRunner");
        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", factory);

        ReadFilter filter = new ReadFilter();
        filter.setLinkUri(URI.create("?limit=10&where=%7B%22model%22:%22BMW%22%7D"));

        adapter.read(filter, new Callback<List<Data>>() {
            @Override
            public void onSuccess(List<Data> data) {
                latch.countDown();
            }

            @Override
            public void onFailure(Exception e) {
                latch.countDown();
            }
        });
        latch.await(500, TimeUnit.MILLISECONDS);

        verify(factory).get(eq(new URL(url.toString() + "?limit=10&where=%7B%22model%22:%22BMW%22%7D")));
    }

    public void runReadWithFilter() throws Exception {

        final CountDownLatch latch = new CountDownLatch(1);

        HttpProviderFactory factory = mock(HttpProviderFactory.class);
        when(factory.get(anyObject())).thenReturn(mock(HttpProvider.class));

        ModuleFields authFields = new ModuleFields();

        PipeModule urlModule = mock(PipeModule.class);

        when(urlModule.loadModule((URI) anyObject(), (String) anyObject(), (byte[]) anyObject()))
                .thenReturn(authFields);

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.module(urlModule);

        RestAdapter<Data> adapter = new RestAdapter<Data>(Data.class, url, config);
        Object restRunner = UnitTestUtils.getPrivateField(adapter, "restRunner");

        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", factory);

        ReadFilter filter = new ReadFilter();
        filter.setLimit(10);
        filter.setWhere(new JSONObject("{\"model\":\"BMW\"}"));

        adapter.read(filter, new Callback<List<Data>>() {
            @Override
            public void onSuccess(List<Data> data) {
                latch.countDown();
            }

            @Override
            public void onFailure(Exception e) {
                latch.countDown();
                Logger.getLogger(getClass().getSimpleName()).log(Level.SEVERE, TAG, e);
            }
        });
        latch.await(500, TimeUnit.MILLISECONDS);

        verify(factory).get(new URL(url.toString() + "?limit=10&model=BMW"));
    }

    /**
     * This test tests the default paging configuration.
     * 
     * @throws java.lang.NoSuchFieldException If this is thrown then the test is broken.
     * @throws java.lang.IllegalAccessException If this is thrown then the test is broken.
     * @throws java.lang.InterruptedException
     */
    public void testLinkPagingReturnsData()
            throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InterruptedException {

        final HttpStubProvider provider = new HttpStubProvider(url,
                new HeaderAndBody(SERIALIZED_POINTS.getBytes(), new HashMap<String, Object>()));

        PageConfig pageConfig = new PageConfig();
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());

        RestfulPipeConfiguration pipeConfig = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        pipeConfig.requestBuilder(new GsonRequestBuilder(builder.create()));
        pipeConfig.pageConfig(pageConfig);

        Pipe<ListClassId> dataPipe = pipeConfig.forClass(ListClassId.class);
        Object restRunner = UnitTestUtils.getPrivateField(dataPipe, "restRunner");

        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                return provider;
            }
        });

        ReadFilter onePageFilter = new ReadFilter();

        onePageFilter.setLimit(1);
        runRead(dataPipe, onePageFilter);
        List<ListClassId> result = runRead(dataPipe, onePageFilter);

        assertNotNull(result);
        assertFalse(result instanceof PagedList);

    }

    /**
     * This test tests the default paging configuration.
     * 
     */
    public void testDefaultPaging() throws InterruptedException, NoSuchFieldException, NoSuchFieldException,
            IllegalArgumentException, IllegalAccessException, URISyntaxException {

        PageConfig pageConfig = new PageConfig();
        GsonBuilder builder = new GsonBuilder().registerTypeAdapter(Point.class,
                new RestAdapterTest.PointTypeAdapter());

        RestfulPipeConfiguration pipeConfig = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        pipeConfig.requestBuilder(new GsonRequestBuilder(builder.create()));
        pipeConfig.pageConfig(pageConfig);

        Pipe<ListClassId> dataPipe = pipeConfig.forClass(ListClassId.class);
        Object restRunner = UnitTestUtils.getPrivateField(dataPipe, "restRunner");

        UnitTestUtils.setPrivateField(restRunner, "httpProviderFactory", new Provider<HttpProvider>() {
            @Override
            public HttpProvider get(Object... in) {
                HashMap<String, Object> headers = new HashMap<String, Object>(1);
                headers.put("Link",
                        "<http://example.com/TheBook/chapter2>; rel=\"previous\";title=\"previous chapter\",<http://example.com/TheBook/chapter3>; rel=\"next\";title=\"next chapter\"");
                HttpStubProvider provider = new HttpStubProvider(url,
                        new HeaderAndBody(SERIALIZED_POINTS.getBytes(), headers));

                return provider;
            }
        });

        ReadFilter onePageFilter = new ReadFilter();
        onePageFilter.setLimit(1);
        List<ListClassId> resultList = runRead(dataPipe, onePageFilter);
        assertTrue(resultList instanceof PagedList);
        WrappingPagedList<ListClassId> pagedList = (WrappingPagedList<ListClassId>) resultList;

        assertEquals(new URI("http://example.com/TheBook/chapter3"), pagedList.getNextFilter().getLinkUri());
        assertEquals(new URI("http://example.com/TheBook/chapter2"), pagedList.getPreviousFilter().getLinkUri());
    }

    public void testBuildPagedResultsFromHeaders() throws Exception {
        PageConfig pageConfig = new PageConfig();
        pageConfig.setMetadataLocation(PageConfig.MetadataLocations.HEADERS);

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.pageConfig(pageConfig);

        RestAdapter adapter = new RestAdapter(Data.class, url, config);
        List<Data> list = new ArrayList<Data>();
        HeaderAndBody response = new HeaderAndBody(new byte[] {}, new HashMap<String, Object>() {
            {
                put("next", "chapter3");
                put("previous", "chapter2");
            }
        });
        JSONObject where = new JSONObject();

        Method method = adapter.getClass().getDeclaredMethod("computePagedList", List.class, HeaderAndBody.class,
                JSONObject.class, Pipe.class);
        method.setAccessible(true);

        WrappingPagedList<Data> pagedList = (WrappingPagedList<Data>) method.invoke(adapter, list, response, where,
                adapter);
        assertEquals(new URI("http://server.com/context/chapter3"), pagedList.getNextFilter().getLinkUri());
        assertEquals(new URI("http://server.com/context/chapter2"), pagedList.getPreviousFilter().getLinkUri());

    }

    public void testBuildPagedResultsFromBody() throws Exception {
        PageConfig pageConfig = new PageConfig();
        pageConfig.setMetadataLocation(PageConfig.MetadataLocations.BODY);
        pageConfig.setNextIdentifier("pages.next");
        pageConfig.setPreviousIdentifier("pages.previous");

        RestfulPipeConfiguration config = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(url);
        config.pageConfig(pageConfig);

        RestAdapter adapter = (RestAdapter) config.forClass(Data.class);

        List<Data> list = new ArrayList<Data>();
        HeaderAndBody response = new HeaderAndBody(
                "{\"pages\":{\"next\":\"chapter3\",\"previous\":\"chapter2\"}}".getBytes(),
                new HashMap<String, Object>());
        JSONObject where = new JSONObject();
        Method method = adapter.getClass().getDeclaredMethod("computePagedList", List.class, HeaderAndBody.class,
                JSONObject.class, Pipe.class);
        method.setAccessible(true);

        WrappingPagedList<Data> pagedList = (WrappingPagedList<Data>) method.invoke(adapter, list, response, where,
                adapter);
        assertEquals(new URI("http://server.com/context/chapter3"), pagedList.getNextFilter().getLinkUri());
        assertEquals(new URI("http://server.com/context/chapter2"), pagedList.getPreviousFilter().getLinkUri());

    }

    public void testRunTimeout() throws Exception {

        final CountDownLatch latch = new CountDownLatch(1);
        URL aerogearserverUrl = new URL("https://controller-aerogear.rhcloud.com/aerogear-controller-demo/");
        RestfulPipeConfiguration pipeConfig = PipeManager.config("listClassId", RestfulPipeConfiguration.class)
                .withUrl(aerogearserverUrl);
        pipeConfig.timeout(1);
        RestAdapter<Data> adapter = (RestAdapter<Data>) pipeConfig.forClass(Data.class);
        final AtomicBoolean onFailCalled = new AtomicBoolean(false);
        final AtomicReference<Exception> exceptionReference = new AtomicReference<Exception>();
        adapter.read(new Callback<List<Data>>() {
            @Override
            public void onSuccess(List<Data> data) {
                latch.countDown();
            }

            @Override
            public void onFailure(Exception e) {
                onFailCalled.set(true);
                exceptionReference.set(e);
                latch.countDown();
            }
        });
        latch.await(50000, TimeUnit.MILLISECONDS);
        assertTrue(onFailCalled.get());
        assertEquals(SocketTimeoutException.class, exceptionReference.get().getCause().getClass());
    }

    private <T> List<T> runRead(Pipe<T> restPipe) throws InterruptedException {
        return runRead(restPipe, null);
    }

    /**
     * Runs a read method, returns the result of the call back and makes sure no
     * exceptions are thrown
     * 
     * @param restPipe
     */
    private <T> List<T> runRead(Pipe<T> restPipe, ReadFilter readFilter) throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicBoolean hasException = new AtomicBoolean(false);
        final AtomicReference<List<T>> resultRef = new AtomicReference<List<T>>();

        restPipe.read(readFilter, new Callback<List<T>>() {
            @Override
            public void onSuccess(List<T> data) {
                resultRef.set(data);
                latch.countDown();
            }

            @Override
            public void onFailure(Exception e) {
                hasException.set(true);
                Logger.getLogger(RestAdapterTest.class.getSimpleName()).log(Level.SEVERE, e.getMessage(), e);
                latch.countDown();
            }
        });

        latch.await(2, TimeUnit.SECONDS);
        Assert.assertFalse(hasException.get());

        return resultRef.get();
    }

    /**
     * Runs a read method, returns the result of the call back and rethrows the
     * underlying exception
     * 
     * @param restPipe
     */
    private <T> List<T> runReadForException(Pipe<T> restPipe, ReadFilter readFilter)
            throws InterruptedException, Exception {
        final CountDownLatch latch = new CountDownLatch(1);
        final AtomicBoolean hasException = new AtomicBoolean(false);
        final AtomicReference<Exception> exceptionref = new AtomicReference<Exception>();
        restPipe.read(readFilter, new Callback<List<T>>() {
            @Override
            public void onSuccess(List<T> data) {
                latch.countDown();
            }

            @Override
            public void onFailure(Exception e) {
                hasException.set(true);
                exceptionref.set(e);
                latch.countDown();
            }
        });

        latch.await(2, TimeUnit.SECONDS);
        Assert.assertTrue(hasException.get());

        throw exceptionref.get();
    }

    public final static class ListClassId {

        List<Point> points = new ArrayList<Point>(10);
        @RecordId
        String id = "1";

        public ListClassId(boolean build) {
            if (build) {
                for (int i = 0; i < 10; i++) {
                    points.add(new Point(i, i * 2));
                }
            }
        }

        public ListClassId() {
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        @Override
        public boolean equals(Object obj) {
            try {
                return points.equals(((ListClassId) obj).points);
            } catch (Throwable ignore) {
                return false;
            }
        }
    }

    private static class PointTypeAdapter implements InstanceCreator, JsonSerializer, JsonDeserializer {

        @Override
        public Object createInstance(Type type) {
            return new Point();
        }

        @Override
        public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
            JsonObject object = new JsonObject();
            object.addProperty("x", ((Point) src).x);
            object.addProperty("y", ((Point) src).y);
            return object;
        }

        @Override
        public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            return new Point(json.getAsJsonObject().getAsJsonPrimitive("x").getAsInt(),
                    json.getAsJsonObject().getAsJsonPrimitive("y").getAsInt());
        }
    }
}