Package org.fcrepo.server.rest

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

/* 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 org.fcrepo.common.http.WebClient;
import org.fcrepo.common.http.WebClientConfiguration;
import org.fcrepo.server.Context;
import org.fcrepo.server.Server;
import org.fcrepo.server.rest.RestUtil.RequestContent;
import org.fcrepo.server.rest.param.DateTimeParam;
import org.fcrepo.server.storage.types.Datastream;
import org.fcrepo.server.storage.types.DatastreamDef;
import org.fcrepo.server.storage.types.MIMETypedStream;
import org.fcrepo.utilities.DateUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
* A rest controller to handle CRUD operations for the Fedora datastream API
* (API-M) Request syntax:
* <p/>
* GET,PUT,POST,DELETE
* prototol://hostname:port/fedora/objects/PID/datastreams/DSID/versions ?
* [dateTime][parmArray]
* <ul>
* <li>protocol - either http or https.</li>
* <li>hostname - required hostname of the Fedora server.</li>
* <li>port - required port number on which the Fedora server is running.</li>
* <li>fedora - required path name for the Fedora access service.</li>
* <li>objects - required path name for the Fedora service.</li>
* <li>PID - required persistent idenitifer of the digital object.</li>
* <li>DSID - required datastream identifier for the datastream.</li>
* <li>dateTime - optional dateTime value indicating dissemination of a version
* of the digital object at the specified point in time.
* <li>parmArray - optional array of method parameters consisting of name/value
* pairs in the form parm1=value1&parm2=value2...</li>
*
* @author cuong.tran@yourmediashelf.com
* @version $Id$
*/
@Path(BaseRestResource.VALID_PID_PART + "/datastreams")
@Component
public class DatastreamResource
        extends BaseRestResource {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatastreamResource.class);

    public DatastreamResource(Server server) {
        super(server);
    }
    /**
     * Inquires upon all object Datastreams to obtain datastreams contained by a
     * digital object. This returns a set of datastream locations that represent
     * all possible datastreams available in the object.
     * <p/>
     * GET /objects/{pid}/datastreams ? asOfDateTime format
     *
     * @param pid
     * @param dateTime
     * @param format
     */
    @GET
    public Response listDatastreams(@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,
                                    @QueryParam(RestParam.PROFILES) @DefaultValue("false") boolean profiles,
                                    @QueryParam(RestParam.DS_STATE) String dsState,
                                    @QueryParam(RestParam.VALIDATE_CHECKSUM) @DefaultValue("false") boolean validateChecksum
                                    ) {

        try {
            Date asOfDateTime = DateUtility.parseDateOrNull(dateTime);
            Context context = getContext();
            MediaType mime = RestHelper.getContentType(format);

            String output;

            if (profiles){
                mime=MediaType.TEXT_XML_TYPE;
                final Datastream[] datastreams = m_management.getDatastreams(context, pid, asOfDateTime, dsState);
                output=getSerializer(context).datastreamProfilesToXML(pid, datastreams, asOfDateTime, validateChecksum);
            } else {
                mime = RestHelper.getContentType(format);
                DatastreamDef[] dsDefs =
                        m_access.listDatastreams(context, pid, asOfDateTime);
                output = getSerializer(context).dataStreamsToXML(pid, asOfDateTime, dsDefs);
                if (TEXT_HTML.isCompatible(mime)) {
                    final CharArrayWriter writer = new CharArrayWriter();
                    transform(output, "access/listDatastreams.xslt", writer);
                    output = writer.toString();
                }
            }

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

    /**
     * Invoke API-M.getDatastream(context, pid, dsID, asOfDateTime)
     * <p/>
     * GET /objects/{pid}/datastreams/{dsID} ? asOfDateTime &
     * validateChecksum=true|false
     */
    @Path("/{dsID}")
    @GET
    public Response getDatastreamProfile(@PathParam(RestParam.PID) String pid,
                                         @PathParam(RestParam.DSID) String dsID,
                                         @QueryParam(RestParam.AS_OF_DATE_TIME) String dateTime,
                                         @QueryParam(RestParam.FORMAT) @DefaultValue(HTML) String format,
                                         @QueryParam(RestParam.VALIDATE_CHECKSUM) @DefaultValue("false") boolean validateChecksum,
                                         @QueryParam(RestParam.FLASH) @DefaultValue("false") boolean flash) {
        try {
            Date asOfDateTime = DateUtility.parseDateOrNull(dateTime);
            Context context = getContext();
            Datastream dsProfile =
                    m_management.getDatastream(context, pid, dsID, asOfDateTime);

            if (dsProfile == null) {
                return Response
                        .status(Status.NOT_FOUND)
                        .type("text/plain")
                        .entity("No datastream could be found. Either there is no datastream for "
                                + "the digital object \""
                                + pid
                                + "\" with datastream ID of \""
                                + dsID
                                + "\"  OR  there are no datastreams that match the specified "
                                + "date/time value of \"" + dateTime + "\".")
                        .build();
            }

            String xml =
                    getSerializer(context)
                            .datastreamProfileToXML(pid,
                                                    dsID,
                                                    dsProfile,
                                                    asOfDateTime,
                                                    validateChecksum);

            MediaType mime = RestHelper.getContentType(format);

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

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

    /**
     * Invoke API-M.getDatastreamHistory(context,pid,dsId) GET
     * /objects/{pid}/datastreams/{dsID}/history
     *
     * @param pid
     *        the PID of the digital object
     * @param dsID
     *        the ID of the datastream
     * @param format
     *        the desired format. Either html or "xml"
     * @return the response, either in XML or XHTML format
     */
    @Path("/{dsID}/history")
    @GET
    public Response getDatastreamHistory(@PathParam(RestParam.PID) String pid,
                                         @PathParam(RestParam.DSID) String dsID,
                                         @QueryParam(RestParam.FORMAT) @DefaultValue(HTML) String format,
                                         @QueryParam(RestParam.FLASH) @DefaultValue("false") boolean flash) {
        try {
            Context context = getContext();
            Datastream[] datastreamHistory =
                    m_management.getDatastreamHistory(context, pid, dsID);

            if (datastreamHistory == null || datastreamHistory.length == 0) {
                return Response
                        .status(Status.NOT_FOUND)
                        .type("text/plain")
                        .entity("No datastream history could be found. There is no datastream history for "
                                + "the digital object \""
                                + pid
                                + "\" with datastream ID of \"" + dsID).build();

            }

            String xml =
                    getSerializer(context)
                            .datastreamHistoryToXml(pid,
                                                    dsID,
                                                    datastreamHistory);

            MediaType mime = RestHelper.getContentType(format);

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

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

    /**
     * Invoke API-A.getDatastreamDissemination(context, pid, dsID, asOfDateTime)
     * <p/>
     * GET /objects/{pid}/datastreams/{dsID}/content ? asOfDateTime
     */
    @Path("/{dsID}/content")
    @GET
    public Response getDatastream(@PathParam(RestParam.PID) String pid,
                                  @PathParam(RestParam.DSID) String dsID,
                                  @QueryParam(RestParam.AS_OF_DATE_TIME) String dateTime,
                                  @QueryParam(RestParam.DOWNLOAD) String download,
                                  @QueryParam(RestParam.FLASH) @DefaultValue("false") boolean flash) {
        Context context = getContext();
        try {
            Date asOfDateTime = DateUtility.parseDateOrNull(dateTime);
            MIMETypedStream stream =
                    m_access.getDatastreamDissemination(context,
                                                           pid,
                                                           dsID,
                                                           asOfDateTime);
            if (m_datastreamFilenameHelper != null) {
                m_datastreamFilenameHelper
                        .addContentDispositionHeader(context,
                                                     pid,
                                                     dsID,
                                                     download,
                                                     asOfDateTime,
                                                     stream);

            }

            return buildResponse(stream);
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Invoke API-M.purgeDatastream
     * <p/>
     * DELETE /objects/{pid}/datastreams/{dsID} ? startDT endDT logMessage
     */
    @Path("/{dsID}")
    @DELETE
    @Produces(MediaType.APPLICATION_JSON)
    public Response deleteDatastream(@PathParam(RestParam.PID) String pid,
                                     @PathParam(RestParam.DSID) String dsID,
                                     @QueryParam(RestParam.START_DT) String startDT,
                                     @QueryParam(RestParam.END_DT) String endDT,
                                     @QueryParam(RestParam.LOG_MESSAGE) String logMessage,
                                     @QueryParam(RestParam.FLASH) @DefaultValue("false") boolean flash) {

        try {
            Context context = getContext();
            Date startDate = DateUtility.parseDateOrNull(startDT);
            Date endDate = DateUtility.parseDateOrNull(endDT);
            Date[] purged = m_management.purgeDatastream(context,
                                        pid,
                                        dsID,
                                        startDate,
                                        endDate,
                                        logMessage);

            // convert purged into a String[] so we can return it as a JSON array
            List<String> results = new ArrayList<String>();
            for (Date d : purged) {
                results.add(DateUtility.convertDateToXSDString(d));
            }
            return Response.ok(m_mapper.writeValueAsString(results)).build();
        } catch (Exception ex) {
            return handleException(ex, flash);
        }
    }

    /**
     * Modify an existing datastream.
     * <p/>
     * PUT /objects/{pid}/datastreams/{dsID} ? dsLocation altIDs dsLabel
     * versionable dsState formatURI checksumType checksum logMessage
     * <p/>
     * Successful Response: Status: 200 OK Content-Type: text/xml Body: XML
     * datastream profile
     */
    @Path("/{dsID}")
    @PUT
    public Response modifyDatastream(@PathParam(RestParam.PID) String pid,
                                     @PathParam(RestParam.DSID) String dsID,
                                     @QueryParam(RestParam.DS_LOCATION) String dsLocation,
                                     @QueryParam(RestParam.ALT_IDS) List<String> altIDs,
                                     @QueryParam(RestParam.DS_LABEL) String dsLabel,
                                     @QueryParam(RestParam.VERSIONABLE) Boolean versionable,
                                     @QueryParam(RestParam.DS_STATE) String dsState,
                                     @QueryParam(RestParam.FORMAT_URI) String formatURI,
                                     @QueryParam(RestParam.CHECKSUM_TYPE) String checksumType,
                                     @QueryParam(RestParam.CHECKSUM) String checksum,
                                     @QueryParam(RestParam.MIME_TYPE) String mimeType,
                                     @QueryParam(RestParam.LOG_MESSAGE) String logMessage,
                                     @QueryParam(RestParam.IGNORE_CONTENT) @DefaultValue("false") boolean ignoreContent,
                                     @QueryParam(RestParam.LAST_MODIFIED_DATE) DateTimeParam lastModifiedDate,
                                     @QueryParam(RestParam.FLASH) @DefaultValue("false") boolean flash) {
        return addOrUpdateDatastream(false,
                                     pid,
                                     dsID,
                                     m_headers.getMediaType(),
                                     mimeType,
                                     null,
                                     dsLocation,
                                     altIDs,
                                     dsLabel,
                                     versionable,
                                     dsState,
                                     formatURI,
                                     checksumType,
                                     checksum,
                                     logMessage,
                                     ignoreContent,
                                     lastModifiedDate,
                                     flash);
    }

    /**
     * Add or modify a datastream.
     * <p/>
     * POST /objects/{pid}/datastreams/{dsID} ? controlGroup dsLocation altIDs
     * dsLabel versionable dsState formatURI checksumType checksum logMessage
     * <p/>
     * Successful Response: Status: 201 Created Location: Datastream profile URL
     * Content-Type: text/xml Body: XML datastream profile
     */
    @Path("/{dsID}")
    @POST
    public Response addDatastream(@PathParam(RestParam.PID) String pid,
                                  @PathParam(RestParam.DSID) String dsID,
                                  @QueryParam(RestParam.CONTROL_GROUP) @DefaultValue("X") String controlGroup,
                                  @QueryParam(RestParam.DS_LOCATION) String dsLocation,
                                  @QueryParam(RestParam.ALT_IDS) List<String> altIDs,
                                  @QueryParam(RestParam.DS_LABEL) String dsLabel,
                                  @QueryParam(RestParam.VERSIONABLE) @DefaultValue("true") Boolean versionable,
                                  @QueryParam(RestParam.DS_STATE) @DefaultValue("A") String dsState,
                                  @QueryParam(RestParam.FORMAT_URI) String formatURI,
                                  @QueryParam(RestParam.CHECKSUM_TYPE) String checksumType,
                                  @QueryParam(RestParam.CHECKSUM) String checksum,
                                  @QueryParam(RestParam.MIME_TYPE) String mimeType,
                                  @QueryParam(RestParam.LOG_MESSAGE) String logMessage,
                                  @QueryParam(RestParam.FLASH) @DefaultValue("false") boolean flash) {
        return addOrUpdateDatastream(true,
                                     pid,
                                     dsID,
                                     m_headers.getMediaType(),
                                     mimeType,
                                     controlGroup,
                                     dsLocation,
                                     altIDs,
                                     dsLabel,
                                     versionable,
                                     dsState,
                                     formatURI,
                                     checksumType,
                                     checksum,
                                     logMessage,
                                     false,
                                     null,
                                     flash);
    }

    protected Response addOrUpdateDatastream(boolean posted,
                                             String pid,
                                             String dsID,
                                             MediaType mediaType,
                                             String mimeType,
                                             String controlGroup,
                                             String dsLocation,
                                             List<String> altIDList,
                                             String dsLabel,
                                             Boolean versionable,
                                             String dsState,
                                             String formatURI,
                                             String checksumType,
                                             String checksum,
                                             String logMessage,
                                             boolean ignoreContent,
                                             DateTimeParam lastModifiedDate,
                                             boolean flash) {

        try {
          LOGGER.info("addOrUpdate");
            String[] altIDs = {};

            if (altIDList != null && altIDList.size() > 0) {
                altIDs = altIDList.toArray(new String[altIDList.size()]);
            }

            Context context = getContext();

            Datastream existingDS =
                    m_management.getDatastream(context, pid, dsID, null);
            if (!posted && versionable == null && existingDS != null){
                versionable = existingDS.DSVersionable;
            }
            Date requestModDate = null;
            if (lastModifiedDate != null) {
                requestModDate = lastModifiedDate.getValue();
            }

            // If a datastream is set to Deleted state, it must be set to
            // another state before any other changes can be made
            if (existingDS != null && existingDS.DSState.equals("D")
                    && dsState != null) {
                if (dsState.equals("A") || dsState.equals("I")) {
                    m_management.setDatastreamState(context,
                                                   pid,
                                                   dsID,
                                                   dsState,
                                                   logMessage);
                    existingDS.DSState = dsState;
                }
            }

            InputStream is = null;

            // Determine if datastream content is included in the request
            if (!ignoreContent) {
                RequestContent content =
                        RestUtil.getRequestContent(m_servletRequest, m_headers);

                if (content != null && content.getContentStream() != null) {
                    is = content.getContentStream();
                    // Give preference to the passed in mimeType
                    if (mimeType == null && content.getMimeType() != null) {
                        mimeType = content.getMimeType();
                    }
                }
            } else {
              LOGGER.warn("ignoring content on {}/{}", pid, dsID);
            }

            // Make sure that there is a mime type value
            if (mimeType == null && mediaType != null) {
                mimeType = mediaType.toString();
            } else if (mimeType == null && mediaType == null
                    && existingDS != null) {
                mimeType = existingDS.DSMIME;
            }

            // set default control group based on mimeType
            if (dsLocation == null
                    && TEXT_XML.isCompatible(MediaType.valueOf(mimeType))
                    && controlGroup == null) {
                controlGroup = "X";
            }

            if (existingDS == null) {
                if (posted) {
                  LOGGER.debug("new ds posted at {}/{}", pid, dsID);
                    if ((dsLocation == null || dsLocation.equals(""))
                            && ("X".equals(controlGroup) || "M"
                                    .equals(controlGroup))) {
                      if (is == null) {
                        LOGGER.warn("No content stream to copy for {}/{}",
                            pid, dsID);
                        return Response.status(Response.Status.BAD_REQUEST)
                            .build();
                      }
                        dsLocation = m_management.putTempStream(context, is);
                    }
                    dsID =
                            m_management.addDatastream(context,
                                                      pid,
                                                      dsID,
                                                      altIDs,
                                                      dsLabel,
                                                      versionable,
                                                      mimeType,
                                                      formatURI,
                                                      dsLocation,
                                                      controlGroup,
                                                      dsState,
                                                      checksumType,
                                                      checksum,
                                                      logMessage);
                } else {
                  LOGGER.warn("new ds but no posted content at {}/{}", pid, dsID);
                    return Response.status(Response.Status.NOT_FOUND).build();
                }
            } else {
                if ("X".equals(existingDS.DSControlGrp)) {
                    // Inline XML can only be modified by value. If there is no stream,
                    // but there is a dsLocation attempt to retrieve the content.
                    if (is == null && dsLocation != null
                            && !dsLocation.equals("")) {
                        try {
                            WebClientConfiguration webconfig = m_server.getWebClientConfig();
                            WebClient webClient = new WebClient(webconfig);
                            is = webClient.get(dsLocation, true);
                        } catch (IOException ioe) {
                            throw new Exception("Could not retrive content from "
                                    + dsLocation
                                    + " due to error: "
                                    + ioe.getMessage());
                        }
                    }
                    m_management.modifyDatastreamByValue(context,
                                                        pid,
                                                        dsID,
                                                        altIDs,
                                                        dsLabel,
                                                        mimeType,
                                                        formatURI,
                                                        is,
                                                        checksumType,
                                                        checksum,
                                                        logMessage,
                                                        requestModDate);
                } else {
                    // Managed content can only be modified by reference.
                    // If there is no dsLocation, but there is a content stream,
                    // store the stream in a temporary location.
                    if (dsLocation == null
                            && ("M".equals(existingDS.DSControlGrp))) {
                        if (is != null) {
                            dsLocation = m_management.putTempStream(context, is);
                        } else {
                            dsLocation = null;
                        }
                    }

                    m_management.modifyDatastreamByReference(context,
                                                            pid,
                                                            dsID,
                                                            altIDs,
                                                            dsLabel,
                                                            mimeType,
                                                            formatURI,
                                                            dsLocation,
                                                            checksumType,
                                                            checksum,
                                                            logMessage,
                                                            requestModDate);
                }

                if (dsState != null) {
                    if (dsState.equals("A") || dsState.equals("D")
                            || dsState.equals("I")) {
                        if (!dsState.equals(existingDS.DSState)) {
                            m_management.setDatastreamState(context,
                                                           pid,
                                                           dsID,
                                                           dsState,
                                                           logMessage);
                        }
                    }
                }

                if (versionable != existingDS.DSVersionable) {
                    m_management.setDatastreamVersionable(context,
                                                         pid,
                                                         dsID,
                                                         versionable,
                                                         logMessage);
                }
            }

            ResponseBuilder builder;
            if (posted) {
                builder =
                        Response.created(m_uriInfo.getRequestUri()
                                .resolve(URLEncoder.encode(dsID, DEFAULT_ENC)));
            } else { // put
                builder = Response.ok();
            }
            builder.header("Content-Type", MediaType.TEXT_XML);
            Datastream dsProfile =
                    m_management.getDatastream(context, pid, dsID, null);
            String xml =
                    getSerializer(context).datastreamProfileToXML(pid,
                                                                  dsID,
                                                                  dsProfile,
                                                                  null,
                                                                  false);
            builder.entity(xml);
            return builder.build();
        } catch (Exception ex) {
          LOGGER.warn(ex.getMessage(), ex);
            return handleException(ex, flash);
        }
    }
}
TOP

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

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.