org.openehealth.ipf.platform.camel.lbs.http.process.AbstractLbsHttpTest.java Source code

Java tutorial

Introduction

Here is the source code for org.openehealth.ipf.platform.camel.lbs.http.process.AbstractLbsHttpTest.java

Source

/*
 * Copyright 2008 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.openehealth.ipf.platform.camel.lbs.http.process;

import org.apache.camel.*;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.impl.DefaultExchange;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.ProtocolException;
import org.apache.commons.httpclient.methods.FileRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openehealth.ipf.commons.lbs.resource.ResourceCompatibleDataSource;
import org.openehealth.ipf.commons.lbs.resource.ResourceDataSource;
import org.openehealth.ipf.commons.lbs.resource.ResourceFactory;
import org.openehealth.ipf.commons.lbs.store.LargeBinaryStore;
import org.openehealth.ipf.commons.lbs.utils.CorruptedInputStream;
import org.openehealth.ipf.platform.camel.core.junit.DirtySpringContextJUnit4ClassRunner;
import org.springframework.beans.factory.annotation.Autowired;

import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;

/**
 * This test creates HTTP endpoints implicitly via the routes. These will be
 * not automatically be removed after the test execution. To do this it is
 * necessary to dirty the Spring application context. The best way to do this
 * is to run this test via {@link DirtySpringContextJUnit4ClassRunner}.
 *  
 * @author Jens Riemschneider
 */
public abstract class AbstractLbsHttpTest {

    public static final String ENDPOINT_NO_EXTRACT = "http://localhost:9452/lbstest_no_extract";

    public static final String ENDPOINT_EXTRACT = "http://localhost:9452/lbstest_extract";

    public static final String ENDPOINT_PING = "http://localhost:9452/lbstest_ping";

    public static final String ENDPOINT_EXTRACT_FACTORY_VIA_BEAN = "http://localhost:9452/lbstest_extract_factory_via_bean";

    public static final String ENDPOINT_EXTRACT_ROUTER = "http://localhost:9452/lbstest_extract_router";

    public static final String ENDPOINT_SEND_ONLY = "direct:lbstest_send_only";

    public static final String ENDPOINT_NON_HTTP = "direct:lbstest_non_http";

    @EndpointInject(uri = "mock:mock")
    protected MockEndpoint mock;

    @Autowired
    private CamelContext camelContext;

    @Autowired
    protected ProducerTemplate producerTemplate;

    @Autowired
    protected LargeBinaryStore store;

    @Resource(name = "resourceFactory")
    private ResourceFactory factory;

    protected File file;

    protected HttpClient httpClient;

    private TestOutputGenerator outputGenerator;

    @Before
    public void setUp() throws Exception {
        camelContext.getShutdownStrategy().setTimeout(5);
        file = File.createTempFile(getClass().getName(), "txt");
        FileWriter writer = new FileWriter(file);
        writer.write("blu bla");
        writer.close();
        httpClient = new HttpClient();
        outputGenerator = new TestOutputGenerator("testoutput");
    }

    @After
    public void tearDown() throws Exception {
        mock.reset();
        // MockEndpoint#reset does not set the default message processor back to
        // null (as of Camel 1.5). We have to do that manually.
        mock.whenAnyExchangeReceived(null);

        file.delete();
        //store.deleteAll();
    }

    @Test
    public void testTextWithoutResourceExtract() throws Exception {
        PostMethod method = new PostMethod(ENDPOINT_NO_EXTRACT);
        method.setRequestEntity(new StringRequestEntity("testtext", "text/plain", null));

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(new Processor() {
            @Override
            public void process(Exchange received) throws Exception {
                // Must be done in here because the connection is only valid during
                // processing
                String body = received.getIn().getBody(String.class);
                assertEquals("testtext", body);
            }
        });
        httpClient.executeMethod(method);
        method.getResponseBody();
        method.releaseConnection();
        mock.assertIsSatisfied();
    }

    @Test
    public void testFileWithoutResourceExtract() throws Exception {
        PostMethod method = new PostMethod(ENDPOINT_NO_EXTRACT);
        method.setRequestEntity(new FileRequestEntity(file, "unknown/unknown"));

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(new Processor() {
            @Override
            public void process(Exchange received) throws Exception {
                // Must be done in here because the connection is only valid during
                // processing
                HttpServletRequest request = received.getIn().getBody(HttpServletRequest.class);
                assertNotNull(request);
                assertEquals("unknown/unknown", request.getContentType());

                String body = received.getIn().getBody(String.class);
                assertEquals("blu bla", body);
            }
        });
        httpClient.executeMethod(method);
        method.releaseConnection();
        mock.assertIsSatisfied();
    }

