Package org.fcrepo.server.rest

Source Code of org.fcrepo.server.rest.FedoraObjectsResource

/* 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.server.rest;

import java.io.ByteArrayInputStream;
import java.io.CharArrayWriter;
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.fcrepo.common.Constants;
import org.fcrepo.server.Context;
import org.fcrepo.server.Server;
import org.fcrepo.server.access.ObjectProfile;
import org.fcrepo.server.rest.RestUtil.RequestContent;
import org.fcrepo.server.rest.param.DateTimeParam;
import org.fcrepo.server.search.Condition;
import org.fcrepo.server.search.FieldSearchQuery;
import org.fcrepo.server.search.FieldSearchResult;
import org.fcrepo.server.storage.types.Validation;
import org.fcrepo.server.utilities.StreamUtility;
import org.fcrepo.utilities.DateUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
* Implement /objects/pid/* REST API
*
* @author cuong.tran@yourmediashelf.com
* @version $Id$
*/
@Path("/")
@Component
public class FedoraObjectsResource extends BaseRestResource {
    private final String FOXML1_1 = "info:fedora/fedora-system:FOXML-1.1";
    private final String ATOMZIP1_1 = "info:fedora/fedora-system:ATOMZip-1.1";

    static final String[] SEARCHABLE_FIELDS = { "pid", "label", "state", "ownerId",
        "cDate", "mDate", "dcmDate", "title", "creator", "subject", "description",
        "publisher", "contributor", "date", "type", "format", "identifier",
        "source", "language", "relation", "coverage", "rights" };

    private static final Logger logger =
            LoggerFactory.getLogger(FedoraObjectsResource.class);

    public FedoraObjectsResource(Server server) {
        super(server);
    }

