package no.met.metadataeditor.service; import com.sun.jersey.api.Responses; import java.io.IOException; import java.io.StringReader; import java.util.List; import java.util.Objects; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.ApplicationPath; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Application; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import no.met.metadataeditor.LogUtils; import no.met.metadataeditor.datastore.DataStore; import no.met.metadataeditor.datastore.DataStoreFactory; import no.met.metadataeditor.datastore.MetadataRecords; import no.met.metadataeditor.datastore.MetadataRecords.ResourceMetadata; import no.met.metadataeditor.validation.ValidatorException; import no.met.metadataeditor.validationclient.ValidationClient; import no.met.metadataeditor.validationclient.ValidationResponse; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * REST API used to communicate with the metadata editor from other systems. */ @ApplicationPath("service") @Path("/metaedit_api/") public class RESTApi extends Application { @Context HttpServletRequest request; @GET @Path("") @Produces("application/xml") @ServiceDescription("Return the list of services with parameters.") public Response capabilities() throws ParserConfigurationException{ Document d = ServiceDescriptionGenerator.getXMLServiceDescription(this.getClass()); return Response.ok(d).build(); } /** * Get the XML for a metadata record. * @param project The project to read from. * @param record The record to read. * @return The XML for a metadata record. Returns a HTTP 404 if the record does not exist. */ @GET @Path("{project}/{record}") @ServiceDescription("Get the XML for the metadata record.") public Response getMetadata(@PathParam("project") String project, @PathParam("record") String record ){ DataStore datastore = DataStoreFactory.getInstance(project); if(!datastore.metadataExists(record)){ return Responses.notFound().build(); } String metadata = datastore.readMetadata(record); return Response.ok(metadata, MediaType.TEXT_XML).build(); } /** * Get the URL to edit a metadata record, and as an option suggest a new * version of the XML record. If new metadata is included in the request and * is different from the current version, the metadata is stored in a * temporary file. Human interaction is then required to make it the * new version. * * Posting metadata to a record that does not exist causes the record to be * created. * * @param project * The project the metadata record is in. * @param record * The identifier for the record. * @param metadata * (optional) The suggested new metadata for the record. * @return Either an url to the metadata editor or a metadata comparison editor for selecting the correct version. */ @POST @Path("{project}/{record}") @ServiceDescription("Get the URL to edit a metadata record and as an option suggest a new version of the XML in the metadata parameter") public Response postMetadata(@PathParam("project") String project, @PathParam("record") String record, String metadata) throws ValidatorException { DataStore datastore = DataStoreFactory.getInstance(project); if( metadata != null && !("".equals(metadata.trim()))){ ValidationClient validationClient = datastore.getValidationClient(metadata); if (validationClient != null) { ValidationResponse validationResponse = validationClient.validate(metadata); if (!validationResponse.success) { throw new ValidatorException(new SAXException(validationResponse.message)); } } } boolean metadataExists = datastore.metadataExists(record); Response response; if(!metadataExists && (metadata == null || "".equals(metadata.trim()) )){ response = Responses.notFound().build(); } else if ( !metadataExists && metadata != null ){ datastore.writeMetadata(record, metadata, datastore.getDefaultUser(), datastore.getDefaultPassword()); response = Response.ok(getEditorUrl(project, record)).build(); } else if( metadataExists && (metadata == null || "".equals(metadata.trim()) )){ response = Response.ok(getEditorUrl(project, record)).build(); } else { if( metadataEqual(metadata, datastore.readMetadata(record))) { response = Response.ok(getEditorUrl(project, record)).build(); } else { datastore.writeMetadata(record + DataStore.THEIRS_IDENTIFIER, metadata, datastore.getDefaultUser(), datastore.getDefaultPassword()); response = Response.ok(getCompareUrl(project, record)).build(); } } return response; } /** * Posting metadata will cause the record to be created/updated and stored in repository @param project * The project the metadata record is in. * @param record * The identifier for the record. * @param metadata * The new metadata for the record. * @return HTTP 200 code if either the record created or updated else HTTP 404 * @throws ValidatorException */ @POST @Path("noedit/{project}/{record}") @ServiceDescription("Post metadata to repository without editing") public Response postMetadataNoEdit(@PathParam("project") String project, @PathParam("record") String record, String metadata) throws ValidatorException { Objects.requireNonNull(project, "Project can not be null"); Objects.requireNonNull(record, "Missing record id"); Objects.requireNonNull(metadata, "Missing metadata"); DataStore datastore = DataStoreFactory.getInstance(project); if( metadata != null && !("".equals(metadata.trim()))){ ValidationClient validationClient = datastore.getValidationClient(metadata); if (validationClient != null) { ValidationResponse validationResponse = validationClient.validate(metadata); if (!validationResponse.success) { throw new ValidatorException(new SAXException(validationResponse.message)); } } } if (metadata != null && !"".equals(metadata.trim())){ datastore.writeMetadata(record, metadata, datastore.getDefaultUser(), datastore.getDefaultPassword()); return Response.ok().build(); } return Responses.notFound().build(); } @GET @Path("list/{project}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public MetadataRecords getRecords(@PathParam("project") String project) throws ValidatorException { Objects.requireNonNull(project, "Missing project name"); MetadataRecords<MetadataRecords.ResourceMetadata> metadataRecords = new MetadataRecords(); DataStore datastore = DataStoreFactory.getInstance(project); List<ResourceMetadata> properties = datastore.listMetadataRecord(); metadataRecords.setRecords(properties); return metadataRecords; } private String getEditorUrl(String project, String record){ return getBaseUrl(project, record) + "editor.xhtml?project=" + project + "&record=" + record; } private String getCompareUrl(String project, String record) { return getBaseUrl(project, record) + "compare.xhtml?project=" + project + "&record=" + record; } private String getBaseUrl(String project, String record){ String url = request.getScheme() + "://" + request.getServerName(); if(request.getServerPort() != 80){ url += ":" + request.getServerPort(); } return url + request.getContextPath() + "/"; } private boolean metadataEqual(String metadata1, String metadata2){ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setCoalescing(true); dbf.setIgnoringElementContentWhitespace(true); dbf.setIgnoringComments(true); DocumentBuilder db; try { db = dbf.newDocumentBuilder(); Document doc1 = db.parse(new InputSource(new StringReader(metadata1))); doc1.normalizeDocument(); Document doc2 = db.parse(new InputSource(new StringReader(metadata2))); doc2.normalizeDocument(); return doc1.isEqualNode(doc2); } catch (ParserConfigurationException | SAXException | IOException e) { LogUtils.logException(Logger.getLogger(RESTApi.class.getName()), "Eroor while parsing metadata", e); } return false; } }