    @Test
    public void testMultipartWithoutResourceExtract() throws Exception {
        PostMethod method = new PostMethod(ENDPOINT_NO_EXTRACT);
        Part[] parts = new Part[] { new FilePart("file1", file), new FilePart("file2", file),
                new StringPart("text", "testtext") };
        method.setRequestEntity(new MultipartRequestEntity(parts, method.getParams()));

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(new Processor() {
            @Override
            public void process(Exchange received) throws Exception {
                // Must be done in here because the connection is only valid during
                // processing
                HttpServletRequest request = received.getIn().getBody(HttpServletRequest.class);
                assertNotNull(request);
                assertTrue("did not receive a multipart message", ServletFileUpload.isMultipartContent(request));

                FileItemFactory factory = new DiskFileItemFactory();
                ServletFileUpload upload = new ServletFileUpload(factory);
                List files = upload.parseRequest(request);
                assertEquals(3, files.size());

                FileItem fileItem = (FileItem) files.get(0);
                assertEquals("file1", fileItem.getFieldName());
                assertEquals("blu bla", fileItem.getString());

                fileItem = (FileItem) files.get(1);
                assertEquals("file2", fileItem.getFieldName());
                assertEquals("blu bla", fileItem.getString());

                fileItem = (FileItem) files.get(2);
                assertEquals("text", fileItem.getFieldName());
                assertEquals("testtext", fileItem.getString());
            }
        });

        httpClient.executeMethod(method);
        method.releaseConnection();
        mock.assertIsSatisfied();
    }

    @Test
    public void testTextEndpointExtract() throws Exception {
        testText(ENDPOINT_EXTRACT);
    }

    @Test
    public void testTextEndpointFactoryViaBean() throws Exception {
        testText(ENDPOINT_EXTRACT_FACTORY_VIA_BEAN);
    }

    @Test
    public void testTextEndpointRouter() throws Exception {
        testText(ENDPOINT_EXTRACT_ROUTER);

        Exchange exchange = mock.getReceivedExchanges().get(0);
        assertEquals("I was here", exchange.getIn().getHeader("tag"));
    }

    private void testText(final String endpoint) throws Exception {
        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(outputGenerator);

        PostMethod method = new PostMethod(endpoint);
        method.setRequestEntity(new StringRequestEntity("testtext", "text/plain", null));
        httpClient.executeMethod(method);
        assertEquals("testoutput", method.getResponseBodyAsString());
        method.releaseConnection();

        mock.assertIsSatisfied();
        Map<String, String> receivedContent = outputGenerator.getReceivedContent();
        assertEquals(1, receivedContent.size());
        assertEquals("testtext", receivedContent.get("unnamed"));
    }

    @Test
    public void testFileEndpointExtract() throws Exception {
        testFile(ENDPOINT_EXTRACT);
    }

    @Test
    public void testFileEndpointFactoryViaBean() throws Exception {
        testFile(ENDPOINT_EXTRACT_FACTORY_VIA_BEAN);
    }

    @Test
    public void testFileEndpointRouter() throws Exception {
        testFile(ENDPOINT_EXTRACT_ROUTER);
    }

    protected void testFile(final String endpoint) throws Exception {
        PostMethod method = new PostMethod(endpoint);
        method.setRequestEntity(new FileRequestEntity(file, "unknown/unknown"));

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(outputGenerator);

        httpClient.executeMethod(method);
        assertEquals("testoutput", method.getResponseBodyAsString());
        method.releaseConnection();

        mock.assertIsSatisfied();
        String receivedBody = outputGenerator.getReceivedBody();
        assertEquals("blu bla", receivedBody);

        ResourceList receivedResources = outputGenerator.getReceivedResources();
        assertEquals(1, receivedResources.size());
        ResourceDataSource resource = receivedResources.get(0);

        assertFalse("clean up did not remove temporary resource", store.contains(resource.getResourceUri()));
    }

