org.exist.xquery.RestBinariesTest.java Source code

Java tutorial

Introduction

Here is the source code for org.exist.xquery.RestBinariesTest.java

Source

/*
 * eXist Open Source Native XML Database
 * Copyright (C) 2001-2017 The eXist Project
 * http://exist-db.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.exist.xquery;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.exist.http.jaxb.Query;
import org.exist.http.jaxb.Result;
import org.exist.test.ExistWebServer;
import org.exist.util.io.FastByteArrayOutputStream;
import org.exist.xmldb.XmldbURI;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.apache.http.HttpStatus.SC_CREATED;
import static org.apache.http.HttpStatus.SC_OK;
import static org.exist.TestUtils.ADMIN_DB_PWD;
import static org.exist.TestUtils.ADMIN_DB_USER;
import static org.junit.Assert.assertArrayEquals;

/**
 * @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
 */
public class RestBinariesTest extends AbstractBinariesTest<Result, Result.Value, Exception> {

    @ClassRule
    public static final ExistWebServer existWebServer = new ExistWebServer(true, false, true);

    private static Executor executor = null;

    @BeforeClass
    public static void setupExecutor() {
        executor = Executor.newInstance()
                .auth(new HttpHost("localhost", existWebServer.getPort()), ADMIN_DB_USER, ADMIN_DB_PWD)
                .authPreemptive(new HttpHost("localhost", existWebServer.getPort()));
    }

    /**
     * {@see https://github.com/eXist-db/exist/issues/790#error-case-2}
     *
     * response:stream is used to return Base64 encoded binary.
     */
    @Test
    public void streamBinarySax() throws JAXBException, IOException {
        final String query = "import module namespace util = \"http://exist-db.org/xquery/util\";\n"
                + "import module namespace response = \"http://exist-db.org/xquery/response\";\n"
                + "let $bin := util:binary-doc('" + TEST_COLLECTION.append(BIN1_FILENAME).toString() + "')\n"
                + "return response:stream($bin, 'media-type=application/octet-stream')";

        final HttpResponse response = postXquery(query);

        final HttpEntity entity = response.getEntity();
        try (final FastByteArrayOutputStream baos = new FastByteArrayOutputStream()) {
            entity.writeTo(baos);

            assertArrayEquals(BIN1_CONTENT, Base64.decodeBase64(baos.toByteArray()));
        }
    }

    /**
     * {@see https://github.com/eXist-db/exist/issues/790#error-case-2}
     *
     * response:stream-binary is used to return raw binary.
     */
    @Test
    public void streamBinaryRaw() throws JAXBException, IOException {
        final String query = "import module namespace util = \"http://exist-db.org/xquery/util\";\n"
                + "import module namespace response = \"http://exist-db.org/xquery/response\";\n"
                + "let $bin := util:binary-doc('" + TEST_COLLECTION.append(BIN1_FILENAME).toString() + "')\n"
                + "return response:stream-binary($bin, 'media-type=application/octet-stream', ())";

        final HttpResponse response = postXquery(query);

        final HttpEntity entity = response.getEntity();
        try (final FastByteArrayOutputStream baos = new FastByteArrayOutputStream()) {
            entity.writeTo(baos);

            assertArrayEquals(BIN1_CONTENT, baos.toByteArray());
        }
    }

    /**
     * {@see https://github.com/eXist-db/exist/issues/790#error-case-5}
     *
     * response:stream is used to return Base64 encoded binary.
     */
    @Test
    public void readAndStreamBinarySax() throws IOException, JAXBException {
        final byte[] data = randomData(1024 * 1024); // 1MB
        final Path tmpInFile = createTemporaryFile(data);

        final String query = "import module namespace file = \"http://exist-db.org/xquery/file\";\n"
                + "import module namespace response = \"http://exist-db.org/xquery/response\";\n"
                + "let $bin := file:read-binary('" + tmpInFile.toAbsolutePath().toString() + "')\n"
                + "return response:stream($bin, 'media-type=application/octet-stream')";

        final HttpResponse response = postXquery(query);

        final HttpEntity entity = response.getEntity();
        try (final FastByteArrayOutputStream baos = new FastByteArrayOutputStream()) {
            entity.writeTo(baos);

            assertArrayEquals(Files.readAllBytes(tmpInFile), Base64.decodeBase64(baos.toByteArray()));
        }
    }

