///////////////////////////////////////////////////////////////////////////// // This file is part of the "Java-DAP" project, a Java implementation // of the OPeNDAP Data Access Protocol. // // Copyright (c) 2010, OPeNDAP, Inc. // Copyright (c) 2002,2003 OPeNDAP, Inc. // // Author: James Gallagher <[email protected]> // // All rights reserved. // // Redistribution and use in source and binary forms, // with or without modification, are permitted provided // that the following conditions are met: // // - Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // - Neither the name of the OPeNDAP nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ///////////////////////////////////////////////////////////////////////////// package opendap.servlet; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.zip.DeflaterOutputStream; import javax.servlet.*; import javax.servlet.http.*; import opendap.dap.*; import opendap.servers.*; import opendap.dap.parsers.ParseException; import opendap.util.Debug; import ucar.nc2.util.EscapeStrings; /** * AbstractServlet is the base servlet class for an OPeNDAP * servers. * Default handlers for all of the acceptable OPeNDAP client * requests are here. * <p/> * Each of the request handlers appears as an adjunct method to * the doGet() method of the base servlet class. In order to * reduce the bulk of this file, many of these methods have been * in wrapper classes in this package (opendap.servlet). * <p/> * This is an abstract class because it is left to the individual * server development efforts to write the getDDS() and * getServerVersion() methods. The getDDS() method is intended to * be where the server specific OPeNDAP server data types are * used via their associated class factory. * <p/> * This code relies on the <code>javax.servlet.ServletConfig</code> * interface (in particular the <code>getInitParameter()</code> method) * to record detailed configuration information used by * the servlet and it's children. * <p/> * The servlet should be started in the servlet engine with the following * initParameters for the tomcat servlet engine: * * <pre> * <servlet> * <servlet-name> * dts * </servlet-name> * <servlet-class> * opendap.servers.test.dts * </servlet-class> * <init-param> * <param-name>INFOcache</param-name> * <param-value>/home/Datasets/info</param-value> * </init-param> * <init-param> * <param-name>DDScache</param-name> * <param-value>/home/Datasets/dds</param-value> * </init-param> * <init-param> * <param-name>DAScache</param-name> * <param-value>/home/Datasets/das</param-value> * </init-param> * <init-param> * <param-name>DDXcache</param-name> * <param-value>/home/Datasets/ddx</param-value> * </init-param> * </servlet> * * </pre> * <p/> * Obviously the actual values of these parameters will depend on your particular * file system. * <h3>See the file <i>SERVLETS</i> in the top level directory of the * software distribution for more detailed information about servlet * configuration.</h3> * Also, the method <code>processDodsURL()</code> could be overloaded * if some kind of special processing of the incoming request is needed * to ascertain the OPeNDAP URL information. * * @author Nathan David Potter * @author jcaron 2/7/07 merge changes * @see opendap.servlet.GetAsciiHandler * @see opendap.servlet.GetDirHandler * @see opendap.servlet.GetHTMLInterfaceHandler * @see opendap.servlet.GetInfoHandler * @see opendap.servlet.ReqState * @see opendap.servlet.ParsedRequest * @see opendap.servlet.GuardedDataset */ public abstract class AbstractServlet extends javax.servlet.http.HttpServlet { ////////////////////////////////////////////////// // Statics static final boolean debug = false; // Define an overall logger for everyone to use // Start with a default logger, but allow an application to change it later public static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractServlet.class); public static void setLog(Class cl) { log = org.slf4j.LoggerFactory.getLogger(cl); } public static void printDODSException(opendap.dap.DAP2Exception de) { de.printStackTrace(); } public static void printThrowable(Throwable t) { AbstractServlet.log.error(t.getMessage()); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); pw.close(); String trace = null; try { sw.close(); trace = sw.toString(); } catch (IOException ioe) { trace = "unknown"; } log.error(trace); } /** * ************************************************************************ * Compression */ protected boolean allowDeflate = true; /** * ************************************************************************ * Debugging */ private boolean track = false; /** * ************************************************************************ * Used for thread syncronization. * * @serial */ private Object syncLock = new Object(); /* * ************************************************************************ * Count "hits" on the server... * * @serial */ private int HitCounter = 0; /** * Keep a copy of the servlet config */ // private ServletConfig servletConfig = null; /** * Keep a copy of the servlet context */ // private ServletContext servletContext = null; /** * path to the root of the servlet in tomcat webapps directory */ protected String rootpath = ""; ////////////////////////////////////////////////////////// // Constructor(s) public AbstractServlet() {} protected void setRootpath(String rootPath) { this.rootpath = rootPath; } ////////////////////////////////////////////////////////// // Get/Set /** * Getter function for rootpath * * @return rootpath * * public String getRootPath() { * return rootpath; * } */ /** * This function must be implemented locally for each OPeNDAP server. It should * return a String containing the OPeNDAP Server Version... * * @return The Server Version String */ public abstract String getServerVersion(); /** * ************************************************************************ * This method must be implemented locally for each OPeNDAP server. The * local implementation of this method is the key piece for connecting * any localized data types that are derived from the opendap.server types * back into the running servlet. * <p/> * This method should do the following: * <ul> * <li>Make a new ServerFactory (aka BaseTypeFactory) for the dataset requested. * <li>Instantiate a ServerDDS using the ServerFactory and populate it (this * could be accomplished by just opening a (cached?) DDS in a file and parsing it) * <li>Return this freshly minted ServerDDS object (to the servlet code where it is used.) * </ul> * * @param rs The ReqState object for this particular client request. * @return The ServerDDS object all parsed and ready to roll. * @throws DAP2Exception * @throws IOException * @throws ParseException * @see opendap.servers.ServerDDS */ protected abstract opendap.servlet.GuardedDataset getDataset(opendap.servlet.ReqState rs) throws Exception; /** * ************************************************************************ * Intitializes the servlet. Init (at this time) basically sets up * the object opendap.util.util.Debug from the debuggery flags in the * servlet InitParameters. The Debug object can be referenced (with * impunity) from anywhere in the VM. */ /* * public void init() throws ServletException { * super.init(); * * // debuggering * String debugOn = getInitParameter("DebugOn"); * if (debugOn != null) { * StringTokenizer toker = new StringTokenizer(debugOn); * while (toker.hasMoreTokens()) * Debug.set(toker.nextToken(), true); * } * * servletConfig = this.getServletConfig(); * servletContext = servletConfig.getServletContext(); * rootpath = servletContext.getRealPath("/"); * * } */ /** * ************************************************************************ * Turns a ParseException into a OPeNDAP DAP2 error and sends it to the client. * * @param pe The <code>ParseException</code> that caused the problem. * @param response The <code>HttpServletResponse</code> for the client. */ public void parseExceptionHandler(ParseException pe, HttpServletResponse response) { log.error("DODSServlet.parseExceptionHandler", pe); if (Debug.isSet("showException")) { log.debug(pe.toString()); printThrowable(pe); } // LogStream.out.println(pe); // pe.printStackTrace(LogStream.out); try { BufferedOutputStream eOut = new BufferedOutputStream(response.getOutputStream()); response.setHeader("Content-Description", "dods-error"); // This should probably be set to "plain" but this works, the // C++ slients don't barf as they would if I sent "plain" AND // the C++ don't expect compressed data if I do this... response.setHeader("Content-Encoding", ""); // response.setContentType("text/plain"); LOOK do we needLogStream.out // Strip any double quotes out of the parser error message. // These get stuck in auto-magically by the javacc generated parser // code and they break our error parser (bummer!) String msg = pe.getMessage().replace('\"', '\''); DAP2Exception de2 = new DAP2Exception(opendap.dap.DAP2Exception.CANNOT_READ_FILE, msg); de2.print(eOut); } catch (IOException ioe) { log.error("Cannot respond to client! IO Error: " + ioe.getMessage()); } } /***************************************************************************/ /** * ************************************************************************ * Sends a OPeNDAP DAP2 error to the client. * * @param de The OPeNDAP DAP2 exception that caused the problem. * @param response The <code>HttpServletResponse</code> for the client. */ public void dap2ExceptionHandler(DAP2Exception de, HttpServletResponse response) { log.info("DODSServlet.dodsExceptionHandler (" + de.getErrorCode() + ") " + de.getErrorMessage()); if (Debug.isSet("showException")) { de.print(System.err); de.printStackTrace(System.err); printDODSException(de); } try { BufferedOutputStream eOut = new BufferedOutputStream(response.getOutputStream()); response.setHeader("Content-Description", "dods-error"); // This should probably be set to "plain" but this works, the // C++ slients don't barf as they would if I sent "plain" AND // the C++ don't expect compressed data if I do this... response.setHeader("Content-Encoding", ""); de.print(eOut); } catch (IOException ioe) { log.error("Cannot respond to client! IO Error: " + ioe.getMessage()); } } /***************************************************************************/ /** * ************************************************************************ * Sends an error to the client. * LOOK: The problem is that if the message is already committed when the IOException occurs, the headers dont get * set. * * @param e The exception that caused the problem. * @param rs The <code>ReqState</code> for the client. */ public void IOExceptionHandler(IOException e, ReqState rs) { HttpServletResponse response = rs.getResponse(); try { BufferedOutputStream eOut = new BufferedOutputStream(response.getOutputStream()); response.setHeader("Content-Description", "dods-error"); // This should probably be set to "plain" but this works, the // C++ slients don't barf as they would if I sent "plain" AND // the C++ don't expect compressed data if I do this... response.setHeader("Content-Encoding", ""); // Strip any double quotes out of the parser error message. // These get stuck in auto-magically by the javacc generated parser // code and they break our error parser (bummer!) String msg = e.getMessage(); if (msg != null) msg = msg.replace('\"', '\''); DAP2Exception de2 = new DAP2Exception(opendap.dap.DAP2Exception.CANNOT_READ_FILE, msg); de2.print(eOut); if (Debug.isSet("showException")) { // Error message log.error("DODServlet ERROR (IOExceptionHandler): " + e); log.error(rs.toString()); if (track) { RequestDebug reqD = (RequestDebug) rs.getUserObject(); log.error(" request number: " + reqD.reqno + " thread: " + reqD.threadDesc); } printThrowable(e); } } catch (IOException ioe) { log.error("Cannot respond to client! IO Error: " + ioe.getMessage()); } } /***************************************************************************/ /** * ************************************************************************ * Sends an error to the client. * * @param e The exception that caused the problem. * @param rs The <code>ReqState</code> for the client. */ public void anyExceptionHandler(Throwable e, ReqState rs) { log.error("DODServlet ERROR (anyExceptionHandler): " + e); printThrowable(e); try { if (rs == null) throw new DAP2Exception("anyExceptionHandler: no request state provided"); log.error(rs.toString()); HttpServletResponse response = rs.getResponse(); log.error(rs.toString()); if (track) { RequestDebug reqD = (RequestDebug) rs.getUserObject(); log.error(" request number: " + reqD.reqno + " thread: " + reqD.threadDesc); } response.setHeader("Content-Description", "dods-error"); // This should probably be set to "plain" but this works, the // C++ slients don't barf as they would if I sent "plain" AND // the C++ don't expect compressed data if I do this... response.setHeader("Content-Encoding", ""); // Strip any double quotes out of the parser error message. // These get stuck in auto-magically by the javacc generated parser // code and they break our error parser (bummer!) String msg = e.getMessage(); if (msg != null) msg = msg.replace('\"', '\''); DAP2Exception de2 = new DAP2Exception(opendap.dap.DAP2Exception.UNDEFINED_ERROR, msg); BufferedOutputStream eOut = new BufferedOutputStream(response.getOutputStream()); de2.print(eOut); } catch (Exception ioe) { log.error("Cannot respond to client! IO Error: " + ioe.getMessage()); } } /***************************************************************************/ /** * ************************************************************************ * Sends a OPeNDAP DAP2 error (type UNKNOWN ERROR) to the client and displays a * message on the server console. * * @param request The client's <code> HttpServletRequest</code> request object. * @param response The server's <code> HttpServletResponse</code> response object. * @param clientMsg Error message <code>String</code> to send to the client. * @param serverMsg Error message <code>String</code> to display on the server console. */ public void sendDODSError(HttpServletRequest request, HttpServletResponse response, String clientMsg, String serverMsg) throws IOException, ServletException { response.setContentType("text/plain"); response.setHeader("XDODS-Server", getServerVersion()); response.setHeader("Content-Description", "dods-error"); // Commented because of a bug in the OPeNDAP C++ stuff... // response.setHeader("Content-Encoding", "none"); ServletOutputStream Out = response.getOutputStream(); DAP2Exception de = new DAP2Exception(opendap.dap.DAP2Exception.UNKNOWN_ERROR, clientMsg); de.print(Out); response.setStatus(HttpServletResponse.SC_OK); log.error(serverMsg); } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's DAS request. Operates on the assumption * that the DAS information is cached on a disk local to the server. If you * don't like that, then you better override it in your server :) * <p/> * <p> * Once the DAS has been parsed it is sent to the requesting client. * * @param rs The ReqState of this client request. Contains all kinds of * important stuff. * @see ReqState */ public void doGetDAS(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("doGetDAS for dataset: " + rs.getDataSet()); } GuardedDataset ds = null; try { ds = getDataset(rs); if (ds == null) return; rs.getResponse().setContentType("text/plain"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-das"); // Commented because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); OutputStream Out = new BufferedOutputStream(rs.getResponse().getOutputStream()); DAS myDAS = ds.getDAS(); myDAS.print(Out); rs.getResponse().setStatus(HttpServletResponse.SC_OK); if (Debug.isSet("showResponse")) { // log.debug("DAS=\n"); // myDAS.print(LogStream.out); } } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (Throwable t) { anyExceptionHandler(t, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's DDS request. Requires the getDDS() method * implemented by each server localization effort. * <p/> * <p> * Once the DDS has been parsed and constrained it is sent to the * requesting client. * * @param rs The ReqState of this client request. Contains all kinds of * important stuff. * @see ReqState */ public void doGetDDS(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) log.debug("doGetDDS for dataset: " + rs.getDataSet()); GuardedDataset ds = null; try { ds = getDataset(rs); if (null == ds) return; rs.getResponse().setContentType("text/plain"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-dds"); // Commented because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); OutputStream Out = new BufferedOutputStream(rs.getResponse().getOutputStream()); // Utilize the getDDS() method to get a parsed and populated DDS // for this server. ServerDDS myDDS = ds.getDDS(); if (rs.getConstraintExpression().equals("")) { // No Constraint Expression? // Send the whole DDS myDDS.print(Out); Out.flush(); } else { // Otherwise, send the constrained DDS // Instantiate the CEEvaluator and parse the constraint expression CEEvaluator ce = new CEEvaluator(myDDS); ce.parseConstraint(rs); // Send the constrained DDS back to the client PrintWriter pw = new PrintWriter(new OutputStreamWriter(Out, StandardCharsets.UTF_8)); myDDS.printConstrained(pw); pw.flush(); } rs.getResponse().setStatus(HttpServletResponse.SC_OK); if (Debug.isSet("showResponse")) { if (rs.getConstraintExpression().equals("")) { // No Constraint Expression? log.debug("Unconstrained DDS=\n"); // myDDS.print(); } else { log.debug("Constrained DDS=\n"); // myDDS.printConstrained(); } } } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (IOException pe) { IOExceptionHandler(pe, rs); } catch (Throwable t) { anyExceptionHandler(t, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's DDS request. Requires the getDDS() method * implemented by each server localization effort. * <p/> * <p> * Once the DDS has been parsed and constrained it is sent to the * requesting client. * * @param rs The ReqState of this client request. Contains all kinds of * important stuff. * @see ReqState */ public void doGetDDX(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) log.debug("doGetDDX for dataset: " + rs.getDataSet()); GuardedDataset ds = null; try { ds = getDataset(rs); if (null == ds) return; rs.getResponse().setContentType("text/plain"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-ddx"); // Commented because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); OutputStream Out = new BufferedOutputStream(rs.getResponse().getOutputStream()); // Utilize the getDDS() method to get a parsed and populated DDS // for this server. ServerDDS myDDS = ds.getDDS(); if (rs.getConstraintExpression().equals("")) { // No Constraint Expression? // Send the whole DDS myDDS.printXML(Out); Out.flush(); } else { // Otherwise, send the constrained DDS // Instantiate the CEEvaluator and parse the constraint expression CEEvaluator ce = new CEEvaluator(myDDS); ce.parseConstraint(rs); // Send the constrained DDS back to the client PrintWriter pw = new PrintWriter(new OutputStreamWriter(Out, StandardCharsets.UTF_8)); myDDS.printConstrainedXML(pw); pw.flush(); } rs.getResponse().setStatus(HttpServletResponse.SC_OK); if (Debug.isSet("showResponse")) { if (rs.getConstraintExpression().equals("")) { // No Constraint Expression? log.debug("Unconstrained DDX=\n"); myDDS.printXML(System.out); } else { log.debug("Constrained DDX=\n"); myDDS.printConstrainedXML(System.out); } } } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (IOException pe) { IOExceptionHandler(pe, rs); } catch (Throwable t) { anyExceptionHandler(t, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's data request. Requires the getDDS() * method implemented by each server localization effort. * <p/> * <p> * Once the DDS has been parsed, the data is read (using the class in the * localized server factory etc.), compared to the constraint expression, * and then sent to the client. * * @param rs The ReqState of this client request. Contains all kinds of * important stuff. * @opendap.ddx.experimental * @see ReqState */ public void doGetBLOB(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("doGetBLOB For: " + rs.getDataSet()); } GuardedDataset ds = null; try { ds = getDataset(rs); if (null == ds) return; rs.getResponse().setContentType("application/octet-stream"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-blob"); ServletOutputStream sOut = rs.getResponse().getOutputStream(); OutputStream bOut; DeflaterOutputStream dOut = null; if (rs.getAcceptsCompressed() && allowDeflate) { rs.getResponse().setHeader("Content-Encoding", "deflate"); dOut = new DeflaterOutputStream(sOut); bOut = new BufferedOutputStream(dOut); } else { // Commented out because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); bOut = new BufferedOutputStream(sOut); } // Utilize the getDDS() method to get a parsed and populated DDS // for this server. ServerDDS myDDS = ds.getDDS(); // Instantiate the CEEvaluator and parse the constraint expression CEEvaluator ce = new CEEvaluator(myDDS); ce.parseConstraint(rs.getConstraintExpression(), rs.getRequestURL().toString()); // Send the binary data back to the client DataOutputStream sink = new DataOutputStream(bOut); ce.send(myDDS.getEncodedName(), sink, ds); sink.flush(); // Finish up sending the compressed stuff, but don't // close the stream (who knows what the Servlet may expect!) if (null != dOut) dOut.finish(); bOut.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (IOException ioe) { IOExceptionHandler(ioe, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's data request. Requires the getDDS() * method implemented by each server localization effort. * <p/> * <p> * Once the DDS has been parsed, the data is read (using the class in the * localized server factory etc.), compared to the constraint expression, * and then sent to the client. * * @param rs The ReqState of this client request. Contains all kinds of * important stuff. * @throws IOException * @throws ServletException * @see ReqState */ public void doGetDAP2Data(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("doGetDAP2Data For: " + rs.getDataSet()); } GuardedDataset ds = null; try { ds = getDataset(rs); if (null == ds) return; rs.getResponse().setContentType("application/octet-stream"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-data"); ServletOutputStream sOut = rs.getResponse().getOutputStream(); OutputStream bOut; DeflaterOutputStream dOut = null; if (rs.getAcceptsCompressed() && allowDeflate) { rs.getResponse().setHeader("Content-Encoding", "deflate"); dOut = new DeflaterOutputStream(sOut); bOut = new BufferedOutputStream(dOut); } else { // Commented out because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); bOut = new BufferedOutputStream(sOut); } // Utilize the getDDS() method to get a parsed and populated DDS // for this server. ServerDDS myDDS = ds.getDDS(); // Instantiate the CEEvaluator and parse the constraint expression CEEvaluator ce = new CEEvaluator(myDDS); ce.parseConstraint(rs); // debug // log.debug("CE DDS = "); // myDDS.printConstrained(LogStream.out); // Send the constrained DDS back to the client PrintWriter pw = new PrintWriter(new OutputStreamWriter(bOut, StandardCharsets.UTF_8)); myDDS.printConstrained(pw); // Send the Data delimiter back to the client // pw.println("Data:"); // JCARON CHANGED pw.flush(); bOut.write("\nData:\n".getBytes(StandardCharsets.UTF_8)); // JCARON CHANGED bOut.flush(); // Send the binary data back to the client DataOutputStream sink = new DataOutputStream(bOut); ce.send(myDDS.getEncodedName(), sink, ds); sink.flush(); // Finish up tsending the compressed stuff, but don't // close the stream (who knows what the Servlet may expect!) if (null != dOut) dOut.finish(); bOut.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (IOException ioe) { IOExceptionHandler(ioe, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's directory request. * <p/> * Returns an html document to the client showing (a possibly pseudo) * listing of the datasets available on the server in a directory listing * format. * <p/> * The bulk of this code resides in the class opendap.servlet.GetDirHandler and * documentation may be found there. * * @param rs The client's <code> ReqState</code> * @see opendap.servlet.GetDirHandler */ public void doGetDIR(ReqState rs) throws Exception { rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("Content-Description", "dods-directory"); try { GetDirHandler di = new GetDirHandler(); di.sendDIR(rs); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (Throwable t) { anyExceptionHandler(t, rs); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's version request. * <p/> * <p> * Returns a plain text document with server version and OPeNDAP core * version #'s * * @param rs The client's <code> ReqState</code> */ public void doGetVER(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("Sending Version Tag."); } rs.getResponse().setContentType("text/plain"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-version"); // Commented because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); pw.println("Server Version: " + getServerVersion()); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } /***************************************************************************/ /** * ************************************************************************ * Default handler for the client's help request. * <p/> * <p> * Returns an html page of help info for the server * * @param rs The client's <code> ReqState </code> */ public void doGetHELP(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("Sending Help Page."); } rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-help"); // Commented because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); printHelpPage(pw); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } /***************************************************************************/ /** * ************************************************************************ * Sends an html document to the client explaining that they have used a * poorly formed URL and then the help page... * * @param rs The client's <code> ReqState </code> */ public void badURL(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("Sending Bad URL Page."); } // log.info("DODSServlet.badURL " + rs.getRequest().getRequestURI()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setHeader("Content-Description", "dods-error"); // Commented because of a bug in the OPeNDAP C++ stuff... // rs.getResponse().setHeader("Content-Encoding", "plain"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); printBadURLPage(pw); printHelpPage(pw); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } /***************************************************************************/ /** * ************************************************************************ * Default handler for OPeNDAP ascii data requests. Returns the request data as * a comma delimited ascii file. Note that this means that the more complex * OPeNDAP structures such as Grids get flattened... * <p/> * <p/> * Modified 2/8/07 jcaron to not make a DConnect2 call to itself * * @param rs the decoded Request State */ public void doGetASC(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("doGetASC For: " + rs.getDataSet()); } GuardedDataset ds = null; try { ds = getDataset(rs); if (ds == null) return; rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/plain"); rs.getResponse().setHeader("Content-Description", "dods-ascii"); if (debug) log.debug("Sending OPeNDAP ASCII Data For: " + rs + " CE: '" + rs.getConstraintExpression() + "'"); ServerDDS dds = ds.getDDS(); // dds = url.getData(ce, null, new asciiFactory()); previous way // Instantiate the CEEvaluator and parse the constraint expression CEEvaluator ce = new CEEvaluator(dds); // i think this makes the dds constrained ce.parseConstraint(rs); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); dds.printConstrained(pw); pw.println("---------------------------------------------"); AsciiWriter writer = new AsciiWriter(); // could be static writer.toASCII(pw, dds, ds); // the way that getDAP2Data works // DataOutputStream sink = new DataOutputStream(bOut); // ce.send(myDDS.getName(), sink, ds); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (Throwable t) { anyExceptionHandler(t, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /** * ************************************************************************ * Default handler for OPeNDAP info requests. Returns an HTML document * describing the contents of the servers datasets. * <p/> * The bulk of this code resides in the class opendap.servlet.GetInfoHandler and * documentation may be found there. * * @param rs The client's <code> ReqState </code> * @see GetInfoHandler */ public void doGetINFO(ReqState rs) throws Exception { if (Debug.isSet("showResponse")) { log.debug("doGetINFO For: " + rs.getDataSet()); } GuardedDataset ds = null; try { ds = getDataset(rs); if (null == ds) return; PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("Content-Description", "dods-description"); GetInfoHandler di = new GetInfoHandler(); di.sendINFO(pw, ds, rs); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (IOException pe) { IOExceptionHandler(pe, rs); } catch (Throwable t) { anyExceptionHandler(t, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /**************************************************************************/ /** * ************************************************************************ * Default handler for OPeNDAP .html requests. Returns the OPeNDAP Web * Interface (aka The Interface From Hell) to the client. * <p/> * The bulk of this code resides in the class * opendap.servlet.GetHTMLInterfaceHandler and * documentation may be found there. * * @param rs The client's <code> ReqState</code> * @see GetHTMLInterfaceHandler */ public void doGetHTML(ReqState rs) throws Exception { GuardedDataset ds = null; try { ds = getDataset(rs); if (ds == null) return; rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("Content-Description", "dods-form"); // Utilize the getDDS() method to get a parsed and populated DDS // for this server. ServerDDS myDDS = ds.getDDS(); DAS das = ds.getDAS(); GetHTMLInterfaceHandler di = new GetHTMLInterfaceHandler(); di.sendDataRequestForm(rs, rs.getDataSet(), myDDS, das); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } catch (ParseException pe) { parseExceptionHandler(pe, rs.getResponse()); } catch (DAP2Exception de) { dap2ExceptionHandler(de, rs.getResponse()); } catch (IOException pe) { IOExceptionHandler(pe, rs); } catch (Throwable t) { anyExceptionHandler(t, rs); } finally { // release lock if needed if (ds != null) ds.release(); } } /***************************************************************************/ /** * ************************************************************************ * Default handler for OPeNDAP catalog.xml requests. * * @param rs The client's <code> ReqState </code> * @see GetHTMLInterfaceHandler */ public void doGetCatalog(ReqState rs) throws Exception { rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/xml"); rs.getResponse().setHeader("Content-Description", "dods-catalog"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); printCatalog(rs, pw); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } // to be overridden by servers that implement catalogs protected void printCatalog(ReqState rs, PrintWriter os) throws IOException { os.println("Catalog not available for this server"); os.println("Server version = " + getServerVersion()); } /***************************************************************************/ /** * ************************************************************************ * Default handler for debug requests; * * @param rs The client's <code> ReqState </code> object. */ public void doDebug(ReqState rs) throws IOException { rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("Content-Description", "dods_debug"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); pw.println("<title>Debugging</title>"); pw.println("<body><pre>"); StringTokenizer tz = new StringTokenizer(rs.getConstraintExpression(), "=;"); while (tz.hasMoreTokens()) { String cmd = tz.nextToken(); pw.println("Cmd= " + cmd); if (cmd.equals("help")) { pw.println(" help;log;logEnd;logShow"); pw.println(" showFlags;showInitParameters;showRequest"); pw.println(" on|off=(flagName)"); doDebugCmd(cmd, tz, pw); // for subclasses } else if (cmd.equals("on")) Debug.set(tz.nextToken(), true); else if (cmd.equals("off")) Debug.set(tz.nextToken(), false); else if (cmd.equals("showFlags")) { Iterator iter = Debug.keySet().iterator(); while (iter.hasNext()) { String key = (String) iter.next(); pw.println(" " + key + " " + Debug.isSet(key)); } } else if (cmd.equals("showInitParameters")) pw.println(rs.toString()); else if (cmd.equals("showRequest")) probeRequest(pw, rs); else if (!doDebugCmd(cmd, tz, pw)) { // for subclasses pw.println(" unrecognized command"); } } pw.println("--------------------------------------"); pw.println("Logging is on"); Iterator iter = Debug.keySet().iterator(); while (iter.hasNext()) { String key = (String) iter.next(); boolean val = Debug.isSet(key); if (val) pw.println(" " + key + " " + Debug.isSet(key)); } pw.println("</pre></body>"); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } protected boolean doDebugCmd(String cmd, StringTokenizer tz, PrintWriter pw) { return false; } /** * ************************************************************************ * Default handler for OPeNDAP status requests; not publically available, * used only for debugging * * @param rs The client's <code> ReqState </code> * @see GetHTMLInterfaceHandler */ public void doGetSystemProps(ReqState rs) throws Exception { rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("Content-Description", "dods-status"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); pw.println("<html>"); pw.println("<title>System Properties</title>"); pw.println("<hr>"); pw.println("<body><h2>System Properties</h2>"); pw.println("<h3>Date: " + new Date() + "</h3>"); Properties sysp = System.getProperties(); Enumeration e = sysp.propertyNames(); pw.println("<ul>"); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = System.getProperty(name); pw.println("<li>" + name + ": " + value + "</li>"); } pw.println("</ul>"); pw.println("<h3>Runtime Info:</h3>"); Runtime rt = Runtime.getRuntime(); pw.println("JVM Max Memory: " + (rt.maxMemory() / 1024) / 1000. + " MB (JVM Maximum Allowable Heap)<br>"); pw.println("JVM Total Memory: " + (rt.totalMemory() / 1024) / 1000. + " MB (JVM Heap size)<br>"); pw.println("JVM Free Memory: " + (rt.freeMemory() / 1024) / 1000. + " MB (Unused part of heap)<br>"); pw.println("JVM Used Memory: " + ((rt.totalMemory() - rt.freeMemory()) / 1024) / 1000. + " MB (Currently active memory)<br>"); pw.println("<hr>"); pw.println("</body>"); pw.println("</html>"); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } /** * ************************************************************************ * Default handler for OPeNDAP status requests; not publically available, * used only for debugging * * @param rs The client's <code> ReqState</code> * @see GetHTMLInterfaceHandler */ public void doGetStatus(ReqState rs) throws Exception { rs.getResponse().setHeader("XDODS-Server", getServerVersion()); rs.getResponse().setContentType("text/html"); rs.getResponse().setHeader("Content-Description", "dods-status"); PrintWriter pw = new PrintWriter(new OutputStreamWriter(rs.getResponse().getOutputStream(), StandardCharsets.UTF_8)); pw.println("<title>Server Status</title>"); pw.println("<body><ul>"); printStatus(pw); pw.println("</ul></body>"); pw.flush(); rs.getResponse().setStatus(HttpServletResponse.SC_OK); } // to be overridden by servers that implement status report protected void printStatus(PrintWriter os) throws IOException { os.println("<h2>Server version = " + getServerVersion() + "</h2>"); os.println("<h2>Number of Requests Received = " + HitCounter + "</h2>"); if (track) { int n = prArr.size(); int pending = 0; StringBuilder preqs = new StringBuilder(); for (int i = 0; i < n; i++) { ReqState rs = (ReqState) prArr.get(i); RequestDebug reqD = (RequestDebug) rs.getUserObject(); if (!reqD.done) { preqs.append("<pre>-----------------------\n"); preqs.append("Request["); preqs.append(reqD.reqno); preqs.append("]("); preqs.append(reqD.threadDesc); preqs.append(") is pending.\n"); preqs.append(rs.toString()); preqs.append("</pre>"); pending++; } } os.println("<h2>" + pending + " Pending Request(s)</h2>"); os.println(preqs.toString()); } } /***************************************************************************/ /** * ************************************************************************ * This is a bit of instrumentation that I kept around to let me look at the * state of the incoming <code>HttpServletRequest</code> from the client. * This method calls the <code>get*</code> methods of the request and prints * the results to standard out. * * @param ps The <code>PrintStream</code> to send output. * @param rs The <code>ReqState</code> object to probe. */ public void probeRequest(PrintWriter ps, ReqState rs) { Enumeration e; int i; ps.println("####################### PROBE ##################################"); ps.println("The HttpServletRequest object is actually a: " + rs.getRequest().getClass().getName()); ps.println(""); ps.println("HttpServletRequest Interface:"); ps.println(" getAuthType: " + rs.getRequest().getAuthType()); ps.println(" getMethod: " + rs.getRequest().getMethod()); ps.println(" getPathInfo: " + rs.getRequest().getPathInfo()); ps.println(" getPathTranslated: " + rs.getRequest().getPathTranslated()); ps.println(" getRequestURL: " + rs.getRequest().getRequestURL()); ps.println(" getQueryString: " + rs.getRequest().getQueryString()); ps.println(" getRemoteUser: " + rs.getRequest().getRemoteUser()); ps.println(" getRequestedSessionId: " + rs.getRequest().getRequestedSessionId()); ps.println(" getRequestURI: " + rs.getRequest().getRequestURI()); ps.println(" getServletPath: " + rs.getRequest().getServletPath()); ps.println(" isRequestedSessionIdFromCookie: " + rs.getRequest().isRequestedSessionIdFromCookie()); ps.println(" isRequestedSessionIdValid: " + rs.getRequest().isRequestedSessionIdValid()); ps.println(" isRequestedSessionIdFromURL: " + rs.getRequest().isRequestedSessionIdFromURL()); ps.println(""); i = 0; e = rs.getRequest().getHeaderNames(); ps.println(" Header Names:"); while (e.hasMoreElements()) { i++; String s = (String) e.nextElement(); ps.print(" Header[" + i + "]: " + s); ps.println(": " + rs.getRequest().getHeader(s)); } ps.println(""); ps.println("ServletRequest Interface:"); ps.println(" getCharacterEncoding: " + rs.getRequest().getCharacterEncoding()); ps.println(" getContentType: " + rs.getRequest().getContentType()); ps.println(" getContentLength: " + rs.getRequest().getContentLength()); ps.println(" getProtocol: " + rs.getRequest().getProtocol()); ps.println(" getScheme: " + rs.getRequest().getScheme()); ps.println(" getServerName: " + rs.getRequest().getServerName()); ps.println(" getServerPort: " + rs.getRequest().getServerPort()); ps.println(" getRemoteAddr: " + rs.getRequest().getRemoteAddr()); ps.println(" getRemoteHost: " + rs.getRequest().getRemoteHost()); // ps.println(" getRealPath: "+rs.getRequest().getRealPath()); ps.println("............................."); ps.println(""); i = 0; e = rs.getRequest().getAttributeNames(); ps.println(" Attribute Names:"); while (e.hasMoreElements()) { i++; String s = (String) e.nextElement(); ps.print(" Attribute[" + i + "]: " + s); ps.println(" Type: " + rs.getRequest().getAttribute(s)); } ps.println("............................."); ps.println(""); i = 0; e = rs.getRequest().getParameterNames(); ps.println(" Parameter Names:"); while (e.hasMoreElements()) { i++; String s = (String) e.nextElement(); ps.print(" Parameter[" + i + "]: " + s); ps.println(" Value: " + rs.getRequest().getParameter(s)); } ps.println("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); ps.println(" . . . . . . . . . Servlet Infomation API . . . . . . . . . . . . . ."); ps.println(""); ps.println("Servlet Context:"); ps.println(""); /* * i = 0; * e = servletContext.getAttributeNames(); * ps.println(" Attribute Names:"); * while (e.hasMoreElements()) { * i++; * String s = (String) e.nextElement(); * ps.print(" Attribute[" + i + "]: " + s); * ps.println(" Type: " + servletContext.getAttribute(s)); * } * * ps.println(" ServletContext.getRealPath(\".\"): " + servletContext.getRealPath(".")); * ps.println(" ServletContext.getMajorVersion(): " + servletContext.getMajorVersion()); * // ps.println("ServletContext.getMimeType(): " + sc.getMimeType()); * ps.println(" ServletContext.getMinorVersion(): " + servletContext.getMinorVersion()); * // ps.println("ServletContext.getRealPath(): " + sc.getRealPath()); */ ps.println("............................."); ps.println("Servlet Config:"); ps.println(""); ServletConfig scnfg = getServletConfig(); i = 0; e = scnfg.getInitParameterNames(); ps.println(" InitParameters:"); while (e.hasMoreElements()) { String p = (String) e.nextElement(); ps.print(" InitParameter[" + i + "]: " + p); ps.println(" Value: " + scnfg.getInitParameter(p)); i++; } ps.println(""); ps.println("######################## END PROBE ###############################"); ps.println(""); } /***************************************************************************/ /** * ************************************************************************ * <p/> * In this (default) implementation of the getServerName() method we just get * the name of the servlet and pass it back. If something different is * required, override this method when implementing the getDDS() and * getServerVersion() methods. * <p/> * This is typically used by the getINFO() method to figure out if there is * information specific to this server residing in the info directory that * needs to be returned to the client as part of the .info rs.getResponse(). * * @return A string containing the name of the servlet class that is running. */ public String getServerName() { // Ascertain the name of this server. String servletName = this.getClass().getName(); return (servletName); } /** * *********************************************************************** * Handles incoming requests from clients. Parses the request and determines * what kind of OPeNDAP response the client is requesting. If the request is * understood, then the appropriate handler method is called, otherwise * an error is returned to the client. * <p/> * This method is the entry point for <code>AbstractServlet</code>. * * @param request The client's <code> HttpServletRequest</code> request * object. * @param response The server's <code> HttpServletResponse</code> response * object. * @see opendap.servlet.ReqState */ public void doGet(HttpServletRequest request, HttpServletResponse response) { // setHeader("Last-Modified", (new Date()).toString() ); boolean isDebug = false; ReqState rs = null; RequestDebug reqD = null; try { rs = getRequestState(request, response); if (rs != null) { if (Debug.isSet("probeRequest")) { probeRequest(new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8), true), rs); } String ds = rs.getDataSet(); String suff = rs.getRequestSuffix(); isDebug = ((ds != null) && ds.equals("debug") && (suff != null) && suff.equals("")); } synchronized (syncLock) { if (!isDebug) { long reqno = HitCounter++; if (track) { reqD = new RequestDebug(reqno, Thread.currentThread().toString()); rs.setUserObject(reqD); if (prArr == null) prArr = new ArrayList(10000); prArr.add((int) reqno, rs); } if (Debug.isSet("showRequest")) { log.debug("-------------------------------------------"); log.debug("Server: " + getServerName() + " Request #" + reqno); log.debug("Client: " + rs.getRequest().getRemoteHost()); log.debug(rs.toString()); log.debug("Request dataset: '" + rs.getDataSet() + "' suffix: '" + rs.getRequestSuffix() + "' CE: '" + rs.getConstraintExpression() + "'"); } } } // synch if (rs != null) { String dataSet = rs.getDataSet(); String requestSuffix = rs.getRequestSuffix(); if (dataSet == null) { doGetDIR(rs); } else if (dataSet.equals("/")) { doGetDIR(rs); } else if (dataSet.equals("")) { doGetDIR(rs); } else if (dataSet.equalsIgnoreCase("/version") || dataSet.equalsIgnoreCase("/version/")) { doGetVER(rs); } else if (dataSet.equalsIgnoreCase("/help") || dataSet.equalsIgnoreCase("/help/")) { doGetHELP(rs); } else if (dataSet.equalsIgnoreCase("/" + requestSuffix)) { doGetHELP(rs); } else if (requestSuffix.equalsIgnoreCase("dds")) { doGetDDS(rs); } else if (requestSuffix.equalsIgnoreCase("das")) { doGetDAS(rs); } else if (requestSuffix.equalsIgnoreCase("ddx")) { doGetDDX(rs); } else if (requestSuffix.equalsIgnoreCase("blob")) { doGetBLOB(rs); } else if (requestSuffix.equalsIgnoreCase("dods")) { doGetDAP2Data(rs); } else if (requestSuffix.equalsIgnoreCase("asc") || requestSuffix.equalsIgnoreCase("ascii")) { doGetASC(rs); } else if (requestSuffix.equalsIgnoreCase("info")) { doGetINFO(rs); } else if (requestSuffix.equalsIgnoreCase("html") || requestSuffix.equalsIgnoreCase("htm")) { doGetHTML(rs); } else if (requestSuffix.equalsIgnoreCase("ver") || requestSuffix.equalsIgnoreCase("version")) { doGetVER(rs); } else if (requestSuffix.equalsIgnoreCase("help")) { doGetHELP(rs); /* * JC added * } else if (dataSet.equalsIgnoreCase("catalog") && requestSuffix.equalsIgnoreCase("xml")) { * doGetCatalog(rs); * } else if (dataSet.equalsIgnoreCase("status")) { * doGetStatus(rs); * } else if (dataSet.equalsIgnoreCase("systemproperties")) { * doGetSystemProps(rs); * } else if (isDebug) { * doDebug(rs); */ } else if (requestSuffix.equals("")) { badURL(rs); } else { badURL(rs); } } else { badURL(rs); } if (reqD != null) reqD.done = true; } catch (Throwable e) { anyExceptionHandler(e, rs); } } /** * @param request * @return the request state */ protected ReqState getRequestState(HttpServletRequest request, HttpServletResponse response) throws DAP2Exception { ReqState rs = null; // The url and query strings will come to us in encoded form // (see HTTPmethod.newMethod()) String baseurl = request.getRequestURL().toString(); baseurl = EscapeStrings.unescapeURL(baseurl); String query = request.getQueryString(); query = EscapeStrings.unescapeURLQuery(query); rs = new ReqState(this, request, response, rootpath, baseurl, query); return rs; } // ************************************************************************** /** * ************************************************************************ * Prints the OPeNDAP Server help page to the passed PrintWriter * * @param pw PrintWriter stream to which to dump the help page. */ private void printHelpPage(PrintWriter pw) { pw.println("<h3>OPeNDAP Server Help</h3>"); pw.println("To access most of the features of this OPeNDAP server, append"); pw.println("one of the following a eight suffixes to a URL: .das, .dds, .dods, .ddx, .blob, .info,"); pw.println(".ver or .help. Using these suffixes, you can ask this server for:"); pw.println("<dl>"); pw.println("<dt> das </dt> <dd> Dataset Attribute Structure (DAS)</dd>"); pw.println("<dt> dds </dt> <dd> Dataset Descriptor Structure (DDS)</dd>"); pw.println("<dt> dods </dt> <dd> DataDDS object (A constrained DDS populated with data)</dd>"); pw.println("<dt> ddx </dt> <dd> XML version of the DDS/DAS</dd>"); pw.println("<dt> blob </dt> <dd> Serialized binary data content for requested data set, " + "with the constraint expression applied.</dd>"); pw.println("<dt> info </dt> <dd> info object (attributes, types and other information)</dd>"); pw.println("<dt> html </dt> <dd> html form for this dataset</dd>"); pw.println("<dt> ver </dt> <dd> return the version number of the server</dd>"); pw.println("<dt> help </dt> <dd> help information (this text)</dd>"); pw.println("</dl>"); pw.println("For example, to request the DAS object from the FNOC1 dataset at URI/GSO (a"); pw.println("test dataset) you would appand `.das' to the URL:"); pw.println("http://opendap.gso.url.edu/cgi-bin/nph-nc/data/fnoc1.nc.das."); pw.println("<p><b>Note</b>: Many OPeNDAP clients supply these extensions for you so you don't"); pw.println("need to append them (for example when using interfaces supplied by us or"); pw.println("software re-linked with a OPeNDAP client-library). Generally, you only need to"); pw.println("add these if you are typing a URL directly into a WWW browser."); pw.println("<p><b>Note</b>: If you would like version information for this server but"); pw.println("don't know a specific data file or data set name, use `/version' for the"); pw.println("filename. For example: http://opendap.gso.url.edu/cgi-bin/nph-nc/version will"); pw.println("return the version number for the netCDF server used in the first example. "); pw.println("<p><b>Suggestion</b>: If you're typing this URL into a WWW browser and"); pw.println("would like information about the dataset, use the `.info' extension."); pw.println("<p>If you'd like to see a data values, use the `.html' extension and submit a"); pw.println("query using the customized form."); } // ************************************************************************** /** * ************************************************************************ * Prints the Bad URL Page page to the passed PrintWriter * * @param pw PrintWriter stream to which to dump the bad URL page. */ private void printBadURLPage(PrintWriter pw) { pw.println("<h3>Error in URL</h3>"); pw.println("The URL extension did not match any that are known by this"); pw.println("server. Below is a list of the five extensions that are be recognized by"); pw.println("all OPeNDAP servers. If you think that the server is broken (that the URL you"); pw.println("submitted should have worked), then please contact the"); pw.println("OPeNDAP user support coordinator at: "); pw.println("<a href=\"mailto:[email protected]\">[email protected]</a><p>"); } // ************************************************************************** // debug private ArrayList prArr = null; private static class RequestDebug { long reqno; String threadDesc; boolean done = false; RequestDebug(long reqno, String threadDesc) { this.reqno = reqno; this.threadDesc = threadDesc; } } }