    @Test
    public void testPingFile() throws Exception {
        PostMethod method = new PostMethod(ENDPOINT_PING);
        method.setRequestEntity(new FileRequestEntity(file, "unknown/unknown"));

        final URI[] resourceUri = new URI[1];

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(new Processor() {
            @Override
            public void process(Exchange exchange) throws Exception {
                ResourceDataSource resource = exchange.getIn().getBody(ResourceDataSource.class);
                resourceUri[0] = resource.getResourceUri();
            }
        });

        httpClient.executeMethod(method);
        assertEquals("blu bla", method.getResponseBodyAsString());
        method.releaseConnection();

        // The resource is deleted immediately after the stream has been written.
        // However, the receiver will also continue right after the stream has
        // been written. There is a minimal chance that the resource has not
        // been removed yet. So we need to wait a little.
        int failCount = 0;
        while (store.contains(resourceUri[0]) && failCount < 5) {
            ++failCount;
            Thread.sleep(100);
        }
        assertFalse("resource not removed from the store", store.contains(resourceUri[0]));
    }

    @Test
    public void testMultipartEndpointExtract() throws Exception {
        testMultipart(ENDPOINT_EXTRACT);
    }

    @Test
    public void testMultipartEndpointFactoryViaBean() throws Exception {
        testMultipart(ENDPOINT_EXTRACT_FACTORY_VIA_BEAN);
    }

    @Test
    public void testMultipartEndpointRouter() throws Exception {
        testMultipart(ENDPOINT_EXTRACT_ROUTER);
    }

    private void testMultipart(String endpoint) throws Exception {
        PostMethod method = new PostMethod(endpoint);
        Part[] parts = new Part[] { new FilePart("file1", file), new FilePart("file2", file),
                new StringPart("text1", "testtext1"), new StringPart("text2", "testtext2") };
        method.setRequestEntity(new MultipartRequestEntity(parts, method.getParams()));

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(outputGenerator);

        httpClient.executeMethod(method);
        assertEquals("testoutput", method.getResponseBodyAsString());
        method.releaseConnection();

        mock.assertIsSatisfied();
        Map<String, String> receivedContent = outputGenerator.getReceivedContent();
        assertEquals(4, receivedContent.size());
        assertEquals("blu bla", receivedContent.get("file1"));
        assertEquals("blu bla", receivedContent.get("file2"));
        assertEquals("testtext1", receivedContent.get("text1"));
        assertEquals("testtext2", receivedContent.get("text2"));

        assertEquals("blu bla", outputGenerator.getReceivedBody());
    }

    @Test
    public void testMultipartSendOnly() throws Exception {
        Exchange sendExchange = new DefaultExchange(camelContext);

        FileDataSource dataSource1 = new FileDataSource(file);
        InputStream inputStream = dataSource1.getInputStream();
        ResourceDataSource resource1 = factory.createResource("test", "text/plain", "text", "first", inputStream);
        inputStream.close();

        ByteArrayDataSource dataSource2 = new ByteArrayDataSource("testdata".getBytes(), "text/plain");
        inputStream = dataSource2.getInputStream();
        ResourceDataSource resource2 = factory.createResource("test", "text/plain", "text", "second",
                dataSource2.getInputStream());
        inputStream.close();

        ResourceList resources = new ResourceList();
        resources.add(resource1);
        resources.add(resource2);
        sendExchange.getIn().setBody(resources);
        sendExchange.setPattern(ExchangePattern.InOut);

        mock.expectedMessageCount(1);
        mock.whenAnyExchangeReceived(outputGenerator);

        Exchange output = producerTemplate.send(ENDPOINT_SEND_ONLY, sendExchange);

        mock.assertIsSatisfied();

        assertEquals("testoutput", output.getOut().getBody(String.class));

        Map<String, String> receivedContent = outputGenerator.getReceivedContent();
        assertEquals(2, receivedContent.size());
        assertEquals("blu bla", receivedContent.get("first"));
        assertEquals("testdata", receivedContent.get("second"));
    }

    @Test
    public void testStoreDoesNotHandleNonHttpMessage() throws Exception {
        Exchange sendExchange = new DefaultExchange(camelContext);
        sendExchange.getIn().setBody("nix http");

        mock.expectedMessageCount(1);
        mock.expectedBodiesReceived("nix http");

        producerTemplate.send(ENDPOINT_NON_HTTP, sendExchange);

        mock.assertIsSatisfied();
    }

