Java tutorial
/* * This work was created by participants in the DataONE project, and is * jointly copyrighted by participating institutions in DataONE. For * more information on DataONE, see our web site at http://dataone.org. * * Copyright 2014 * * 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 org.dataone.proto.trove.mn.rest.v1; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.net.URLCodec; import org.apache.log4j.Logger; import org.dataone.mimemultipart.MultipartRequestResolver; import org.dataone.proto.trove.mn.rest.base.AbstractWebController; import org.dataone.proto.trove.mn.service.v1.SolrIndex; import org.dataone.service.exceptions.IdentifierNotUnique; import org.dataone.service.exceptions.InsufficientResources; import org.dataone.service.exceptions.InvalidRequest; import org.dataone.service.exceptions.InvalidSystemMetadata; import org.dataone.service.exceptions.InvalidToken; import org.dataone.service.exceptions.NotAuthorized; import org.dataone.service.exceptions.NotFound; import org.dataone.service.exceptions.NotImplemented; import org.dataone.service.exceptions.ServiceFailure; import org.dataone.service.exceptions.UnsupportedType; import org.dataone.service.mn.tier1.v1.MNRead; import org.dataone.service.mn.tier3.v1.MNStorage; import org.dataone.service.types.v1.DescribeResponse; import org.dataone.service.types.v1.Event; import org.dataone.service.types.v1.Identifier; import org.dataone.service.types.v1.LogEntry; import org.dataone.service.types.v1.ObjectFormatIdentifier; import org.dataone.service.types.v1.ObjectList; import org.dataone.service.types.v1.Session; import org.dataone.service.types.v1.SystemMetadata; import org.dataone.service.util.TypeMarshaller; import org.jibx.runtime.JiBXException; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.servlet.ModelAndView; /** * @author waltz * */ @Controller public class ObjectController extends AbstractWebController { @Autowired @Qualifier("mnStorageService") MNStorage mnStorage; @Autowired @Qualifier("mnReadService") MNRead mnRead; MultipartRequestResolver multipartRequestResolver = new MultipartRequestResolver("/tmp/trove", 1000000000, 0); static final int SMALL_BUFF_SIZE = 25000; static final int MED_BUFF_SIZE = 50000; static final int LARGE_BUFF_SIZE = 100000; Logger logger = Logger.getLogger(ObjectController.class.getName()); private URLCodec urlCodec = new URLCodec(); @Autowired @Qualifier("d1LoggingService") SolrIndex d1Log; /** * return the object requested * * @param request * @param response * @param pid * @throws InvalidToken * @throws ServiceFailure * @throws IOException * @throws NotAuthorized * @throws NotFound * @throws NotImplemented */ @RequestMapping(value = "/v1/object/{pid}", method = RequestMethod.GET) public void get(HttpServletRequest request, HttpServletResponse response, @PathVariable String pid) throws InvalidToken, ServiceFailure, IOException, NotAuthorized, NotFound, NotImplemented, InvalidRequest, InsufficientResources { debugRequest(request); Identifier id = new Identifier(); try { id.setValue(urlCodec.decode(pid, "UTF-8")); } catch (DecoderException ex) { throw new ServiceFailure("20000", ex.getMessage()); } catch (UnsupportedEncodingException ex) { throw new ServiceFailure("20001", ex.getMessage()); } if (!logRequest(request, Event.READ, id)) { throw new ServiceFailure("20001", "unable to log request"); } Session session = new Session(); if (!this.logRequest(request, Event.READ, id)) { throw new ServiceFailure("20001", "unable to log request"); } InputStream in = mnRead.get(id); OutputStream out = response.getOutputStream(); try { byte[] buffer = null; int filesize = in.available(); if (filesize < 250000) { buffer = new byte[SMALL_BUFF_SIZE]; } else if (filesize >= 250000 && filesize <= 500000) { buffer = new byte[MED_BUFF_SIZE]; } else { buffer = new byte[LARGE_BUFF_SIZE]; } while (true) { int amountRead = in.read(buffer); if (amountRead == -1) { break; } out.write(buffer, 0, amountRead); out.flush(); } } finally { if (in != null) { in.close(); } if (out != null) { out.flush(); out.close(); } } } /** * Delete the object requested, and return the pid of object deleted * * @param request * @param response * @param pid * @throws InvalidToken * @throws ServiceFailure * @throws IOException * @throws NotAuthorized * @throws NotFound * @throws NotImplemented */ @RequestMapping(value = "/v1/object/{pid}", method = RequestMethod.DELETE) public ModelAndView delete(HttpServletRequest request, HttpServletResponse response, @PathVariable String pid) throws InvalidToken, ServiceFailure, IOException, NotAuthorized, NotFound, NotImplemented, InvalidRequest { debugRequest(request); Identifier id = new Identifier(); try { id.setValue(urlCodec.decode(pid, "UTF-8")); } catch (DecoderException ex) { throw new ServiceFailure("20000", ex.getMessage()); } catch (UnsupportedEncodingException ex) { throw new ServiceFailure("20001", ex.getMessage()); } Session session = new Session(); if (!this.logRequest(request, Event.DELETE, id)) { throw new ServiceFailure("20001", "unable to log request"); } Identifier deletedPid = mnStorage.delete(session, id); return new ModelAndView("xmlIdentifierViewResolver", "org.dataone.service.types.v1.Identifier", deletedPid); } /** * Describe the object requested, this is no body returned, only HTTP header info * * @param request * @param response * @param pid * @throws InvalidToken * @throws ServiceFailure * @throws IOException * @throws NotAuthorized * @throws NotFound * @throws NotImplemented */ @RequestMapping(value = "/v1/object/{pid}", method = RequestMethod.HEAD) public void describe(HttpServletRequest request, HttpServletResponse response, @PathVariable String pid) throws InvalidToken, ServiceFailure, IOException, NotAuthorized, NotFound, NotImplemented, InvalidRequest { debugRequest(request); Identifier id = new Identifier(); try { id.setValue(urlCodec.decode(pid, "UTF-8")); } catch (DecoderException ex) { throw new ServiceFailure("20000", ex.getMessage()); } catch (UnsupportedEncodingException ex) { throw new ServiceFailure("20001", ex.getMessage()); } Session session = new Session(); DescribeResponse describe = mnRead.describe(session, id); response.addHeader("Content-Length", describe.getContent_Length().toString()); /* FROM http://www.ietf.org/rfc/rfc2822.txt * Internet Message Format * 3.3. Date and Time Specification ..............................14 * * Date and time occur in several header fields. This section specifies * the syntax for a full date and time specification. Though folding * white space is permitted throughout the date-time specification, it * is RECOMMENDED that a single space be used in each place that FWS * appears (whether it is required or optional); some older * implementations may not interpret other occurrences of folding white * space correctly. * * date-time = [ day-of-week "," ] date FWS time [CFWS] * * day-of-week Logger d1Log = DataONELog.getLogger(MNReadImpl.class.getName()); = ([FWS] day-name) / obs-day-of-week * * day-name = "Mon" / "Tue" / "Wed" / "Thu" / * "Fri" / "Sat" / "Sun" * * date = day month year * * year = 4*DIGIT / obs-year * * month = (FWS month-name FWS) / obs-month * * month-name = "Jan" / "Feb" / "Mar" / "Apr" / * "May" / "Jun" / "Jul" / "Aug" / * "Sep" / "Oct" / "Nov" / "Dec" * * day = ([FWS] 1*2DIGIT) / obs-day * * time = time-of-day FWS zone * * time-of-day = hour ":" minute [ ":" second ] * * hour = 2DIGIT / obs-hour * * minute = 2DIGIT / obs-minute * * second = 2DIGIT / obs-second * * zone = (( "+" / "-" ) 4DIGIT) / obs-zone * */ DateTime lastModifiedDate = new DateTime(describe.getLast_Modified(), DateTimeZone.UTC); response.addHeader("Last-Modified", lastModifiedDate.toString()); response.addHeader("DataONE-ObjectFormat", describe.getDataONE_ObjectFormatIdentifier().getValue()); response.addHeader("DataONE-Checksum", describe.getDataONE_Checksum().getAlgorithm() + "," + describe.getDataONE_Checksum().getValue()); OutputStream out = response.getOutputStream(); response.flushBuffer(); out.close(); } /** * */ @RequestMapping(value = { "/v1/object", "/v1/object/" }, method = RequestMethod.GET) public ModelAndView search(HttpServletRequest request, HttpServletResponse response) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken { debugRequest(request); Session cert = new Session(); Integer start = 0; Integer count = 1000; ObjectFormatIdentifier format = null; String startTime = null; String endTime = null; Boolean replica = null; DateTime startDate = null; DateTime endDate = null; if (request.getParameterMap().keySet().contains("start")) { String[] values = (String[]) request.getParameterMap().get("start"); start = Integer.parseInt(values[0]); } if (request.getParameterMap().keySet().contains("count")) { String[] values = (String[]) request.getParameterMap().get("count"); count = Integer.parseInt(values[0]); } if (request.getParameterMap().keySet().contains("replicaStatus")) { String[] values = (String[]) request.getParameterMap().get("replicaStatus"); replica = Boolean.getBoolean(values[0]); } if (request.getParameterMap().keySet().contains("format")) { String[] values = (String[]) request.getParameterMap().get("format"); format = new ObjectFormatIdentifier(); format.setValue(values[0]); } if (request.getParameterMap().keySet().contains("startTime")) { String[] values = (String[]) request.getParameterMap().get("startTime"); startTime = values[0]; } if (request.getParameterMap().keySet().contains("endTime")) { String[] values = (String[]) request.getParameterMap().get("endTime"); endTime = values[0]; } LogEntry logEntry = new LogEntry(); d1Log.add(logEntry); //d1Log.put("search", "start = " + start + " count = " + count + " format = " + (format == null ? "null" : format.getValue()) + " startTime = " + startTime + " endTime = " + endTime); logger.info("start = " + start + " count = " + count + " format = " + (format == null ? "null" : format.getValue()) + " startTime = " + startTime + " endTime = " + endTime); if (!((startTime == null) && (endTime == null))) { if (startTime == null) { startTime = "1900-01-01T00:00:00Z"; } if (endTime == null) { endTime = new DateTime().toString(); } startDate = new DateTime(startTime, DateTimeZone.UTC); endDate = new DateTime(endTime, DateTimeZone.UTC); } ObjectList objectList = mnRead.listObjects(cert, (startDate == null ? null : startDate.toDate()), (endDate == null ? null : endDate.toDate()), format, replica, start, count); return new ModelAndView("xmlObjectListViewResolver", "org.dataone.service.types.v1.ObjectList", objectList); } /** * */ @RequestMapping(value = "/v1/object/{pid}", method = RequestMethod.POST) public ModelAndView create(MultipartHttpServletRequest fileRequest, HttpServletResponse response) throws InvalidSystemMetadata, InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, NotImplemented, InvalidRequest { Session session = new Session(); Identifier identifier = new Identifier(); MultipartFile sytemMetaDataMultipart = null; MultipartFile objectMultipart = null; SystemMetadata systemMetadata = null; Set<String> keys = fileRequest.getFileMap().keySet(); for (String key : keys) { if (key.equalsIgnoreCase("sysmeta")) { sytemMetaDataMultipart = fileRequest.getFileMap().get(key); } else { objectMultipart = fileRequest.getFileMap().get(key); } } if (sytemMetaDataMultipart != null) { try { systemMetadata = (SystemMetadata) TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sytemMetaDataMultipart.getInputStream()); } catch (IOException ex) { throw new InvalidSystemMetadata("15001", ex.getMessage()); } catch (JiBXException ex) { throw new InvalidSystemMetadata("15002", ex.getMessage()); } catch (InstantiationException ex) { throw new InvalidSystemMetadata("15003", ex.getMessage()); } catch (IllegalAccessException ex) { throw new InvalidSystemMetadata("15004", ex.getMessage()); } } else { throw new InvalidSystemMetadata("15005", "System Metadata was not found as Part of Multipart Mime message"); } identifier.setValue(systemMetadata.getIdentifier().getValue()); InputStream objectInputStream = null; if (objectMultipart != null && !(objectMultipart.isEmpty())) { try { objectInputStream = objectMultipart.getInputStream(); } catch (IOException ex) { throw new InvalidRequest("15006", ex.getMessage()); } } else { throw new InvalidRequest("15007", "Object to be created is not attached"); } DateTime dt = new DateTime(); systemMetadata.setDateUploaded(dt.toDate()); systemMetadata.setDateSysMetadataModified(dt.toDate()); if (!this.logRequest(fileRequest, Event.CREATE, identifier)) { throw new ServiceFailure("20001", "unable to log request"); } identifier = mnStorage.create(session, identifier, objectInputStream, systemMetadata); return new ModelAndView("xmlIdentifierViewResolver", "org.dataone.service.types.v1.Identifier", identifier); } /** * update the object requested * * @param request * @param response * @param pid * @throws InvalidToken * @throws ServiceFailure * @throws IOException * @throws NotAuthorized * @throws NotFound * @throws NotImplemented */ @RequestMapping(value = "/v1/object/{pid}", method = RequestMethod.PUT) public void update(MultipartHttpServletRequest fileRequest, HttpServletResponse response, @PathVariable String pid) throws InvalidToken, ServiceFailure, IOException, NotAuthorized, NotFound, NotImplemented, InvalidRequest, InsufficientResources, InvalidSystemMetadata, IdentifierNotUnique, UnsupportedType { debugRequest(fileRequest); Identifier id = new Identifier(); try { id.setValue(urlCodec.decode(pid, "UTF-8")); } catch (DecoderException ex) { throw new ServiceFailure("20000", ex.getMessage()); } catch (UnsupportedEncodingException ex) { throw new ServiceFailure("20001", ex.getMessage()); } if (!this.logRequest(fileRequest, Event.UPDATE, id)) { throw new ServiceFailure("20001", "unable to log request"); } Session session = new Session(); InputStream objectInputStream = null; MultipartFile sytemMetaDataMultipart = null; MultipartFile objectMultipart = null; SystemMetadata systemMetadata = null; Set<String> keys = fileRequest.getFileMap().keySet(); for (String key : keys) { if (key.equalsIgnoreCase("sysmeta")) { sytemMetaDataMultipart = fileRequest.getFileMap().get(key); } else { objectMultipart = fileRequest.getFileMap().get(key); } } if (sytemMetaDataMultipart != null) { try { systemMetadata = (SystemMetadata) TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sytemMetaDataMultipart.getInputStream()); } catch (IOException ex) { throw new InvalidSystemMetadata("15001", ex.getMessage()); } catch (JiBXException ex) { throw new InvalidSystemMetadata("15002", ex.getMessage()); } catch (InstantiationException ex) { throw new InvalidSystemMetadata("15003", ex.getMessage()); } catch (IllegalAccessException ex) { throw new InvalidSystemMetadata("15004", ex.getMessage()); } } else { throw new InvalidSystemMetadata("15005", "System Metadata was not found as Part of Multipart Mime message"); } if (objectMultipart != null && !(objectMultipart.isEmpty())) { try { objectInputStream = objectMultipart.getInputStream(); } catch (IOException ex) { throw new InvalidRequest("15006", ex.getMessage()); } } else { throw new InvalidRequest("15007", "Object to be created is not attached"); } id.setValue(systemMetadata.getIdentifier().getValue()); DateTime dt = new DateTime(); systemMetadata.setDateSysMetadataModified(dt.toDate()); mnStorage.update(id, null, id, null); InputStream in = mnRead.get(id); OutputStream out = response.getOutputStream(); try { byte[] buffer = null; int filesize = in.available(); if (filesize < 250000) { buffer = new byte[SMALL_BUFF_SIZE]; } else if (filesize >= 250000 && filesize <= 500000) { buffer = new byte[MED_BUFF_SIZE]; } else { buffer = new byte[LARGE_BUFF_SIZE]; } while (true) { int amountRead = in.read(buffer); if (amountRead == -1) { break; } out.write(buffer, 0, amountRead); out.flush(); } } finally { if (in != null) { in.close(); } if (out != null) { out.flush(); out.close(); } } } }