    /**
     * {@see https://github.com/eXist-db/exist/issues/790#error-case-5}
     *
     * response:stream-binary is used to return raw binary.
     */
    @Test
    public void readAndStreamBinaryRaw() throws IOException, JAXBException {
        final byte[] data = randomData(1024 * 1024); // 1MB
        final Path tmpInFile = createTemporaryFile(data);

        final String query = "import module namespace file = \"http://exist-db.org/xquery/file\";\n"
                + "import module namespace response = \"http://exist-db.org/xquery/response\";\n"
                + "let $bin := file:read-binary('" + tmpInFile.toAbsolutePath().toString() + "')\n"
                + "return response:stream-binary($bin, 'media-type=application/octet-stream', ())";

        final HttpResponse response = postXquery(query);

        final HttpEntity entity = response.getEntity();
        try (final FastByteArrayOutputStream baos = new FastByteArrayOutputStream()) {
            entity.writeTo(baos);

            assertArrayEquals(Files.readAllBytes(tmpInFile), baos.toByteArray());
        }
    }

    @Override
    protected void storeBinaryFile(final XmldbURI filePath, final byte[] content) throws Exception {
        final HttpResponse response = executor
                .execute(Request.Put(getRestUrl() + filePath.toString())
                        .setHeader("Content-Type", "application/octet-stream").bodyByteArray(content))
                .returnResponse();

        if (response.getStatusLine().getStatusCode() != SC_CREATED) {
            throw new Exception("Unable to store binary file: " + filePath);
        }
    }

    private String getRestUrl() {
        return "http://localhost:" + existWebServer.getPort() + "/rest";
    }

    @Override
    protected void removeCollection(final XmldbURI collectionUri) throws Exception {
        final HttpResponse response = executor.execute(Request.Delete(getRestUrl() + collectionUri.toString()))
                .returnResponse();

        if (response.getStatusLine().getStatusCode() != SC_OK) {
            throw new Exception("Unable to delete collection: " + collectionUri);
        }
    }

    @Override
    protected QueryResultAccessor<Result, Exception> executeXQuery(final String xquery) throws Exception {
        final HttpResponse response = postXquery(xquery);
        final HttpEntity entity = response.getEntity();
        try (final InputStream is = entity.getContent()) {
            final JAXBContext jaxbContext = JAXBContext.newInstance("org.exist.http.jaxb");
            final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            final Result result = (Result) unmarshaller.unmarshal(is);

            return consumer -> consumer.accept(result);
        }
    }

    private HttpResponse postXquery(final String xquery) throws JAXBException, IOException {
        final Query query = new Query();
        query.setText(xquery);

        final JAXBContext jaxbContext = JAXBContext.newInstance("org.exist.http.jaxb");
        final Marshaller marshaller = jaxbContext.createMarshaller();

        final HttpResponse response;
        try (final FastByteArrayOutputStream baos = new FastByteArrayOutputStream()) {
            marshaller.marshal(query, baos);
            response = executor.execute(Request.Post(getRestUrl() + "/db/").bodyByteArray(baos.toByteArray(),
                    ContentType.APPLICATION_XML)).returnResponse();
        }

        if (response.getStatusLine().getStatusCode() != SC_OK) {
            throw new IOException(
                    "Unable to query, HTTP response code: " + response.getStatusLine().getStatusCode());
        }

        return response;
    }

    @Override
    protected long size(final Result result) throws Exception {
        return result.getCount();
    }

    @Override
    protected Result.Value item(final Result results, final int index) throws Exception {
        return results.getValue().get(index);
    }

    @Override
    protected boolean isBinaryType(final Result.Value item) throws Exception {
        final String type = item.getType();
        return "xs:base64Binary".equals(type) || "xs:hexBinary".equals(type);
    }

    @Override
    protected boolean isBooleanType(Result.Value item) throws Exception {
        return "xs:boolean".equals(item.getType());
    }

    @Override
    protected byte[] getBytes(final Result.Value item) throws Exception {
        switch (item.getType()) {
        case "xs:base64Binary":
            return Base64.decodeBase64(item.getContent().get(0).toString());

        case "xs:hexBinary":
            return Hex.decodeHex(item.getContent().get(0).toString());

        default:
            throw new UnsupportedOperationException();
        }
    }

    @Override
    protected boolean getBoolean(final Result.Value item) throws Exception {
        return Boolean.parseBoolean(item.getContent().get(0).toString());
    }
}