    /**
     * Regression test of a problem I saw when debugging the HTTP stuff. Closing 
     * the input stream wasn't handled as expected. This test ensures that an 
     * exception during the send of the HTTP message closes the input stream 
     */
    @Test
    public void testFetchHandlesCorruptStreamMultipart() throws Exception {
        Exchange sendExchange = new DefaultExchange(camelContext);
        CorruptedInputStream inputStream = new CorruptedInputStream();
        ResourceCompatibleDataSource dataSource1 = createMock(ResourceCompatibleDataSource.class);
        expect(dataSource1.getInputStream()).andReturn(inputStream).anyTimes();
        expect(dataSource1.getContentType()).andReturn("text/plain").anyTimes();
        expect(dataSource1.getName()).andReturn("test").anyTimes();
        expect(dataSource1.getContentLength()).andReturn(245L).anyTimes();
        replay(dataSource1);

        ByteArrayDataSource dataSource2 = new ByteArrayDataSource("testdata".getBytes(), "text/plain");

        ResourceDataSource resource1 = new ResourceDataSource("first", dataSource1);
        ResourceDataSource resource2 = new ResourceDataSource("second", dataSource2);

        ResourceList resources = new ResourceList();
        resources.add(resource1);
        resources.add(resource2);
        sendExchange.getIn().setBody(resources);

        mock.expectedMessageCount(0);

        Exchange output = producerTemplate.send(ENDPOINT_SEND_ONLY, sendExchange);

        mock.assertIsSatisfied();

        assertTrue("Stream was not closed", inputStream.isClosed());

        assertEquals(IOException.class, output.getException().getClass());
    }

    /**
     * Regression test of a problem I saw when debugging the HTTP stuff. Closing 
     * the input stream wasn't handled as expected. This test ensures that an 
     * exception during the send of the HTTP message closes the input stream 
     */
    @Test
    public void testFetchHandlesCorruptStreamSingle() throws Exception {
        Exchange sendExchange = new DefaultExchange(camelContext);
        CorruptedInputStream inputStream = new CorruptedInputStream();
        ResourceCompatibleDataSource dataSource = createMock(ResourceCompatibleDataSource.class);
        expect(dataSource.getInputStream()).andReturn(inputStream).anyTimes();
        expect(dataSource.getContentType()).andReturn("text/plain").anyTimes();
        expect(dataSource.getName()).andReturn("test").anyTimes();
        expect(dataSource.getContentLength()).andReturn(245L).anyTimes();
        replay(dataSource);

        ResourceDataSource resource = new ResourceDataSource("content", dataSource);

        ResourceList resources = new ResourceList();
        resources.add(resource);
        sendExchange.getIn().setBody(resources);

        mock.expectedMessageCount(0);

        Exchange output = producerTemplate.send(ENDPOINT_SEND_ONLY, sendExchange);

        mock.assertIsSatisfied();

        assertTrue("Stream was not closed", inputStream.isClosed());

        assertEquals(ProtocolException.class, output.getException().getClass());
    }

    protected final class TestOutputGenerator implements Processor {
        private final String output;
        private final Map<String, String> receivedContent = new HashMap<String, String>();
        private String receivedBody;
        private ResourceList receivedResources;

        private TestOutputGenerator(String output) {
            this.output = output;
        }

        public String getReceivedBody() {
            return receivedBody;
        }

        @Override
        public void process(Exchange exchange) throws Exception {
            ResourceList resources = exchange.getIn().getBody(ResourceList.class);
            receivedResources = resources;
            for (ResourceDataSource resource : resources) {
                InputStream inputStream = resource.getInputStream();
                try {
                    receivedContent.put(resource.getId(), IOUtils.toString(inputStream));
                } finally {
                    inputStream.close();
                }
            }

            receivedBody = exchange.getIn().getBody(String.class);

            exchange.getOut().setBody(new ByteArrayInputStream(output.getBytes()));
        }

        public Map<String, String> getReceivedContent() {
            return receivedContent;
        }

        public ResourceList getReceivedResources() {
            return receivedResources;
        }
    }

    private static class ByteArrayDataSource implements DataSource {
        private byte[] data;
        private String contentType;

        public ByteArrayDataSource(byte[] data, String contentType) {
            this.data = data;
            this.contentType = contentType;
        }

        public InputStream getInputStream() throws IOException {
            return new ByteArrayInputStream(data);
        }

        public OutputStream getOutputStream() throws IOException {
            throw new IOException("read-only data source");
        }

        public String getContentType() {
            return contentType;
        }

        public String getName() {
            return "test";
        }
    }
}