import java.util.logging.Logger; import java.security.Principal; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletOutputStream; import java.io.OutputStreamWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; /* for streaming the cutout-file */ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Arrays; import java.util.List; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; import java.util.Properties; // for Logging/Accounting import org.json.simple.JSONObject; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; import java.nio.file.StandardOpenOption; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import vo.parameter.*; public class ServletCutout extends javax.servlet.http.HttpServlet { protected static final Logger LOGGER = Logger.getLogger(ServletCutout.class.getName()); protected static final Settings settings = Settings.getInstance(); final String RESPONSE_ENCODING = "utf-8"; final String DEFAULT_RESPONSEFORMAT = settings.defaults.responseFormat; final String DEFAULT_SKY_SYSTEM = settings.defaults.skySystem; final String DEFAULT_SPEC_SYSTEM = settings.defaults.specSystem; final String DEFAULT_TIME_SYSTEM = "MJD_UTC"; // FIXME take from confif file boolean showDuration = settings.defaults.showDuration; long startTime_msec; protected Cutout cutout = new CutoutImpl(settings); private Subsurvey[] subsurveys = null; public void init() throws ServletException { super.init(); LOGGER.info("AMQP : " + settings.amqpConn.toString()); LOGGER.info("FITS : " + settings.fitsPaths.toString()); String surveysAbsPathname = settings.fitsPaths.surveysMetadataAbsPathname(); if( (surveysAbsPathname != null) && (surveysAbsPathname.length() > 1) ) subsurveys = Subsurvey.loadSubsurveys(surveysAbsPathname); } protected void doSodaDescriptor(PrintWriter writer, String requestUrl) { String theDescriptor = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; writer.println(theDescriptor); } protected void doCutoutStream(String id, Pos pos, Band band, Time time, Pol pol, OutputStream respOutputStream) throws IOException, InterruptedException { LOGGER.info("trace" + pos); Resolver rsl = new ResolverFromId(); rsl.resolve(id); // if(pos != null) pos.setSystem(Pos.System.valueOf(DEFAULT_SKY_SYSTEM)); if(band != null) band.setSystem(Band.System.valueOf(DEFAULT_SPEC_SYSTEM)); if(time != null) time.setSystem(Time.System.valueOf(DEFAULT_TIME_SYSTEM)); cutout.doStream(rsl.relPathname(), rsl.hdunum(), pos, band, time, pol, respOutputStream); } protected DataLink doCutoutFile(String id, Pos pos, Band band, Time time, Pol pol, boolean countNullValues, String respFormat) throws IOException, InterruptedException { LOGGER.info("trace"); String relPathname; int hdunum; String dbUri = settings.dbConn.uri(); if(settings.dbConn.isDbUriEmpty()) { Resolver rsl = new ResolverFromId(); rsl.resolve(id); relPathname = rsl.relPathname(); hdunum = rsl.hdunum(); } else { ResolverByObsCore rsl = new ResolverByObsCore(settings.dbConn, subsurveys); rsl.resolve(id); relPathname = rsl.relPathname(); hdunum = rsl.hdunum(); String subsurveyId = rsl.obsCollection(); //this implementation assumes ObsCore::obs_collection holds ssID FitsCard[] extraCards = null; if(subsurveyId != null) { extraCards = Subsurvey.subsurveysFindCards(subsurveys, subsurveyId); } else { LOGGER.info("Resolver with Obscore returns subsurveyId null: no extraCards loaded."); } // FIXME use of extraCards not implemented } final String DEFAULT_TIME_SYSTEM = "MJD_UTC"; // FIXME take from confif file if(pos != null) pos.setSystem(Pos.System.valueOf(DEFAULT_SKY_SYSTEM)); if(band != null) band.setSystem(Band.System.valueOf(DEFAULT_SPEC_SYSTEM)); if(time != null) time.setSystem(Time.System.valueOf(DEFAULT_TIME_SYSTEM)); CutResult cutResult = cutout.doFile(relPathname, hdunum, pos, band, time, pol, false, null); DataLink dlk = new DataLink(); dlk.id = id; dlk.accessUrl = dlk.convertLocalPathnameToRemoteUrl(cutResult.filename, settings.fitsPaths.cutouts(), settings.fitsPaths.cutoutsUrl()); dlk.serviceDef = null; dlk.errorMessage = null; dlk.description = "A cutout from " + id;// + " by parameters " // + pos.toString() + " " + band.toString() + " " + time.toString() + " " + pol.toString(); dlk.semantics = "http://www.ivoa.net/rdf/datalink/core#proc#cutout"; dlk.contentType = "application/fits"; dlk.contentLength = cutResult.filesize; // VLKB-extension to DataLink: Coord coord = new Coord(DEFAULT_SKY_SYSTEM, pos, DEFAULT_SPEC_SYSTEM, band, time, pol); LOGGER.info(coord.toString()); dlk.inputs = new Inputs(id, coord, countNullValues); dlk.versionString = Version.asString; dlk.cut = null; dlk.absCutPathname = cutResult.filename; dlk.datacubeCount = 1; dlk.nullVals = ((cutResult.nullValueCount.percent < 0) || (cutResult.nullValueCount.totalCount < 1)) ? null : cutResult.nullValueCount; dlk.mcutResultArr = null; return dlk; } protected void doMultiValuedParamNotSupported(String message, PrintWriter printWriter) { printWriter.println("MultiValuedParamNotSupported : " + message); } protected void doUsageError(String message, PrintWriter printWriter) { printWriter.println("UsageError : " + message); } protected void doError(String message, PrintWriter printWriter) { printWriter.println("Error : " + message); } /* HTTP/J2EE -> SODA */ /* DALI allows GET and POST for sync services */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, UnsupportedEncodingException { startTime_msec = System.currentTimeMillis(); final boolean NO_QUERY_STRING = (request.getQueryString() == null); if(NO_QUERY_STRING) { writeSodaDescriptor(request, response); LOGGER.info("normal exit with SODA service descriptor"); return; } else { convertHttpToSoda(request, response); LOGGER.info("normal exit"); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, UnsupportedEncodingException { startTime_msec = System.currentTimeMillis(); final boolean NO_QUERY_STRING = (request.getQueryString() == null); if(NO_QUERY_STRING) { writeSodaDescriptor(request, response); LOGGER.info("normal exit with SODA service descriptor"); return; } else { convertHttpToSoda(request, response); LOGGER.info("normal exit"); } } protected void writeSodaDescriptor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, UnsupportedEncodingException { PrintWriter writer = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), RESPONSE_ENCODING)); response.setContentType("text/xml"); doSodaDescriptor(writer, request.getRequestURL().toString()); writer.close(); } /* private Map collectSodaParams(HttpServletRequest req) { Map params = new HashMap(); for(VoParam paramToken : VoParam.values()) { String[] paramValue = req.getParameterValues(paramToken.toString()); params.put(paramToken, paramValue); } return params; } */ protected void convertHttpToSoda(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, UnsupportedEncodingException { ServletOutputStream respOutputStream = response.getOutputStream(); try { /*/Map params = collectSodaParams(request); SodaParser parser = new SodaParser(params); String id = null; Pos pos = null; Band band = null; Time time = null; Pol pol = null; if(parser.sodaReq_hasSodaId()) { id = parser.sodaReq_getId(); pos = parser.sodaReq_getPosCirclePolygon(); band = parser.sodaReq_getBand(); time = parser.sodaReq_getTime(); pol = parser.sodaReq_getPol(); } else { id = parser.vlkbReq_getPubdid(); pos = parser.vlkbReq_getCircleRect(); band = parser.vlkbReq_getVelocity(); } */ Map params = request.getParameterMap(); String id = SingleStringParam.parseSingleStringParam(params, "ID"); Pos pos = Pos.parsePos(params, DEFAULT_SKY_SYSTEM); Band band = Band.parseBand(params, DEFAULT_SPEC_SYSTEM); Time time = Time.parseTime(params, DEFAULT_TIME_SYSTEM); Pol pol = Pol.parsePol(params); String respFormat = sodaReq_getResponseFormat(request, DEFAULT_RESPONSEFORMAT); LOGGER.info("responseFormat: " + respFormat); if(respFormat.equals("application/fits")) { response.setContentType(respFormat); doCutoutStream(id, pos, band, time, pol, respOutputStream); } else if(respFormat.equals("application/x-vlkb+xml")) { boolean countNullValues = vlkbReq_getNullValues(request); response.setContentType(respFormat); DataLink respDataLink = doCutoutFile(id, pos, band, time, pol, countNullValues, respFormat); /* FIXME errors from engine not checked - cut-file might not have been created */ LOGGER.info("DataLink - id:" + respDataLink.id + " url: " + respDataLink.accessUrl ); final String respEncoding = "utf-8"; PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING)); XmlSerializer.serializeToLegacyCutResults(writer, respEncoding, respDataLink, showDuration, startTime_msec); writer.close(); /* NOTE must close to force flush to complete the xml */ } else { throw new IllegalArgumentException("Unsupported RESPONSEFORMAT value : " + respFormat); } } catch(MultiValuedParamNotSupported ex) { LOGGER.info("MultiValuedParamNotSupported: " + ex.getMessage()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setContentType("text/plain"); PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING)); doMultiValuedParamNotSupported(ex.getMessage(), writer); writer.close(); } catch(IllegalArgumentException ex) { LOGGER.info("IllegalArgumentException: " + ex.getMessage()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setContentType("text/plain"); PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING)); doUsageError(ex.getMessage(), writer); writer.close(); } catch(Exception ex) { LOGGER.info("Exception: " + ex.getMessage()); ex.printStackTrace(); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setContentType("text/plain"); PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING)); doError(ex.toString(), writer); writer.close(); } finally { respOutputStream.close(); } } /* SODA */ /* return null if value not present or the value if present exactly once * else throw MultiplicityNotSupoorted SODA_error */ private String soda_getSingleValue(HttpServletRequest req, String name) { String[] valArr = req.getParameterValues(name); if(valArr == null) return null; else if(valArr.length == 0) return null; else if(valArr.length == 1) return valArr[0]; else throw new IllegalArgumentException( "MultiValuedParamNotSupported: " + name + " was found " + valArr.length + " times"); } private String sodaReq_getResponseFormat(HttpServletRequest req, String defaultResponseFormat) { String respFormat = soda_getSingleValue(req, "RESPONSEFORMAT"); return ((respFormat == null) ? defaultResponseFormat : respFormat); } private boolean vlkbReq_getNullValues(HttpServletRequest request) { return (null != soda_getSingleValue(request, "nullvals")); } } /* from SODA (upon error): Error codes are specified in DALI. Error documents should be text using the text/plain content-type and the text must begin with one of the following strings: Error CodeDescription --------------------------------------- Error: General error (not covered below) AuthenticationError: Not authenticated AuthorizationError: Not authorized to access the resource ServiceUnavailable: Transient error (could succeed with retry) UsageError: Permanent error (retry pointless) MultiValuedParamNotSupported: request included multiple values for a parameter but the service only supports a single value */ /* from DALI (upon successful request): The service should set HTTP headers (Fielding and Gettys et al., 1999) that are useful to the correct values where possible. Recommended headers to set when possible: Content-Type Content-Encoding Content-Length -- not in SPDA-stream impossible to know Last-Modified -- not in SODA-stream impossible to know */