Package org.fcrepo.test.api

Source Code of org.fcrepo.test.api.TestAdminAPI$HttpResponseInfo

/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.test.api;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
import static org.apache.http.HttpStatus.SC_OK;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Set;

import junit.framework.JUnit4TestAdapter;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.fcrepo.client.FedoraClient;
import org.fcrepo.common.http.PreemptiveAuth;
import org.fcrepo.server.access.FedoraAPIAMTOM;
import org.fcrepo.server.management.FedoraAPIMMTOM;
import org.fcrepo.server.types.gen.Datastream;
import org.fcrepo.server.utilities.TypeUtility;
import org.fcrepo.test.FedoraServerTestCase;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.JUnitCore;


/**
* Tests of the REST API. Tests assume a running instance of Fedora with the
* REST API enabled. //TODO: actually validate the ResponseBody instead of just
* HTTP status codes
*
* @author Edwin Shin
* @author Bill Branan
* @version $Id$
* @since 3.0
*/
public class TestAdminAPI
        extends FedoraServerTestCase {

    private static FedoraClient s_client;
   
    private FedoraAPIAMTOM apia;

    private FedoraAPIMMTOM apim;

    // used for determining test configuration
    private static String authAccessProperty = "fedora.authorize.access";

    protected Boolean authorizeAccess = null;
    protected String url;

    private final boolean chunked = false;
   
    @BeforeClass
    public static void bootstrap() throws Exception {
        s_client = getFedoraClient();
        ingestSimpleImageDemoObjects(s_client);
        ingestSimpleDocumentDemoObjects(s_client);
    }
   
    @AfterClass
    public static void cleanUp() throws Exception {
        purgeDemoObjects(s_client);
        s_client.shutdown();
    }

    @Before
    public void setUp() throws Exception {
        apia = s_client.getAPIAMTOM();
        apim = s_client.getAPIMMTOM();
    }

    @After
    public void tearDown() throws Exception {
    }

    // determine if test config specifies that Access requests should be authorized

    private boolean getAuthAccess() {
        if (authorizeAccess == null) {
            String property = System.getProperty(authAccessProperty);
            if (property.equals("true")) {
                authorizeAccess = true;
            } else if (property.equals("false")) {
                authorizeAccess = false;
            } else {
                assertTrue("Failed to determine whether to perform authorization on Access requests from: " +
                           authAccessProperty, false);
                throw new RuntimeException(
                        "Failed to determine whether to perform authorization on Access requests from: " +
                        authAccessProperty);
            }

        }
        return authorizeAccess;

    }

    @Test
    public void testModifyControlGroup() throws Exception {

        //////////////////////////////////////////////////
        // tests on file of PIDs - XML
        //////////////////////////////////////////////////


        // get objects to modify from a basic search query, to provide xml input
        url = "/objects?pid=true&title=false&query=pid~demo%3A*&maxResults=1000&resultFormat=xml";

        HttpResponse res = get(getAuthAccess());
        assertEquals(SC_OK, res.getStatusLine().getStatusCode());

        File objectsListFile = null;

        try {
            // output to a file
            objectsListFile = File.createTempFile("TestAdminAPI", null);
            FileWriter fw = new FileWriter(objectsListFile);
            String queryResults = EntityUtils.toString(res.getEntity());
            fw.write(queryResults);
            fw.close();

            // run the tests
            testFileInput(objectsListFile);

        } finally {
            // clean up
            if (objectsListFile != null)
                if (!objectsListFile.delete())
                    objectsListFile.deleteOnExit();
        }

        purgeDemoObjects(s_client);
        ingestSimpleImageDemoObjects(s_client);
        ingestSimpleDocumentDemoObjects(s_client);

        //////////////////////////////////////////////////
        // tests on file of PIDs - flat file
        //////////////////////////////////////////////////

        objectsListFile = null;

        try {
            // generate flat file list of demo objects
            objectsListFile = File.createTempFile("TestAdminAPI", null);
            FileWriter fw = new FileWriter(objectsListFile);

            Set<String> objects = getDemoObjects(s_client);
            for (String pid : objects) {
                fw.write(pid + "\n");
            }
            fw.close();

            // run the tests
            testFileInput(objectsListFile);

        } finally {
            // clean up
            if (objectsListFile != null)
                if (!objectsListFile.delete())
                    objectsListFile.deleteOnExit();
        }

        purgeDemoObjects(s_client);
        ingestSimpleImageDemoObjects(s_client);
        ingestSimpleDocumentDemoObjects(s_client);

        //////////////////////////////////////////////////
        // tests on single object
        //////////////////////////////////////////////////

        Set<String> objects = getDemoObjects(s_client);

        // test on the first object we find
        String pid = objects.toArray(new String[0])[0];
        // test 404 on object not found
        url = this.modifyDatastreamControlGroupUrl(pid + "doesnotexist", "DC", "M", false, false, false);
        res = get(true);
        assertEquals(SC_NOT_FOUND, res.getStatusLine().getStatusCode());

        // test 404 on datastream not found
        url = this.modifyDatastreamControlGroupUrl(pid, "doesnotexist", "M", false, false, false);
        res = get(true);
        assertEquals(SC_NOT_FOUND, res.getStatusLine().getStatusCode());

        // TODO: getting stream contents?  (could use REST call instead)


        // datastream contents before modification
        byte[] before = TypeUtility.convertDataHandlerToBytes(apia.getDatastreamDissemination(pid, "DC", null).getStream());

        url = this.modifyDatastreamControlGroupUrl(pid, "DC", "M", false, false, false);
        res = get(true);
        String contents = EntityUtils.toString(res.getEntity());
        assertEquals(SC_OK, res.getStatusLine().getStatusCode());

        // check control group modified
        Datastream ds = apim.getDatastream(pid, "DC", null);
        assertEquals("ControlGroup", "M", ds.getControlGroup().value());


        // datastream contents after modification
        byte[] after = TypeUtility.convertDataHandlerToBytes(apia.getDatastreamDissemination(pid, "DC", null).getStream());

        // check they are the same
        // (comparing as strings as the assertEquals is a lot easier to read...)
        String beforeString = new String(before, "UTF-8");
        String afterString = new String(after, "UTF-8");
        assertEquals("Datastream contents ", beforeString, afterString);

        //////////////////////////////////////////////////
        // tests on list of pids
        //////////////////////////////////////////////////

        // test on the second and third objects we found
        String pid1 = objects.toArray(new String[0])[1];
        String pid2 = objects.toArray(new String[0])[2];


        // modify both
        url = this.modifyDatastreamControlGroupUrl(pid1 + "," + pid2, "DC", "M", false, false, false);
        res = get(true);
        assertEquals(SC_OK, res.getStatusLine().getStatusCode());

        contents = EntityUtils.toString(res.getEntity());
        int[] counts = getCounts(contents);

        // check for modification in result stream
        assertEquals("Object count", 2, counts[0]);
        assertEquals("Datastream count", 2, counts[1]);

        // check control groups actually modified
        ds = apim.getDatastream(pid1, "DC", null);
        assertEquals("ControlGroup", "M", ds.getControlGroup().value());

        ds = apim.getDatastream(pid2, "DC", null);
        assertEquals("ControlGroup", "M", ds.getControlGroup().value());
    }


    private void testFileInput(File objectsListFile) throws Exception {

        // count objects we know already have Managed content DC
        // (ingest creates some of these, ending in _M)
        // used later in checking results
        Set<String> objects = getDemoObjects(s_client);
        int managedObjects = 0;
        for (String pid : objects) {
            if (pid.endsWith("_M"))
                managedObjects++;
        }

        // modify datastreams, based on file input - DC
        url = this.modifyDatastreamControlGroupUrl("file:///" + objectsListFile.getAbsolutePath(), "DC", "M", false, false, false);
        HttpResponse res = get(true);
        int status = res.getStatusLine().getStatusCode();
        String modified = EntityUtils.toString(res.getEntity());
        assertEquals(SC_OK, status);

        // object and datastream count message expected
        String logExpected = "Updated " + (objects.size() - managedObjects) + " objects and " + (objects.size() - managedObjects) + " datastreams";
        assertTrue("Wrong number of objects/datastreams updated.  Expected " + logExpected + "\n" + "Log file shows:" + "\n" + modified, modified.contains(logExpected));

        // do again, this time we expect no modifications (already modified, so should be ignored)
        res = get(true);

        modified = EntityUtils.toString(res.getEntity());
        assertEquals(SC_OK, res.getStatusLine().getStatusCode());

        // object and datastream count message expected
        logExpected = "Updated " + 0 + " objects and " + 0 + " datastreams";
        assertTrue("Wrong number of objects/datastreams updated", modified.contains(logExpected));

        // do again modifying DC and RELS-EXT
        // DC is already M, so won't result in modifications
        // not all objects have RELS-EXT- the simple document demo objects don't
        // so we check that object and datastream count is greater than zero but 2 less than number of objects
        // FIXME: could iterate all objects before/after and do more exact tests
        url = this.modifyDatastreamControlGroupUrl("file:///" + objectsListFile.getAbsolutePath(), "DC,RELS-EXT", "M", false, false, false);
        res = get(true);
        modified = EntityUtils.toString(res.getEntity());
        assertEquals(SC_OK, res.getStatusLine().getStatusCode());

        int[] counts = getCounts(modified);
        int objectCount = counts[0];
        int datastreamCount = counts[1];

        if (objectCount <= 0 || objectCount > (objects.size() - managedObjects - 2) || datastreamCount <= 0 || datastreamCount > (objects.size() - managedObjects - 2))
            fail("Incorrect number of objects and datastreams modified: objects " + objectCount + " datastreams " + datastreamCount);

    }


    /**
     * Parses results, returns object count, datastream count
     * @param response
     * @return
     */
    private int[] getCounts(String response) {
        int objectCountPos = response.lastIndexOf("Updated ");
        int datastreamCountPos = response.lastIndexOf(" objects and ");
        int datastreamEndCountPos = response.lastIndexOf(" datastreams");

        int objectCount = Integer.parseInt(response.substring(objectCountPos + "Updated ".length(), datastreamCountPos));
        int datastreamCount = Integer.parseInt(response.substring(datastreamCountPos + " objects and ".length(), datastreamEndCountPos));

        int[] res = {objectCount, datastreamCount};

        return res;
    }

    private String modifyDatastreamControlGroupUrl(String pid, String dsID, String controlGroup, boolean addXMLHeader, boolean reformat, boolean setMIMETypeCharset) {

        String ret;
        try {
            ret = "/management/control" +
                "?action=modifyDatastreamControlGroup" +
                "&pid=" + URLEncoder.encode(pid, "UTF-8") +
                "&dsID=" + URLEncoder.encode(dsID, "UTF-8") +
                "&controlGroup=" + controlGroup +
                "&addXMLHeader=" + addXMLHeader +
                "&reformat=" + reformat +
                "&setMIMETypeCharset=" + setMIMETypeCharset;
        } catch (UnsupportedEncodingException e) {
            // should never happen
            throw new RuntimeException(e);
        }

        return ret;
    }


// helper methods

    private HttpClient getClient(boolean auth) {
        DefaultHttpClient client = new PreemptiveAuth();
        if (auth) {
            client
                    .getCredentialsProvider()
                    .setCredentials(new AuthScope(getHost(), Integer
                            .valueOf(getPort())),
                                    new UsernamePasswordCredentials(getUsername(),
                                                                    getPassword()));
        }
        return client;
    }

    /**
     * Issues an HTTP GET for the specified URL.
     *
     * @param authenticate
     * @return HttpResponse
     * @throws Exception
     */
    protected HttpResponse get(boolean authenticate) throws Exception {
        return getOrDelete("GET", authenticate);
    }

    protected HttpResponse delete(boolean authenticate) throws Exception {
        return getOrDelete("DELETE", authenticate);
    }

    /**
     * Issues an HTTP PUT to <code>url</code>. Callers are responsible for
     * calling releaseConnection() on the returned <code>HttpMethod</code>.
     *
     * @param authenticate
     * @return
     * @throws Exception
     */
    protected HttpResponse put(boolean authenticate) throws Exception {
        return putOrPost("PUT", null, authenticate);
    }

    protected HttpResponse put(String requestContent, boolean authenticate)
            throws Exception {
        return putOrPost("PUT", requestContent, authenticate);
    }

    protected HttpResponse post(String requestContent, boolean authenticate)
            throws Exception {
        return putOrPost("POST", requestContent, authenticate);
    }

    protected HttpResponse put(File requestContent, boolean authenticate)
            throws Exception {
        return putOrPost("PUT", requestContent, authenticate);
    }

    protected HttpResponse post(File requestContent, boolean authenticate)
            throws Exception {
        return putOrPost("POST", requestContent, authenticate);
    }


    private HttpResponse getOrDelete(String method, boolean authenticate)
            throws Exception {
        if (url == null || url.length() == 0) {
            throw new IllegalArgumentException("url must be a non-empty value");
        } else if (!(url.startsWith("http://") || url.startsWith("https://"))) {
            url = getBaseURL() + url;
        }
        HttpUriRequest httpMethod = null;
        if (method.equals("GET")) {
            httpMethod = new HttpGet(url);
        } else if (method.equals("DELETE")) {
            httpMethod = new HttpDelete(url);
        } else {
            throw new IllegalArgumentException("method must be one of GET or DELETE.");
        }
        httpMethod.setHeader(HttpHeaders.CONNECTION, "Keep-Alive");

        return getClient(authenticate).execute(httpMethod);
    }

    private HttpResponse putOrPost(String method,
                                   Object requestContent,
                                   boolean authenticate) throws Exception {


        if (url == null || url.length() == 0) {
            throw new IllegalArgumentException("url must be a non-empty value");
        } else if (!(url.startsWith("http://") || url.startsWith("https://"))) {
            url = getBaseURL() + url;
        }


        HttpEntityEnclosingRequestBase httpMethod = null;
        try {
            if (method.equals("PUT")) {
                httpMethod = new HttpPut(url);
            } else if (method.equals("POST")) {
                httpMethod = new HttpPost(url);
            } else {
                throw new IllegalArgumentException("method must be one of PUT or POST.");
            }
            httpMethod.setHeader(HttpHeaders.CONNECTION, "Keep-Alive");
            if (requestContent != null) {
                if (requestContent instanceof String) {
                    StringEntity entity = new StringEntity((String) requestContent,
                            Charset.forName("UTF-8"));
                    entity.setChunked(chunked);
                    httpMethod.setEntity(entity);
                } else if (requestContent instanceof File) {
                    MultipartEntity entity =
                            new MultipartEntity();
                    entity.addPart(((File) requestContent)
                            .getName(), new FileBody((File) requestContent));
                    entity.addPart("param_name", new StringBody("value"));
                    httpMethod.setEntity(entity);
                } else {
                    throw new IllegalArgumentException("requestContent must be a String or File");
                }
            }
            return getClient(authenticate).execute(httpMethod);
        } finally {
            if (httpMethod != null) {
                httpMethod.releaseConnection();
            }
        }
    }


    class HttpResponseInfo {

        private final int statusCode;

        private final byte[] responseBody;

        private final Header[] responseHeaders;

        HttpResponseInfo(int status, byte[] body, Header[] headers) {
            statusCode = status;
            responseBody = body;
            responseHeaders = headers;
        }

        HttpResponseInfo(HttpResponse response)
                throws IOException {
            statusCode = response.getStatusLine().getStatusCode();
            //responseBody = method.getResponseBody();
            InputStream is = response.getEntity().getContent();
            responseBody = IOUtils.toByteArray(is);
            IOUtils.closeQuietly(is);
            responseHeaders = response.getAllHeaders();
        }

        public int getStatusCode() {
            return statusCode;
        }

        public byte[] getResponseBody() {
            return responseBody;
        }

        public String getResponseBodyString() {
            try {
                return new String(responseBody, "UTF-8");
            } catch (UnsupportedEncodingException wontHappen) {
                throw new Error(wontHappen);
            }
        }

        public Header[] getResponseHeaders() {
            return responseHeaders;
        }

        public Header getResponseHeader(String headerName) {
            for (Header header : responseHeaders) {
                if (header.getName().equalsIgnoreCase(headerName)) {
                    return header;
                }
            }
            return null;
        }
    }
   
    public static junit.framework.Test suite() {
        return new JUnit4TestAdapter(TestAdminAPI.class);
    }

    public static void main(String[] args) {
        JUnitCore.runClasses(TestAdminAPI.class);
    }

}
TOP

Related Classes of org.fcrepo.test.api.TestAdminAPI$HttpResponseInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.