Skip to content
Snippets Groups Projects
Select Git revision
  • f265eb17e754c8620c33bb77e912b0248e6146ad
  • master default protected
  • ia2
  • adql2.1-ia2
  • private_rows
5 results

MySQLTranslator.java

Blame
  • 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
       */