/*
* � Copyright IBM Corp. 2011
*
* 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 com.ibm.domino.das.resources;
import static com.ibm.domino.commons.model.IGatekeeperProvider.FEATURE_REST_API_DATA_DOCUMENT;
import static com.ibm.domino.das.service.DataService.STAT_DOCUMENT;
import static com.ibm.domino.das.servlet.DasServlet.DAS_LOGGER;
import static com.ibm.domino.services.HttpServiceConstants.*;
import static com.ibm.domino.services.rest.RestParameterConstants.*;
import static com.ibm.domino.services.rest.RestServiceConstants.*;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URI;
import java.util.Date;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
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.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import lotus.domino.Database;
import lotus.domino.DateTime;
import lotus.domino.Document;
import lotus.domino.NotesException;
import org.apache.http.impl.cookie.DateParseException;
import com.ibm.commons.util.StringUtil;
import com.ibm.commons.util.io.json.JsonJavaFactory;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.commons.util.io.json.JsonParser;
import com.ibm.domino.commons.util.UriHelper;
import com.ibm.domino.das.service.DataService;
import com.ibm.domino.das.utils.ErrorHelper;
import com.ibm.domino.httpmethod.PATCH;
import com.ibm.domino.services.ServiceException;
import com.ibm.domino.services.content.JsonDocumentContent;
import com.ibm.domino.services.rest.das.document.DocumentParameters;
import com.ibm.domino.services.util.JsonWriter;
@Path(PARAM_DATA + PARAM_SEPERATOR + PARAM_DOCUMENTS + PARAM_SEPERATOR + PARAM_UNID + PARAM_SEPERATOR + UNID_RESOURCE_PATH)
public class DocumentResource extends AbstractDasResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getDocumentByUnid(@Context final UriInfo uriInfo,
@PathParam(PARAM_UNID) final String unid,
@HeaderParam(HEADER_IF_MODIFIED_SINCE) final String ifModifiedSince,
@QueryParam(PARAM_COMPACT) final boolean compact,
@QueryParam(PARAM_DOC_STRONGTYPE)final boolean strongType,
@QueryParam(PARAM_DOC_HIDDEN)final boolean hidden,
@QueryParam(PARAM_DOC_MARKREAD) final String markRead,
@QueryParam(PARAM_DOC_MULTIPART) final String multipart,
@QueryParam(PARAM_DOC_LOWERCASEFIELDS) final boolean lowercaseItems,
@QueryParam(PARAM_DOC_FIELDS) final String items){
DAS_LOGGER.traceEntry(this, "getDocumentByUnid"); // $NON-NLS-1$
DataService.beforeRequest(FEATURE_REST_API_DATA_DOCUMENT, STAT_DOCUMENT);
final ResponseBuilder builder = Response.ok();
abstract class StreamingOutputImpl implements StreamingOutput {
Response response = null;
public void setResponse(Response response) {
this.response = response;
}
}
StreamingOutputImpl streamJsonEntity = new StreamingOutputImpl() {
// @Override
public void write(OutputStream outputStream) throws IOException {
Document document = null;
try {
Database database = getDatabase(DB_ACCESS_VIEWS_DOCS);
try {
document = database.getDocumentByUNID(unid);
}
catch (NotesException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.NOT_FOUND));
}
URI baseUri = UriHelper.copy(uriInfo.getAbsolutePath(),DataService.isUseRelativeUrls());
//Unid don't contain special characters, so don't need encode
baseUri = UriHelper.trimAtLast(baseUri, PARAM_SEPERATOR + unid);
OutputStreamWriter streamWriter = new OutputStreamWriter(outputStream);
String lastModifiedHeader = ifModifiedSince(document, ifModifiedSince);
if (lastModifiedHeader != null) {
response.getMetadata().add(HEADER_LAST_MODIFIED, lastModifiedHeader);
}
JsonWriter jsonWriter = new JsonWriter(streamWriter, compact);
JsonDocumentContent content = new JsonDocumentContent(document);
int sysItems = DocumentParameters.SYS_ITEM_ALL;
String rtType = TYPE_MULTIPART;
// Handle parameters
if (!hidden) {
sysItems &= ~DocumentParameters.SYS_ITEM_HIDDEN;
}
if (multipart != null && multipart.compareToIgnoreCase(PARAM_VALUE_FALSE) == 0) {
rtType = TYPE_RICHTEXT; // Deprecated
}
List<String> defItemFilter = null;
if ( StringUtil.isNotEmpty(items) ) {
defItemFilter = DataService.getParameterStringList(PARAM_DOC_FIELDS, items);
}
try {
content.writeDocumentAsJson(jsonWriter, sysItems, true,
defItemFilter, lowercaseItems, null,
strongType, rtType, baseUri.toString());
}
finally {
jsonWriter.flush();
}
// Handle parameters.
if (markRead == null || markRead.compareToIgnoreCase(PARAM_VALUE_TRUE) == 0) {
document.markRead();
}
else if (markRead.compareToIgnoreCase(PARAM_VALUE_FALSE) != 0) {
throw new WebApplicationException(ErrorHelper.createErrorResponse("Invalid parameter.", Response.Status.BAD_REQUEST)); // $NLX-DocumentResource.Invalidparameter-1$
}
streamWriter.close();
}
catch (NotesException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.BAD_REQUEST));
}
catch (ServiceException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.BAD_REQUEST));
}
finally {
if (document != null) {
try {
document.recycle();
} catch (NotesException e) {
// Ignore
}
}
}
}
};
builder.type(MediaType.APPLICATION_JSON_TYPE).entity(streamJsonEntity);
Response response = builder.build();
streamJsonEntity.setResponse(response);
DAS_LOGGER.traceExit(this, "getDocumentByUnid", response); // $NON-NLS-1$
return response;
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response putDocumentByUnid(String requestEntity,
@HeaderParam(HEADER_IF_UNMODIFIED_SINCE) final String ifUnmodifiedSince,
@PathParam(PARAM_UNID) String unid,
@QueryParam(PARAM_DOC_FORM) String form,
@QueryParam(PARAM_DOC_COMPUTEWITHFORM) String computeWithForm) {
DAS_LOGGER.traceEntry(this, "putDocumentByUnid"); // $NON-NLS-1$
DataService.beforeRequest(FEATURE_REST_API_DATA_DOCUMENT, STAT_DOCUMENT);
Response response = updateDocumentByUnid(requestEntity, ifUnmodifiedSince, unid, form, computeWithForm, true);
DAS_LOGGER.traceExit(this, "putDocumentByUnid", response); // $NON-NLS-1$
return response;
}
@PATCH
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response patchDocumentByUnid(String requestEntity,
@HeaderParam(HEADER_IF_UNMODIFIED_SINCE) final String ifUnmodifiedSince,
@PathParam(PARAM_UNID) String unid,
@QueryParam(PARAM_DOC_FORM) String form,
@QueryParam(PARAM_DOC_COMPUTEWITHFORM) String computeWithForm) {
DAS_LOGGER.traceEntry(this, "patchDocumentByUnid"); // $NON-NLS-1$
DataService.beforeRequest(FEATURE_REST_API_DATA_DOCUMENT, STAT_DOCUMENT);
Response response = updateDocumentByUnid(requestEntity, ifUnmodifiedSince, unid, form, computeWithForm, false);
DAS_LOGGER.traceExit(this, "patchDocumentByUnid", response); // $NON-NLS-1$
return response;
}
@DELETE
public Response deleteDocumentByUnid(
@HeaderParam(HEADER_IF_UNMODIFIED_SINCE) String ifUnmodifiedSince,
@PathParam(PARAM_UNID) String unid) {
DAS_LOGGER.traceEntry(this, "deleteDocumentByUnid"); // $NON-NLS-1$
DataService.beforeRequest(FEATURE_REST_API_DATA_DOCUMENT, STAT_DOCUMENT);
// String jsonEntity = null;
try {
Database database = this.getDatabase(DB_ACCESS_VIEWS_DOCS);
Document document = null;
try {
document = database.getDocumentByUNID(unid);
}
catch (NotesException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.NOT_FOUND));
}
ifUnmodifiedSince(document, ifUnmodifiedSince);
if(!document.remove(true)) {
throw new WebApplicationException(ErrorHelper.createErrorResponse("Document is not deleted because another user modified it.", Response.Status.CONFLICT)); // $NLX-DocumentResource.Documentisnotdeletedbecauseanothe-1$
}
}
catch (NotesException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.BAD_REQUEST));
}
ResponseBuilder builder = Response.ok();
// builder.type(MediaType.APPLICATION_JSON_TYPE).entity(jsonEntity);
Response response = builder.build();
DAS_LOGGER.traceExit(this, "deleteDocumentByUnid", response); // $NON-NLS-1$
return response;
}
public Response updateDocumentByUnid(String requestEntity,
@HeaderParam(HEADER_IF_UNMODIFIED_SINCE) final String ifUnmodifiedSince,
@PathParam(PARAM_UNID) String unid,
@QueryParam(PARAM_DOC_FORM) String form,
@QueryParam(PARAM_DOC_COMPUTEWITHFORM) String computeWithForm,
boolean put) {
String jsonEntity = null;
Document document = null;
try {
// Write JSON content
Database database = this.getDatabase(DB_ACCESS_VIEWS_DOCS);
try {
document = database.getDocumentByUNID(unid);
}
catch (NotesException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.NOT_FOUND));
}
ifUnmodifiedSince(document, ifUnmodifiedSince);
JsonDocumentContent content = new JsonDocumentContent(document);
JsonJavaObject jsonItems;
JsonJavaFactory factory = JsonJavaFactory.instanceEx;
try {
StringReader reader = new StringReader(requestEntity);
try {
jsonItems = (JsonJavaObject) JsonParser.fromJson(factory, reader);
}
finally {
reader.close();
}
}
catch (Exception ex) {
throw new ServiceException(ex, "Error while parsing the JSON content"); // $NLX-DocumentResource.ErrorwhileparsingtheJSONcontent-1$
}
// Handle parameters.
if (StringUtil.isNotEmpty(form)) {
document.replaceItemValue(ITEM_FORM, form);
}
content.updateFields(jsonItems, put);
if (computeWithForm != null && computeWithForm.compareToIgnoreCase(PARAM_VALUE_TRUE) == 0) {
document.computeWithForm(true, true);
}
document.save();
}
catch (NotesException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.BAD_REQUEST));
}
catch (ServiceException e) {
throw new WebApplicationException(ErrorHelper.createErrorResponse(e, Response.Status.BAD_REQUEST));
}
finally {
if (document != null) {
try {
document.recycle();
} catch (NotesException e) {
// Ignore
}
}
}
ResponseBuilder builder = Response.ok();
builder.type(MediaType.APPLICATION_JSON_TYPE).entity(jsonEntity);
Response response = builder.build();
return response;
}
static private void ifUnmodifiedSince(Document document,
final String ifUnmodifiedSince) throws NotesException {
DateTime lastModifiedDateTime = document.getLastModified();
if (lastModifiedDateTime != null) {
Date lastModifiedDate = lastModifiedDateTime.toJavaDate();
if (lastModifiedDate != null) {
// Formats the given date according to the RFC 1123 pattern.
String lastModifiedHeader = org.apache.http.impl.cookie.DateUtils.formatDate(lastModifiedDate);
if (lastModifiedHeader != null) {
if (ifUnmodifiedSince != null) {
if (!ifUnmodifiedSince.equalsIgnoreCase(lastModifiedHeader)) {
try {
Date ifUnmodifiedSinceDate = org.apache.http.impl.cookie.DateUtils.parseDate(ifUnmodifiedSince);
if (lastModifiedDate.after(ifUnmodifiedSinceDate) ) {
throw new WebApplicationException(Response.Status.PRECONDITION_FAILED);
}
}
catch (DateParseException e) {
throw new WebApplicationException(Response.Status.PRECONDITION_FAILED);
}
}
}
}
}
}
}
static private String ifModifiedSince(Document document, final String ifModifiedSince)
throws NotesException {
String lastModifiedHeader = null;
DateTime lastModifiedDateTime = document.getLastModified();
if (lastModifiedDateTime != null) {
Date lastModifiedDate = lastModifiedDateTime.toJavaDate();
if (lastModifiedDate != null) {
// Formats the given date according to the RFC 1123 pattern.
lastModifiedHeader = org.apache.http.impl.cookie.DateUtils.formatDate(lastModifiedDate);
if (lastModifiedHeader != null) {
if (ifModifiedSince != null) {
if (ifModifiedSince.equalsIgnoreCase(lastModifiedHeader)) {
throw new WebApplicationException(Response.Status.NOT_MODIFIED);
}
try {
Date ifModifiedSinceDate = org.apache.http.impl.cookie.DateUtils.parseDate(ifModifiedSince);
if (ifModifiedSinceDate.equals(lastModifiedDate) || ifModifiedSinceDate.after(lastModifiedDate)) {
throw new WebApplicationException(Response.Status.NOT_MODIFIED);
}
}
catch (DateParseException e) {
// If we can not parse the If-Modified-Since then continue.
DAS_LOGGER.info("Can not parse the If-Modified-Since header."); // $NON-NLS-1$
}
}
}
}
}
return lastModifiedHeader;
}
}