    @GET
    @Path("/")
    @Produces( { HTML, XML })
    public Response searchObjects(
            @QueryParam(RestParam.TERMS)
            String terms,
            @QueryParam(RestParam.QUERY)
            String query,
            @QueryParam(RestParam.MAX_RESULTS)
            @DefaultValue("25")
            int maxResults,
            @QueryParam(RestParam.SESSION_TOKEN)
            String sessionToken,
            @QueryParam(RestParam.RESULT_FORMAT)
            @DefaultValue(HTML)
            String format,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {

        try {
            Context context = getContext();
            String[] wantedFields = getWantedFields(m_servletRequest);
            MediaType mime = RestHelper.getContentType(format);

            FieldSearchResult result = null;

            if (wantedFields.length > 0 || sessionToken != null) {
                if (sessionToken != null) {
                    result = m_access.resumeFindObjects(context, sessionToken);
                } else {
                    if ((terms != null) && (terms.length() != 0)) {
                        result = m_access.findObjects(context, wantedFields, maxResults, new FieldSearchQuery(terms));
                    } else {
                        result = m_access.findObjects(context, wantedFields, maxResults, new FieldSearchQuery(Condition.getConditions(query)));
                    }
                }
            }

            String output;
            if (TEXT_HTML.isCompatible(mime)) {
                output = getSerializer(context).searchResultToHtml(query, terms, SEARCHABLE_FIELDS, wantedFields, maxResults, result);
            } else {
                output = getSerializer(context).searchResultToXml(result);

            }

            return Response.ok(output, mime).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Implements the "getNextPID" functionality of the Fedora Management LITE
     * (API-M-LITE) interface using a java servlet front end. The syntax defined
     * by API-M-LITE for getting a list of the next available PIDs has the
     * following binding:
     * <ol>
     * <li>getNextPID URL syntax:
     * protocol://hostname:port/fedora/objects/nextPID[?numPIDs=NUMPIDS&namespace=NAMESPACE&format=html,xml]
     * This syntax requests a list of next available PIDS. The parameter numPIDs
     * determines the number of requested PIDS to generate. If omitted, numPIDs
     * defaults to 1. The namespace parameter determines the namespace to be
     * used in generating the PIDs. If omitted, namespace defaults to the
     * namespace defined in the fedora.fcfg configuration file for the parameter
     * pidNamespace. The xml parameter determines the type of output returned.
     * If the parameter is omitted or has a value of "false", a MIME-typed
     * stream consisting of an html table is returned providing a browser-savvy
     * means of viewing the object profile. If the value specified is "true",
     * then a MIME-typed stream consisting of XML is returned.</li>
     */
    @Path("/nextPID")
    @POST
    public Response getNextPID(
            @QueryParam("numPIDs")
            @DefaultValue("1")
            int numPIDS,
            @QueryParam(RestParam.NAMESPACE)
            String namespace,
            @QueryParam("format")
            @DefaultValue(HTML)
            String format,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) throws Exception {

        try {
            Context context = getContext();
            String[] pidList = m_management.getNextPID(context, numPIDS, namespace);
            MediaType mime = RestHelper.getContentType(format);

            if (pidList.length > 0) {
                String output = getSerializer(context).pidsToXml(pidList);

                if (TEXT_HTML.isCompatible(mime)) {
                    CharArrayWriter writer = new CharArrayWriter();
                    transform(output, "management/getNextPIDInfo.xslt", writer);
                    output = writer.toString();
                }

                return Response.ok(output, mime).build();
            } else {
                return Response.noContent().build();
            }
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    private static String[] getWantedFields(
            HttpServletRequest request) {
        List<String> fields = new ArrayList<String>();

        for (String f : SEARCHABLE_FIELDS) {
            if ("true".equals(request.getParameter(f))) {
                fields.add(f);
            }
        }

        return fields.toArray(new String[fields.size()]);
    }

    @Path(VALID_PID_PART +"/validate")
    @GET
    @Produces({XML})
    public Response doObjectValidation(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.AS_OF_DATE_TIME)
            String dateTime,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {
        try {
            Context context = getContext();
            Date asOfDateTime = DateUtility.parseDateOrNull(dateTime);
            MediaType mediaType = TEXT_XML;

            Validation validation = m_management.validate(context, pid, asOfDateTime);

            String xml = getSerializer(context).objectValidationToXml(validation);
            return Response.ok(xml, mediaType).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }


    /**
     * Exports the entire digital object in the specified XML format
     * ("info:fedora/fedora-system:FOXML-1.1" or
     * "info:fedora/fedora-system:METSFedoraExt-1.1"), and encoded appropriately
     * for the specified export context ("public", "migrate", or "archive").
     * <p/>
     * GET /objects/{pid}/export ? format context encoding
     */
    @Path(VALID_PID_PART + "/export")
    @GET
    @Produces({XML, ZIP})
    public Response getObjectExport(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.FORMAT)
            @DefaultValue(FOXML1_1)
            String format,
            @QueryParam(RestParam.EXPORT_CONTEXT)
            String exportContext,
            @QueryParam(RestParam.ENCODING)
            @DefaultValue(DEFAULT_ENC)
            String encoding,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {

        try {
            Context context = getContext();
            InputStream is = m_management.export(context, pid, format, exportContext, encoding);
            MediaType mediaType = TEXT_XML;
            if (format.equals(ATOMZIP1_1)) {
                mediaType = MediaType.valueOf(ZIP);
            }
            return Response.ok(is, mediaType).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Gets a list of timestamps indicating when components changed in an
     * object. This is a set of timestamps indicating when a datastream or
     * disseminator was created or modified in the object. These timestamps can
     * be used to request a timestamped dissemination request to view the object
     * as it appeared at a specific point in time.
     * <p/>
     * GET /objects/{pid}/versions ? format
     */
    @Path(VALID_PID_PART + "/versions")
    @GET
    public Response getObjectHistory(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.FORMAT)
            @DefaultValue(HTML)
            String format,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {

        try {
            Context context = getContext();
            String[] objectHistory = m_access.getObjectHistory(context, pid);
            String xml = getSerializer(context).objectHistoryToXml(objectHistory, pid);
            MediaType mime = RestHelper.getContentType(format);

            if (TEXT_HTML.isCompatible(mime)) {
                CharArrayWriter writer = new CharArrayWriter();
                transform(xml, "access/viewObjectHistory.xslt", writer);
                xml = writer.toString();
            }

            return Response.ok(xml, mime).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Gets a profile of the object which includes key metadata fields and URLs
     * for the object Dissemination Index and the object Item Index. Can be
     * thought of as a default view of the object.
     * <p/>
     * GET /objects/{pid}/objectXML
     */
    @Path(VALID_PID_PART + "/objectXML")
    @GET
    @Produces(XML)
    public Response getObjectXML(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {

        try {
            Context context = getContext();
            InputStream is = m_management.getObjectXML(context, pid, DEFAULT_ENC);

            return Response.ok(is, TEXT_XML).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Gets a profile of the object which includes key metadata fields and URLs
     * for the object Dissemination Index and the object Item Index. Can be
     * thought of as a default view of the object.
     * <p/>
     * GET /objects/{pid} ? format asOfDateTime
     */
    @GET
    @Produces({HTML, XML})
    @Path(VALID_PID_PART)
    public Response getObjectProfile(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.AS_OF_DATE_TIME)
            String dateTime,
            @QueryParam(RestParam.FORMAT)
            @DefaultValue(HTML)
            String format,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {

        try {
            Date asOfDateTime = DateUtility.parseDateOrNull(dateTime);
            Context context = getContext();
            ObjectProfile objProfile = m_access.getObjectProfile(context, pid, asOfDateTime);
            String xml = getSerializer(context).objectProfileToXML(objProfile, asOfDateTime);

            MediaType mime = RestHelper.getContentType(format);

            if (TEXT_HTML.isCompatible(mime)) {
                CharArrayWriter writer = new CharArrayWriter();
                transform(xml, "access/viewObjectProfile.xslt", writer);
                xml = writer.toString();
            }

            return Response.ok(xml, mime).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Permanently removes an object from the repository.
     * <p/>
     * DELETE /objects/{pid} ? logMessage
     */
    @DELETE
    @Path(VALID_PID_PART)
    public Response deleteObject(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam("logMessage")
            String logMessage,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {
        try {
            Context context = getContext();
            Date d = m_management.purgeObject(context, pid, logMessage);
            return Response.ok(DateUtility.convertDateToXSDString(d), MediaType.TEXT_PLAIN_TYPE).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    @POST
    @Path("/new")
    @Consumes({XML, FORM})
    public Response newObject(
                              @javax.ws.rs.core.Context
                              HttpHeaders headers,
                              @QueryParam(RestParam.LABEL)
                              String label,
                              @QueryParam(RestParam.LOG_MESSAGE)
                              String logMessage,
                              @QueryParam(RestParam.FORMAT)
                              @DefaultValue(FOXML1_1)
                              String format,
                              @QueryParam(RestParam.ENCODING)
                              @DefaultValue(DEFAULT_ENC)
                              String encoding,
                              @QueryParam(RestParam.NAMESPACE)
                              String namespace,
                              @QueryParam(RestParam.OWNER_ID)
                              String ownerID,
                              @QueryParam(RestParam.STATE)
                              @DefaultValue("A")
                              String state,
                              @QueryParam(RestParam.IGNORE_MIME)
                              @DefaultValue("false")
                              boolean ignoreMime,
                              @QueryParam(RestParam.FLASH)
                              @DefaultValue("false")
                              boolean flash) {
        return createObject(headers, "new", label, logMessage, format, encoding, namespace, ownerID, state, ignoreMime, flash);
    }
    /**
     * Create/Update a new digital object. If no xml given in the body, will
     * create an empty object.
     * <p/>
     * POST /objects/{pid} ? label logMessage format encoding namespace ownerId state
     */
    @POST
    @Path(VALID_PID_PART)
    @Consumes({XML, FORM, ZIP})
    public Response createObject(
            @javax.ws.rs.core.Context
            HttpHeaders headers,
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.LABEL)
            String label,
            @QueryParam(RestParam.LOG_MESSAGE)
            String logMessage,
            @QueryParam(RestParam.FORMAT)
            @DefaultValue(FOXML1_1)
            String format,
            @QueryParam(RestParam.ENCODING)
            @DefaultValue(DEFAULT_ENC)
            String encoding,
            @QueryParam(RestParam.NAMESPACE)
            String namespace,
            @QueryParam(RestParam.OWNER_ID)
            String ownerID,
            @QueryParam(RestParam.STATE)
            @DefaultValue("A")
            String state,
            @QueryParam(RestParam.IGNORE_MIME)
            @DefaultValue("false")
            boolean ignoreMime,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {
        try {
            Context context = getContext();

            InputStream is = null;

            // Determine if content is provided
            RequestContent content =
                    RestUtil.getRequestContent(m_servletRequest, headers);
            if (content != null && content.getContentStream() != null) {
                if (ignoreMime) {
                    is = content.getContentStream();
                } else {
                    // Make sure content is XML or ZIP
                    String contentMime = content.getMimeType();
                    if (contentMime != null) {
                        MediaType t = MediaType.valueOf(contentMime);
                        if (TEXT_XML.isCompatible(t) || APP_ZIP.isCompatible(t)) {
                            is = content.getContentStream();
                        }
                    }
                }
            }

            // If no content is provided, use a FOXML template
            if (is == null) {
                if (pid == null || pid.equals("new")) {
                    pid = m_management.getNextPID(context, 1, namespace)[0];
                }

                if (ownerID == null || "".equals(ownerID.trim())) {
                    ownerID = context.getSubjectValue(Constants.SUBJECT.LOGIN_ID.uri);
                }
                is = new ByteArrayInputStream(getFOXMLTemplate(pid, label, ownerID, encoding).getBytes());
            } else {

                if (namespace != null && !namespace.equals("")) {
                    logger.warn("The namespace parameter is only applicable whene object " +
                                "content is not provided, thus the namespace provided '" +
                                namespace + "' has been ignored.");
                }
            }

            pid = m_management.ingest(context, is, logMessage, format, encoding, pid);

            URI createdLocation = m_uriInfo.getRequestUri().resolve(URLEncoder.encode(pid, DEFAULT_ENC));
            return Response.created(createdLocation).entity(pid).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Update (modify) digital object.
     * <p>PUT /objects/{pid} ? label logMessage ownerId state lastModifiedDate</p>
     *
     * @param pid              the persistent identifier
     * @param label
     * @param logMessage
     * @param ownerID
     * @param state
     * @param lastModifiedDate Optional XSD dateTime to guard against concurrent
     *                         modification. If provided (i.e. not null), the request will fail with an
     *                         HTTP 409 Conflict if lastModifiedDate is earlier than the object's
     *                         lastModifiedDate.
     * @return The timestamp for this modification (as an XSD dateTime string)
     * @see org.fcrepo.server.management.Management#modifyObject(org.fcrepo.server.Context, String, String, String, String, String)
     */
    @PUT
    @Path(VALID_PID_PART)
    @Produces(MediaType.TEXT_PLAIN)
    public Response updateObject(
            @PathParam(RestParam.PID)
            String pid,
            @QueryParam(RestParam.LABEL)
            String label,
            @QueryParam(RestParam.LOG_MESSAGE)
            String logMessage,
            @QueryParam(RestParam.OWNER_ID)
            String ownerID,
            @QueryParam(RestParam.STATE)
            String state,
            @QueryParam(RestParam.LAST_MODIFIED_DATE)
            DateTimeParam lastModifiedDate,
            @QueryParam(RestParam.FLASH)
            @DefaultValue("false")
            boolean flash) {
        try {
            Context context = getContext();
            Date requestModDate = null;
            if (lastModifiedDate != null) {
                requestModDate = lastModifiedDate.getValue();
            }
            Date lastModDate =
                    m_management.modifyObject(context, pid, state, label, ownerID, logMessage, requestModDate);
            return Response.ok().entity(DateUtility.convertDateToXSDString(lastModDate)).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    private static String getFOXMLTemplate(
            String pid,
            String label,
            String ownerId,
            String encoding) {
        StringBuilder xml = new StringBuilder();
        xml.append("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
        xml.append("<foxml:digitalObject VERSION=\"1.1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
        xml.append("    xmlns:foxml=\"info:fedora/fedora-system:def/foxml#\"\n");
        xml.append(
                "           xsi:schemaLocation=\"" + Constants.FOXML.uri + " " + Constants.FOXML1_1.xsdLocation + "\"");
        if (pid != null && pid.length() > 0) {
            xml.append("\n           PID=\"" + StreamUtility.enc(pid) + "\">\n");
        } else {
            xml.append(">\n");
        }
        xml.append("  <foxml:objectProperties>\n");
        xml.append("    <foxml:property NAME=\"info:fedora/fedora-system:def/model#state\" VALUE=\"A\"/>\n");
        xml.append("    <foxml:property NAME=\"info:fedora/fedora-system:def/model#label\" VALUE=\""
                   + StreamUtility.enc(label) + "\"/>\n");
        xml.append("    <foxml:property NAME=\"info:fedora/fedora-system:def/model#ownerId\" VALUE=\""
                   + ownerId + "\"/>\n");
        xml.append("  </foxml:objectProperties>\n");
        xml.append("</foxml:digitalObject>");

        return xml.toString();
    }
}
TOP

Related Classes of org.fcrepo.server.rest.FedoraObjectsResource

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.