Select Git revision
Cutout.java
-
Robert Butora authored
links to vlkb-volib with support for coord-system params (POSSYS BANDSYS TIMESYS) and their defaults from settings-file
Robert Butora authoredlinks to vlkb-volib with support for coord-system params (POSSYS BANDSYS TIMESYS) and their defaults from settings-file
ServletCutout.java 16.22 KiB
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 =
"<VOTABLE xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.ivoa.net/xml/VOTable/v1.3\" version=\"1.3\">"
+ "<RESOURCE type=\"meta\" utype=\"adhoc:service\" name=\"this\">"
+ "<PARAM name=\"standardID\" datatype=\"char\" arraysize=\"*\" value=\"ivo://ivoa.net/std/SODA#sync-1.0\"/>"
+ "<PARAM name=\"accessURL\" datatype=\"char\" arraysize=\"*\" value=\"" + requestUrl + "\"/>"
+ "<GROUP name=\"inputParams\">"
+ "<PARAM name=\"ID\" ucd=\"meta.id;meta.dataset\" datatype=\"char\" arraysize=\"*\" value=\"\"/>"
+ "<PARAM name=\"POS\" ucd=\"pos.outline;obs\" datatype=\"char\" arraysize=\"*\" value=\"\"/>"
+ "<PARAM name=\"CIRCLE\" ucd=\"phys.angArea;obs\" unit=\"deg\" datatype=\"double\" arraysize=\"3\" xtype=\"circle\" value=\"\"/>"
+ "<PARAM name=\"POLYGON\" unit=\"deg\" ucd=\"pos.outline;obs\" datatype=\"double\" arraysize=\"*\" xtype=\"polygon\" value=\"\"/>"
+ "<PARAM name=\"BAND\" ucd=\"em.wl;stat.interval\" unit=\"m\" datatype=\"double\" arraysize=\"2\" xtype=\"interval\" value=\"\"/>"
+ "<PARAM name=\"TIME\" ucd=\"time.interval;obs.exposure\" unit=\"d\" datatype=\"double\" arraysize=\"2\" xtype=\"interval\" value=\"\"/>"
+ "<PARAM name=\"POL\" ucd=\"meta.code;phys.polarization\" datatype=\"char\" arraysize=\"*\" value=\"\"/>"
+ "<PARAM name=\"RESPONSEFORMAT\" ucd=\"meta.code.mime\" datatype=\"char\" arraysize=\"*\" value=\"application/fits\"/>"
+ "</GROUP>"
+ "</RESOURCE>"
+ "</VOTABLE>";
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<VoParam, String[]> collectSodaParams(HttpServletRequest req)
{
Map<VoParam, String[]> params = new HashMap<VoParam, String[]>();
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<SodaParam, String[]> 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<String, String[]> 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
*/