/* * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package thredds.server.ncss.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import thredds.server.config.FormatsAvailabilityService; import thredds.server.config.TdsContext; import thredds.server.ncss.dataservice.FeatureDatasetService; import thredds.server.ncss.exception.NcssException; import thredds.server.ncss.exception.UnsupportedOperationException; import thredds.server.ncss.exception.UnsupportedResponseFormatException; import thredds.server.ncss.format.SupportedFormat; import thredds.server.ncss.format.SupportedOperation; import thredds.server.ncss.params.NcssParamsBean; import thredds.server.ncss.view.dsg.DsgSubsetWriterFactory; import thredds.servlet.ServletUtil; import thredds.util.Constants; import thredds.util.ContentType; import ucar.ma2.InvalidRangeException; import ucar.nc2.NetcdfFileWriter; import ucar.nc2.constants.FeatureType; import ucar.nc2.dt.grid.GridDataset; import ucar.nc2.ft.FeatureDataset; import ucar.nc2.ft.FeatureDatasetPoint; import ucar.nc2.util.DiskCache2; import ucar.nc2.util.IO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.File; import java.io.IOException; import java.text.ParseException; import java.util.Formatter; import java.util.Set; /** * Annotated controller for Netcdf Subset Service * * @author jcaron * @author mhermida */ @Controller @RequestMapping("/ncss") public class NcssController extends AbstractNcssController { //static private final Logger log = LoggerFactory.getLogger(NcssController.class); @Autowired FeatureDatasetService datasetService; @Autowired TdsContext tdsContext; /* @RequestMapping("/ncss/grid/**") public String forwardGrid(HttpServletRequest req) { String reqString = req.getServletPath(); assert reqString.startsWith("/ncss/grid"); reqString = reqString.substring(10); String forwardString = "forward:/ncss" + reqString; // strip off '?/grid if (null != req.getQueryString()) forwardString += "?"+req.getQueryString(); return forwardString; } */ /** * Handles ncss data requests. * Dont know what responder to use until we can open the dataset. * * @param req request * @param res result * @throws IOException * @throws UnsupportedResponseFormatException * @throws InvalidRangeException * @throws ParseException */ @RequestMapping("**") public void handleRequest(HttpServletRequest req, HttpServletResponse res, @Valid NcssParamsBean params, BindingResult validationResult) throws Exception { // System.out.printf("%s%n", ServletUtil.showRequestDetail(null, req)); if (validationResult.hasErrors()) { handleValidationErrorsResponse(res, HttpServletResponse.SC_BAD_REQUEST, validationResult); return; } String datasetPath = getDatasetPath(req); try (FeatureDataset fd = datasetService.findDatasetByPath(req, res, datasetPath)) { if (fd == null) { //handleValidationErrorMessage(res, HttpServletResponse.SC_NOT_FOUND, // "dataset path not found " + datasetPath); return; } Formatter errs = new Formatter(); if (!params.intersectsTime(fd, errs)) { handleValidationErrorMessage(res, HttpServletResponse.SC_BAD_REQUEST, errs.toString()); return; } FeatureType ft = fd.getFeatureType(); if (ft == FeatureType.GRID) { if (!params.hasLatLonPoint()) { handleRequestGrid(res, params, datasetPath, (GridDataset) fd); } else { handleRequestGridAsPoint(res, params, datasetPath, fd); } } else if (ft == FeatureType.POINT) { handleRequestDsg(res, params, datasetPath, fd); } else if (ft == FeatureType.STATION) { handleRequestDsg(res, params, datasetPath, fd); } else { throw new UnsupportedOperationException("Feature Type " + ft.toString() + " not supported"); } } } void handleRequestGrid(HttpServletResponse res, NcssParamsBean params, String datasetPath, GridDataset gridDataset) throws IOException, NcssException, ParseException, InvalidRangeException { //params.isValidGridRequest(); ??? // Supported formats are netcdf3 (default) and netcdf4 (if available) SupportedFormat sf = SupportedOperation.GRID_REQUEST.getSupportedFormat(params.getAccept()); NetcdfFileWriter.Version version = NetcdfFileWriter.Version.netcdf3; if (sf.equals(SupportedFormat.NETCDF4)) { if (FormatsAvailabilityService.isFormatAvailable(SupportedFormat.NETCDF4)) { version = NetcdfFileWriter.Version.netcdf4; } else { handleValidationErrorMessage(res, HttpServletResponse.SC_BAD_REQUEST, "NetCDF-4 format not available"); return; } } //File netcdfResult = null; //try { GridResponder gds = GridResponder.factory(gridDataset, datasetPath); File netcdfResult = gds.getResponseFile(res, params, version); //} catch (Exception e) { // handleValidationErrorMessage(res, HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); // return; //} // filename download attachment String suffix = (version == NetcdfFileWriter.Version.netcdf4) ? ".nc4" : ".nc"; int pos = datasetPath.lastIndexOf("/"); String filename = (pos >= 0) ? datasetPath.substring(pos + 1) : datasetPath; if (!filename.endsWith(suffix)) { filename += suffix; } // Headers... HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set(ContentType.HEADER, sf.getResponseContentType()); httpHeaders.set(Constants.Content_Disposition, Constants.setContentDispositionValue(filename)); setResponseHeaders(res, httpHeaders); IO.copyFileB(netcdfResult, res.getOutputStream(), 60000); res.flushBuffer(); res.getOutputStream().close(); res.setStatus(HttpServletResponse.SC_OK); } void handleRequestGridAsPoint(HttpServletResponse res, NcssParamsBean params, String datasetPath, FeatureDataset fd) throws Exception { SupportedFormat format = SupportedOperation.POINT_REQUEST.getSupportedFormat(params.getAccept()); DiskCache2 diskCache = NcssDiskCache.getInstance().getDiskCache(); NcssResponder pds = GridAsPointResponder.factory(diskCache, format, res.getOutputStream()); setResponseHeaders(res, pds.getResponseHeaders(fd, format, datasetPath)); pds.respond(res, fd, datasetPath, params, format); } void handleRequestDsg(HttpServletResponse res, NcssParamsBean params, String datasetPath, FeatureDataset fd) throws Exception { SupportedOperation supportedOp; switch (fd.getFeatureType()) { case POINT: supportedOp = SupportedOperation.POINT_REQUEST; break; case STATION: supportedOp = SupportedOperation.STATION_REQUEST; break; default: throw new UnsupportedOperationException(String.format( "%s format not currently supported for DSG subset writing.", fd.getFeatureType())); } SupportedFormat format = supportedOp.getSupportedFormat(params.getAccept()); DiskCache2 diskCache = NcssDiskCache.getInstance().getDiskCache(); NcssResponder pds = DsgSubsetWriterFactory.newInstance( (FeatureDatasetPoint) fd, params, diskCache, res.getOutputStream(), format); setResponseHeaders(res, pds.getResponseHeaders(fd, format, datasetPath)); pds.respond(res, fd, datasetPath, params, format); } /* @RequestMapping("**") void streamPointData(HttpServletRequest req, HttpServletResponse res, @Valid NcssParamsBean params, BindingResult validationResult) throws IOException, NcssException, ParseException, InvalidRangeException { if (validationResult.hasErrors()) { handleValidationErrorsResponse(res, HttpServletResponse.SC_BAD_REQUEST, validationResult); } else { SupportedFormat format = SupportedOperation.isSupportedFormat(params.getAccept(), SupportedOperation.POINT_REQUEST); String datasetPath = getDatasetPath(req); FeatureDataset fd = null; try { fd = datasetService.findDatasetByPath(req, res, datasetPath); if (fd == null) throw new UnsupportedOperationException("Feature Type not supported"); NCSSPointDataStream pds = NCSSPointDataStreamFactory.getDataStreamer(fd, params, format, res.getOutputStream()); setResponseHeaders(res, pds.getResponseHeaders(fd, format, datasetPath)); pds.pointDataStream(res, fd, datasetPath, params, format); } finally { if (fd != null) fd.close(); } } } @RequestMapping(value = "**", params = {"!latitude", "!longitude", "!subset", "!req"}) void getGridSubset(@Valid GridDataRequestParamsBean params, BindingResult validationResult, HttpServletResponse response, HttpServletRequest request) throws NcssException, IOException, InvalidRangeException, ParseException { if (validationResult.hasErrors()) { handleValidationErrorsResponse(response, HttpServletResponse.SC_BAD_REQUEST, validationResult); } else { String pathInfo = getDatasetPath(request); FeatureDataset fd = null; try { fd = datasetService.findDatasetByPath(request, response, pathInfo); if (fd == null) throw new UnsupportedOperationException("Feature Type not supported"); if (fd.getFeatureType() == FeatureType.GRID) { // Supported formats are netcdf3 (default) and netcdf4 (if available) SupportedFormat sf = SupportedOperation.isSupportedFormat(params.getAccept(), SupportedOperation.GRID_REQUEST); NetcdfFileWriter.Version version = NetcdfFileWriter.Version.netcdf3; if (sf.equals(SupportedFormat.NETCDF4)) { version = NetcdfFileWriter.Version.netcdf4; } GridDataset gridDataset = (GridDataset) fd; GridDataStream gds = GridDataStream.valueOf(gridDataset, pathInfo); File netcdfResult = gds.getResponseFile(request, response, params, version); // Headers... HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("Content-Type", sf.getResponseContentType()); setResponseHeaders(response, httpHeaders); IO.copyFileB(netcdfResult, response.getOutputStream(), 60000); response.flushBuffer(); response.getOutputStream().close(); response.setStatus(HttpServletResponse.SC_OK); gridDataset.close(); } else if (fd.getFeatureType() == FeatureType.STATION) { SupportedFormat sf = SupportedOperation.isSupportedFormat(params.getAccept(), SupportedOperation.POINT_REQUEST); PointDataRequestParamsBean pdr = RequestParamsAdapter.adaptGridParamsToPointParams(params); NCSSPointDataStream pds = NCSSPointDataStreamFactory.getDataStreamer(fd, pdr, sf, response.getOutputStream()); setResponseHeaders(response, pds.getResponseHeaders(fd, sf, pathInfo)); pds.pointDataStream(response, fd, pathInfo, pdr, sf); } } finally { if (fd != null) fd.close(); } } } */ private void setResponseHeaders(HttpServletResponse response, HttpHeaders httpHeaders) { Set<String> keySet = httpHeaders.keySet(); for (String key : keySet) { if (httpHeaders.containsKey(key)) { // LOOK why test again? response.setHeader(key, httpHeaders.get(key).get(0)); // LOOK why only first one ? } } } }