cdr.forms.SwordDepositHandler.java Source code

Java tutorial

Introduction

Here is the source code for cdr.forms.SwordDepositHandler.java

Source

/**
 * Copyright 2010 The University of North Carolina at Chapel Hill
 *
 * 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 cdr.forms;

import edu.unc.lib.schemas.acl.AccessControlType;
import edu.unc.lib.schemas.acl.AclFactory;
import edu.unc.lib.schemas.acl.AclPackage;
import edu.unc.lib.schemas.acl.GrantType;
import gov.loc.mets.AgentType;
import gov.loc.mets.AmdSecType;
import gov.loc.mets.DivType;
import gov.loc.mets.FLocatType;
import gov.loc.mets.FileGrpType1;
import gov.loc.mets.FileSecType;
import gov.loc.mets.FileType;
import gov.loc.mets.FptrType;
import gov.loc.mets.LOCTYPEType;
import gov.loc.mets.MDTYPEType;
import gov.loc.mets.MdSecType;
import gov.loc.mets.MdWrapType;
import gov.loc.mets.MetsFactory;
import gov.loc.mets.MetsHdrType;
import gov.loc.mets.MetsPackage;
import gov.loc.mets.MetsType;
import gov.loc.mets.ROLEType;
import gov.loc.mets.SmLinkType;
import gov.loc.mets.StructLinkType1;
import gov.loc.mets.StructMapType;
import gov.loc.mets.TYPEType;
import gov.loc.mets.XmlDataType1;
import gov.loc.mets.util.Link;
import gov.loc.mets.util.METSConstants;
import gov.loc.mets.util.MetsResourceFactoryImpl;
import gov.loc.mods.mods.MODSFactory;
import gov.loc.mods.mods.MODSPackage;
import gov.loc.mods.mods.ModsDefinition;
import gov.loc.mods.mods.NameDefinition;
import gov.loc.mods.mods.XsString;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.FileRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xml.type.internal.XMLCalendar;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3._1999.xlink.XlinkPackage;

import crosswalk.DateInputField;
import crosswalk.FileBlock;
import crosswalk.Form;
import crosswalk.FormElement;
import crosswalk.InputField;
import crosswalk.MajorBlock;
import crosswalk.MajorEntry;
import crosswalk.MetadataBlock;
import crosswalk.OutputElement;
import crosswalk.OutputMetadataSections;
import crosswalk.OutputProfile;
import crosswalk.TextInputField;

import cdr.forms.DepositResult.Status;

public class SwordDepositHandler implements DepositHandler, ExternalDepositFileConfigurationProvider {

    private static final Logger LOG = LoggerFactory.getLogger(SwordDepositHandler.class);

    private String serviceUrl;
    private String username;
    private String password;
    private String externalPath;
    private String externalUriBase;

    public String getServiceUrl() {
        return serviceUrl;
    }

    public void setServiceUrl(String serviceUrl) {
        this.serviceUrl = serviceUrl;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    private String defaultContainer = null;

    public String getDefaultContainer() {
        return defaultContainer;
    }

    /**
     * Set the default deposit container. String should be appropriate
     * 
     * @param defaultContainer
     */
    public void setDefaultContainer(String defaultContainer) {
        this.defaultContainer = defaultContainer;
    }

    public String getExternalPath() {
        return externalPath;
    }

    public void setExternalPath(String externalPath) {
        this.externalPath = externalPath;
    }

    public String getExternalUriBase() {
        return externalUriBase;
    }

    public void setExternalUriBase(String externalUriBase) {
        this.externalUriBase = externalUriBase;
    }

    public DepositResult deposit(Deposit deposit) {

        // Prepare the submission package

        Submission submission = Submission.create(deposit, this);

        File zipFile = makeZipFile(submission.getMetsDocumentRoot(), submission.getFiles());

        // Obtain the path for the collection in which we'll attempt to make the deposit

        Form form = deposit.getForm();

        String containerId = form.getDepositContainerId();

        if (containerId == null || "".equals(containerId.trim()))
            containerId = this.getDefaultContainer();

        String depositPath = getServiceUrl() + "collection/" + containerId;

        // Make the SWORD request

        String pid = "uuid:" + UUID.randomUUID().toString();

        HttpClient client = new HttpClient();

        UsernamePasswordCredentials creds = new UsernamePasswordCredentials(this.getUsername(), this.getPassword());
        client.getState().setCredentials(getAuthenticationScope(depositPath), creds);
        client.getParams().setAuthenticationPreemptive(true);

        PostMethod post = new PostMethod(depositPath);

        RequestEntity fileRequestEntity = new FileRequestEntity(zipFile, "application/zip");

        Header contentDispositionHeader = new Header("Content-Disposition", "attachment; filename=package.zip");
        post.addRequestHeader(contentDispositionHeader);

        Header packagingHeader = new Header("Packaging", "http://cdr.unc.edu/METS/profiles/Simple");
        post.addRequestHeader(packagingHeader);

        Header slugHeader = new Header("Slug", pid);
        post.addRequestHeader(slugHeader);

        post.setRequestEntity(fileRequestEntity);

        // Interpret the response from the SWORD endpoint

        DepositResult result = new DepositResult();

        try {

            // Set the result's status based on the HTTP response code

            int responseCode = client.executeMethod(post);

            if (responseCode >= 300) {
                LOG.error(String.valueOf(responseCode));
                LOG.error(post.getResponseBodyAsString());
                result.setStatus(Status.FAILED);
            } else {
                result.setStatus(Status.COMPLETE);
            }

            // Save the response body

            result.setResponseBody(post.getResponseBodyAsString());

            // Assign additional attributes based on the response body.

            try {

                Namespace atom = Namespace.getNamespace("http://www.w3.org/2005/Atom");

                SAXBuilder sx = new SAXBuilder();
                org.jdom.Document d = sx.build(post.getResponseBodyAsStream());

                // Set accessURL to the href of the first <link rel="alternate"> inside an Atom entry

                if (result.getStatus() == Status.COMPLETE) {

                    if (d.getRootElement().getNamespace().equals(atom)
                            && d.getRootElement().getName().equals("entry")) {
                        @SuppressWarnings("unchecked")
                        List<Element> links = d.getRootElement().getChildren("link", atom);

                        for (Element link : links) {
                            if ("alternate".equals(link.getAttributeValue("rel"))) {
                                result.setAccessURL(link.getAttributeValue("href"));
                                break;
                            }
                        }
                    }

                }

            } catch (JDOMException e) {
                LOG.error("There was a problem parsing the SWORD response.", e);
            }

            LOG.debug("response was: \n" + post.getResponseBodyAsString());

        } catch (HttpException e) {
            LOG.error("Exception during SWORD deposit", e);
            throw new Error(e);
        } catch (IOException e) {
            LOG.error("Exception during SWORD deposit", e);
            throw new Error(e);
        }

        return result;

    }

    private String serializeMets(gov.loc.mets.DocumentRoot root) {

        ResourceSet rs = new ResourceSetImpl();
        rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put("mets", new MetsResourceFactoryImpl());
        rs.getPackageRegistry().put(MODSPackage.eNS_URI, MODSPackage.eINSTANCE);
        rs.getPackageRegistry().put(MetsPackage.eNS_URI, MetsPackage.eINSTANCE);
        rs.getPackageRegistry().put(XlinkPackage.eNS_URI, XlinkPackage.eINSTANCE);
        rs.getPackageRegistry().put(AclPackage.eNS_URI, AclPackage.eINSTANCE);

        gov.loc.mets.MetsType mets = root.getMets();

        File tmp;
        try {
            tmp = File.createTempFile("tmp", ".mets");
        } catch (IOException e1) {
            throw new Error(e1);
        }

        URI uri = URI.createURI(tmp.toURI().toString());
        XMLResource res = (XMLResource) rs.createResource(uri);
        res.getContents().add(root);

        StringWriter sw = new StringWriter();
        Map<Object, Object> options = new HashMap<Object, Object>();

        options.put(XMLResource.OPTION_ENCODING, "utf-8");
        options.put(XMLResource.OPTION_DECLARE_XML, "");
        options.put(XMLResource.OPTION_LINE_WIDTH, new Integer(80));
        options.put(XMLResource.OPTION_ROOT_OBJECTS, Collections.singletonList(mets));

        try {
            res.save(sw, options);
        } catch (IOException e) {
            throw new Error("failed to serialize XML for model object", e);
        }

        return sw.toString();

    }

    private File makeZipFile(gov.loc.mets.DocumentRoot metsDocumentRoot,
            IdentityHashMap<DepositFile, String> filenames) {

        // Get the METS XML

        String metsXml = serializeMets(metsDocumentRoot);

        // Create the zip file

        File zipFile;

        try {
            zipFile = File.createTempFile("tmp", ".zip");
        } catch (IOException e) {
            throw new Error(e);
        }

        FileOutputStream fileOutput;

        try {
            fileOutput = new FileOutputStream(zipFile);
        } catch (FileNotFoundException e) {
            throw new Error(e);
        }

        ZipOutputStream zipOutput = new ZipOutputStream(fileOutput);

        try {

            ZipEntry entry;

            // Write the METS

            entry = new ZipEntry("mets.xml");
            zipOutput.putNextEntry(entry);

            PrintStream xmlPrintStream = new PrintStream(zipOutput);
            xmlPrintStream.print(metsXml);

            // Write files

            for (DepositFile file : filenames.keySet()) {

                if (!file.isExternal()) {

                    entry = new ZipEntry(filenames.get(file));
                    zipOutput.putNextEntry(entry);

                    FileInputStream fileInput = new FileInputStream(file.getFile());

                    byte[] buffer = new byte[1024];
                    int length;

                    while ((length = fileInput.read(buffer)) != -1)
                        zipOutput.write(buffer, 0, length);

                    fileInput.close();

                }

            }

            zipOutput.finish();
            zipOutput.close();

            fileOutput.close();

        } catch (IOException e) {

            throw new Error(e);

        }

        return zipFile;

    }

    /**
     * Generates a limited authentication scope for the supplied URL, so that an HTTP client will not send username and
     * passwords to other URLs.
     * 
     * @param queryURL
     *           the URL for the query.
     * @return an authentication scope tuned to the requested URL.
     * @throws IllegalArgumentException
     *            if <code>queryURL</code> is not a well-formed URL.
     */
    public static AuthScope getAuthenticationScope(String queryURL) {
        if (queryURL == null) {
            throw new NullPointerException("Cannot derive authentication scope for null URL");
        }
        try {
            URL url = new URL(queryURL);
            // port defaults to 80 unless the scheme is https
            // or the port is explicitly set in the URL.
            int port = 80;
            if (url.getPort() == -1) {
                if ("https".equals(url.getProtocol())) {
                    port = 443;
                }
            } else {
                port = url.getPort();
            }
            return new AuthScope(url.getHost(), port);
        } catch (MalformedURLException mue) {
            throw new IllegalArgumentException("supplied URL <" + queryURL + "> is ill-formed:" + mue.getMessage());
        }
    }

}