Something went wrong on our end
Select Git revision
-
Ruben Farinelli authored
for the iteration method
Ruben Farinelli authoredfor the iteration method
ServletCutout.java 14.76 KiB
import java.util.logging.Logger;
import java.util.logging.Level;
import java.security.Principal;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
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.*;
import vo.error.*;
public class ServletCutout extends HttpServlet
{
protected static final Logger LOGGER = Logger.getLogger(ServletCutout.class.getName());
protected static final Settings settings = Settings.getInstance();
protected static final Subsurvey[] subsurveys = Subsurvey.loadSubsurveys(settings.fitsPaths.surveysMetadataAbsPathname());
protected boolean resolveFromId = true;//FIXME separate setting authz is separate table settings.dbConn.isDbUriEmpty();
protected boolean useEngineOverCli = settings.amqpConn.isHostnameEmpty();
final String RESPONSE_ENCODING = "utf-8";
final String DEFAULT_RESPONSEFORMAT = "application/fits";
public void init() throws ServletException
{
LOGGER.config("FITS : " + settings.fitsPaths.toString());
if(subsurveys != null)
LOGGER.config("Subsurveys loaded : " + String.valueOf(subsurveys.length));
LOGGER.config("DEFAULT_RESPONSEFORMAT : " + DEFAULT_RESPONSEFORMAT);
LOGGER.config("Resolver : " + (resolveFromId ? "IVOID" : "DB"));
LOGGER.config("Engine : " + (useEngineOverCli ? "CLI" : "AMQP"));
if(!useEngineOverCli)
LOGGER.config("AMQP : " + settings.amqpConn.toString());
}
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=\"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=\"PIXELS\" ucd=\"instr.pixel;meta.dataset\" datatype=\"char\" arraysize=\"*\" value=\"\"/>"
+ "<PARAM name=\"RESPONSEFORMAT\" ucd=\"meta.code.mime\" datatype=\"char\" arraysize=\"*\" value=\"application/fits\"/>"
+ "<PARAM name=\"POSSYS\" ucd=\"pos.frame\" datatype=\"char\" arraysize=\"*\" value=\"\">"
+ "<DESCRIPTION>Coordinate system for POS values</DESCRIPTION>"
+ "<VALUES>"
+ "<OPTION>ICRS</OPTION>"
+ "<OPTION>GALACTIC</OPTION>"
+ "<OPTION>GRID</OPTION>"
+ "</VALUES>"
+ "</PARAM>"
+ "<PARAM name=\"BANDSYS\" ucd=\"spect;pos.frame\" datatype=\"char\" arraysize=\"*\" value=\"\">"
+ "<DESCRIPTION>Coordinate system for BAND vlaues.</DESCRIPTION>"
+ "<VALUES>"
+ "<OPTION>WAVE_Barycentric</OPTION>"
+ "<OPTION>VELO_LSRK</OPTION>"
+ "<OPTION>GRID</OPTION>"
+ "</VALUES>"
+ "</PARAM>"
+ "</GROUP>"
+ "</RESOURCE>"
+ "</VOTABLE>";
writer.println(theDescriptor);
}
protected void doCutoutStream(String id, Pos pos, Band band, Time time, Pol pol, String pixels,
OutputStream respOutputStream) throws IOException, InterruptedException
{
LOGGER.fine("trace " + pos);
final Resolver resolver = (resolveFromId ?
new ResolverFromId(subsurveys) :
new ResolverByObsCore(settings.dbConn, subsurveys));
final Soda soda = new SodaImpl(settings.fitsPaths);
resolver.resolve(id);
soda.doStream(resolver.relPathname(), resolver.hdunum(), pos, band, time, pol, pixels, respOutputStream);
}
protected CutResult doCutoutFile(String id, Pos pos, Band band, Time time, Pol pol, String pixels,
boolean countNullValues)
throws IOException, InterruptedException
{
LOGGER.fine("trace");
FitsCard[] extraCards = null;
final Resolver resolver = (resolveFromId ?
new ResolverFromId(subsurveys)
: new ResolverByObsCore(settings.dbConn, subsurveys));
final Vlkb vlkb = (useEngineOverCli ?
new VlkbCli(settings, subsurveys)
: new VlkbAmqp(settings, subsurveys));
resolver.resolve(id);
String subsurveyId = resolver.obsCollection();
if(subsurveyId != null)
{
extraCards = Subsurvey.subsurveysFindCards(subsurveys, subsurveyId);
}
else
{
LOGGER.fine("Resolver returns subsurveyId null: no extraCards loaded.");
}
return vlkb.doFile(resolver.relPathname(), resolver.hdunum(),
pos, band, time, pol, pixels, countNullValues, extraCards);
}
/* HTTP/J2EE -> SODA */
/* DALI allows GET and POST for sync services */
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, UnsupportedEncodingException
{
final boolean NO_QUERY_STRING = (request.getQueryString() == null);
if(NO_QUERY_STRING)
{
writeSodaDescriptor(request, response);
LOGGER.fine("normal exit with SODA service descriptor");
return;
}
else
{
LOGGER.info(URLDecoder.decode(request.getQueryString(), "UTF-8"));
execRequest(request, response);
LOGGER.fine("normal exit");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, UnsupportedEncodingException
{
final boolean NO_QUERY_STRING = (request.getQueryString() == null);
if(NO_QUERY_STRING)
{
writeSodaDescriptor(request, response);
LOGGER.fine("normal exit with SODA service descriptor");
return;
}
else
{
LOGGER.info(URLDecoder.decode(request.getQueryString(), "UTF-8"));
execRequest(request, response);
LOGGER.fine("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();
}
protected void execRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, UnsupportedEncodingException
{
long startTime_msec = System.currentTimeMillis();
long startTime_nsec = System.nanoTime();
ServletOutputStream respOutputStream = response.getOutputStream();
try
{
Map<String, String[]> params = request.getParameterMap();
String id = SingleStringParam.parseSingleStringParam(params, "ID");
Pos pos = Pos.parsePos(params);
Band band = Band.parseBand(params);
Time time = Time.parseTime(params);
Pol pol = Pol.parsePol(params);
String pixels = SingleStringParam.parseSingleStringParam(params, "PIXELS");
String respFormat = sodaReq_getResponseFormat(request, DEFAULT_RESPONSEFORMAT);
LOGGER.finest("responseFormat: " + respFormat);
if(respFormat.startsWith("application/fits"))
{
response.setContentType(respFormat);
doCutoutStream(id, pos, band, time, pol, pixels, respOutputStream);
}
else if(respFormat.startsWith("application/x-vlkb+xml"))
{
boolean showDuration = true;
boolean countNullValues = vlkbReq_getNullValues(request);
response.setContentType(respFormat);
CutResult cutResult = doCutoutFile(id, pos, band, time, pol, pixels, countNullValues);// FIXME wh? , respFormat);
/* FIXME errors from engine not checked - cut-file might not have been created */
PrintWriter writer =new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING));
String accessUrl = convertLocalPathnameToRemoteUrl(cutResult.fileName,
settings.fitsPaths.cutouts(),
settings.fitsPaths.cutoutsUrl());
XmlSerializer.serializeToLegacyCutResult(writer, RESPONSE_ENCODING,
cutResult, accessUrl,
id, pos, band, time, pol, pixels, countNullValues,
showDuration, startTime_msec);
writer.close(); /* must close to force flush to complete the xml */
}
else
{
throw new IllegalArgumentException("Unsupported RESPONSEFORMAT value : " + respFormat);
}
}
catch(MultiValuedParamNotSupported ex)
{
LOGGER.warning("MultiValuedParamNotSupported: " + ex.getMessage());
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("text/plain");
PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING));
Lib.doMultiValuedParamNotSupported(ex.getMessage(), writer);
writer.close();
}
catch(IllegalArgumentException ex)
{
LOGGER.warning("IllegalArgumentException: " + ex.getMessage());
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("text/plain");
PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING));
Lib.doUsageError(ex.getMessage(), writer);
writer.close();
}
catch(Exception ex)
{
LOGGER.severe("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));
Lib.doError(ex.toString(), writer);
writer.close();
}
finally
{
respOutputStream.close();
}
LOGGER.fine("RUNTIME[sec]: "+String.valueOf( (System.nanoTime() - startTime_nsec) / 1000000000.0 ));
}
private String convertLocalPathnameToRemoteUrl(String localPathname,
String FITScutpath, String FITSRemoteUrlCutouts)
{
LOGGER.fine("trace " + localPathname);
String fileName = localPathname.replaceAll(FITScutpath + "/", "");
LOGGER.finest("local filename: " + fileName);
String remotefname = FITSRemoteUrlCutouts + "/" + fileName;
LOGGER.finest("remote url : " + remotefname);
return remotefname;
}
/* 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
*/