/*
* Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "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 UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* class RadarServer
*
* Library for the TDS Radar Server
*
* Currently servers Level2 and Level3 Radar files.
* Finds the files for a particular station, product and time.
* Outputs either html or xml files.
*
* By: Robb Kambic 09/25/2007
*
*/
package thredds.server.radarServer;
import thredds.servlet.*;
import thredds.catalog.*;
import ucar.nc2.time.CalendarDateRange;
import java.io.*;
import java.util.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import javax.servlet.*;
import javax.servlet.http.*;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.transform.XSLTransformer;
import org.jdom2.output.XMLOutputter;
import org.jdom2.output.Format;
import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.util.StringUtil2;
public class RadarServer extends AbstractServlet {
public enum RadarType {
nexrad, terminal
}
static public InvCatalogImpl cat;
static public final String catName = "radarCollections.xml";
static public URI catURI;
static public List datasets;
static public HashMap<String, String> dataLocation = new HashMap<String, String>();
static public RadarMethods rm;
private boolean allow = false;
private boolean debug = false;
protected long getLastModified(HttpServletRequest req) {
contentPath = ServletUtil.getContentPath();
File file = new File(contentPath + getPath() + catName);
return file.lastModified();
}
// must end with "/"
protected String getPath() {
return "servers/";
}
public void init() throws ServletException {
super.init();
//allow = ThreddsConfig.getBoolean("NetcdfSubsetService.allow", true);
//String radarLevel2Dir = ThreddsConfig.get("NetcdfSubsetService.radarLevel2DataDir", "/data/ldm/pub/native/radar/level2/");
//if (!allow) return;
contentPath = ServletUtil.getContentPath();
rm = new RadarMethods(contentPath, logServerStartup);
// read in radarCollections.xml catalog
InvCatalogFactory factory = InvCatalogFactory.getDefaultFactory(false); // no validation
cat = readCatalog(factory, getPath() + catName, contentPath + getPath() + catName);
if (cat == null) {
logServerStartup.info("cat initialization failed");
return;
}
//URI tmpURI = cat.getBaseURI();
cat.setBaseURI(catURI);
// get path and location from cat
List parents = cat.getDatasets();
for (int i = 0; i < parents.size(); i++) {
InvDataset top = (InvDataset) parents.get(i);
datasets = top.getDatasets(); // dataset scans
for (int j = 0; j < datasets.size(); j++) {
InvDatasetScan ds = (InvDatasetScan) datasets.get(j);
if (ds.getPath() != null) {
dataLocation.put(ds.getPath(), ds.getScanLocation());
logServerStartup.info("path =" + ds.getPath() + " location =" + ds.getScanLocation());
}
ds.setXlinkHref(ds.getPath() + "/dataset.xml");
}
}
logServerStartup.info(getClass().getName() + " initialization complete ");
} // end init
// get pathInfo and parmameters from servlet call
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
PrintWriter pw = null;
try {
long startms = System.currentTimeMillis();
if (cat == null || rm.nexradList == null) { // something major wrong
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "radarServer Radar Station/Catalog initialization problem");
return;
}
// setup
String pathInfo = req.getPathInfo();
if (pathInfo == null) pathInfo = "";
RadarType radarType = RadarType.nexrad; //default
if (pathInfo.indexOf('/', 1) > 1) {
String rt = pathInfo.substring(1, pathInfo.indexOf('/', 1));
radarType = RadarType.valueOf(rt);
}
// default is xml, assume errors will be recorded by logger from this point
if (!pathInfo.endsWith("html")) {
pw = res.getWriter();
res.setContentType("text/xml; charset=iso-8859-1"); //default
}
// radar query
if (req.getQueryString() != null) {
//log.debug("RadarServer query ="+ req.getQueryString() );
if (log.isDebugEnabled()) log.debug("<documentation>\n" + req.getQueryString() + "</documentation>\n");
rm.radarQuery(radarType, req, res, pw);
if (log.isDebugEnabled()) log.debug("after doGet " + (System.currentTimeMillis() - startms));
pw.flush();
return;
}
// return radarCollections catalog xml or html
if (pathInfo.startsWith("/catalog.xml") || pathInfo.startsWith("/dataset.xml")) {
InvCatalogFactory factory = InvCatalogFactory.getDefaultFactory(false); // no validation
String catAsString = factory.writeXML(cat);
pw.println(catAsString);
res.setStatus(HttpServletResponse.SC_OK);
pw.flush();
return;
} else if (pathInfo.startsWith("/catalog.html") || pathInfo.startsWith("/dataset.html")) {
try {
int i = HtmlWriter.getInstance().writeCatalog(req, res, cat, true); // show catalog as HTML
} catch (Exception e) {
log.error("Radar HtmlWriter failed ", e);
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "radarServer HtmlWriter error " + pathInfo);
return;
}
return;
}
// level2 and level3 catalog/dataset
if (pathInfo.contains("level2/catalog.") || pathInfo.contains("level3/catalog.")
|| pathInfo.contains("level2/dataset.") || pathInfo.contains("level3/dataset.")) {
level2level3catalog(radarType, pathInfo, pw, req, res);
return;
}
// return stations of dataset
if (pathInfo.endsWith("stations.xml")) {
pathInfo = pathInfo.replace("/stations.xml", "");
Element rootElem = new Element("stationsList");
Document doc = new Document(rootElem);
doc = rm.stationsXML(radarType, doc, rootElem, pathInfo.substring(1));
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
pw.println(fmt.outputString(doc));
pw.flush();
return;
}
// return specific dataset information, ie IDD
if (pathInfo.endsWith("dataset.xml") || pathInfo.endsWith("catalog.xml")) {
datasetInfoXml(radarType, pathInfo, pw);
return;
}
// needs work nobody using it now
// return Dataset information in html form format
if (pathInfo.endsWith("dataset.html") || pathInfo.endsWith("catalog.html")) {
datasetInfoHtml(radarType, pathInfo, pw, res);
return;
}
// mal formed request with no exceptions
res.sendError(HttpServletResponse.SC_NOT_FOUND);
} catch (FileNotFoundException e) {
if ( ! res.isCommitted() ) res.sendError(HttpServletResponse.SC_NOT_FOUND);
} catch (Throwable e) {
log.error("RadarServer.doGet failed", e);
if ( ! res.isCommitted() ) res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
} // end doGet
private void level2level3catalog(RadarType radarType, String pathInfo, PrintWriter pw, HttpServletRequest req, HttpServletResponse res)
throws IOException {
try {
String type;
if (pathInfo.contains("level2"))
type = radarType.toString() + "/level2";
else
type = radarType.toString() + "/level3";
ByteArrayOutputStream os = new ByteArrayOutputStream(10000);
InvCatalogFactory factory = InvCatalogFactory.getDefaultFactory(false);
factory.writeXML(cat, os, true);
InvCatalogImpl tCat = factory.readXML(new ByteArrayInputStream(os.toByteArray()), catURI);
Iterator parents = tCat.getDatasets().iterator();
while (parents.hasNext()) {
ArrayList<InvDatasetImpl> delete = new ArrayList<InvDatasetImpl>();
InvDatasetImpl top = (InvDatasetImpl) parents.next();
Iterator tDatasets = top.getDatasets().iterator();
while (tDatasets.hasNext()) {
InvDatasetImpl ds = (InvDatasetImpl) tDatasets.next();
if (ds instanceof InvDatasetScan) {
InvDatasetScan ids = (InvDatasetScan) ds;
if (ids.getPath() == null)
continue;
if (ids.getPath().contains(type)) {
ids.setXlinkHref(ids.getPath() + "/dataset.xml");
} else {
delete.add(ds);
}
}
}
// remove datasets
for (InvDatasetImpl idi : delete) {
top.removeDataset(idi);
}
}
if (pathInfo.endsWith("xml")) {
String catAsString = factory.writeXML(tCat);
pw.println(catAsString);
pw.flush();
} else {
HtmlWriter.getInstance().writeCatalog(req, res, tCat, true); // show catalog as HTML
}
} catch (Throwable e) {
log.error("RadarServer.level2level3catalog failed", e);
if ( ! res.isCommitted() ) res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
return;
}
private void datasetInfoXml(RadarType radarType, String pathInfo, PrintWriter pw) throws IOException {
try {
pw.println("<catalog xmlns=\"http://www.unidata.ucar.edu/namespaces/thredds/InvCatalog/v1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" name=\"Radar Data\" version=\"1.0.1\">\n");
// add service
pw.println(" <service name=\"radarServer\" base=\"/thredds/radarServer/\" serviceType=\"DQC\" />\n");
pathInfo = pathInfo.replace("/dataset.xml", "");
pathInfo = pathInfo.replace("/catalog.xml", "");
if (pathInfo.startsWith("/"))
pathInfo = pathInfo.substring(1);
for (int i = 0; i < datasets.size(); i++) {
InvDatasetScan ds = (InvDatasetScan) datasets.get(i);
if (!(pathInfo.equals(ds.getPath()))) {
continue;
}
pw.println(" <dataset ID=\"" + ds.getID() + "\" serviceName=\"radarServer\">");
pw.println(" <urlpath>" + ds.getPath() + "</urlpath>");
pw.println(" <dataType>" + ds.getDataType() + "</dataType>");
pw.println(" <dataFormat>" + ds.getDataFormatType() + "</dataFormat>");
pw.println(" <serviceName>radarServer</serviceName>");
pw.println(" <metadata inherited=\"true\">");
pw.println(" <documentation type=\"summary\">" + ds.getSummary() +
"</documentation>");
CalendarDateRange dr = ds.getCalendarDateCoverage();
pw.println(" <TimeSpan>");
pw.print(" <start>");
if (pathInfo.contains("IDD")) {
pw.print(rm.getStartDateTime(ds.getPath()));
} else {
pw.print(dr.getStart().toString());
}
pw.println("</start>");
pw.println(" <end>" + dr.getEnd().toString() + "</end>");
pw.println(" </TimeSpan>");
ThreddsMetadata.GeospatialCoverage gc = ds.getGeospatialCoverage();
LatLonRect bb = new LatLonRect();
gc.setBoundingBox(bb);
pw.println(" <LatLonBox>");
pw.println(" <north>" + gc.getLatNorth() + "</north>");
pw.println(" <south>" + gc.getLatSouth() + "</south>");
pw.println(" <east>" + gc.getLonEast() + "</east>");
pw.println(" <west>" + gc.getLonWest() + "</west>");
pw.println(" </LatLonBox>");
ThreddsMetadata.Variables cvs = (ThreddsMetadata.Variables) ds.getVariables().get(0);
List vl = cvs.getVariableList();
pw.println(" <Variables>");
for (int j = 0; j < vl.size(); j++) {
ThreddsMetadata.Variable v = (ThreddsMetadata.Variable) vl.get(j);
pw.println(" <variable name=\"" + v.getName() + "\" vocabulary_name=\"" +
v.getVocabularyName() + "\" units=\"" + v.getUnits() + "\" />");
}
pw.println(" </Variables>");
String[] stations = rm.stationsDS(radarType, dataLocation.get(ds.getPath()));
rm.printStations(stations, pw, radarType );
pw.println(" </metadata>");
pw.println(" </dataset>");
}
pw.println("</catalog>");
pw.flush();
} catch (Throwable e) {
log.error("RadarServer.datasetInfoXml", e);
}
return;
}
private void datasetInfoHtml(RadarType radarType, String pathInfo, PrintWriter pw, HttpServletResponse res)
throws IOException {
pathInfo = pathInfo.replace("/dataset.html", "");
pathInfo = pathInfo.replace("/catalog.html", "");
Element root = new Element("RadarNexrad");
Document doc = new Document(root);
if (pathInfo.startsWith("/"))
pathInfo = pathInfo.substring(1);
for (int i = 0; i < datasets.size(); i++) {
InvDatasetScan ds = (InvDatasetScan) datasets.get(i);
if (!(pathInfo.equals(ds.getPath()))) {
continue;
}
// at this point a valid dataset
// fix the location
root.setAttribute("location", "/thredds/radarServer/" + ds.getPath());
// spatial range
ThreddsMetadata.GeospatialCoverage gc = ds.getGeospatialCoverage();
LatLonRect bb = new LatLonRect();
gc.setBoundingBox(bb);
String north = Double.toString(gc.getLatNorth());
String south = Double.toString(gc.getLatSouth());
String east = Double.toString(gc.getLonEast());
String west = Double.toString(gc.getLonWest());
Element LatLonBox = new Element("LatLonBox");
LatLonBox.addContent(new Element("north").addContent(north));
LatLonBox.addContent(new Element("south").addContent(south));
LatLonBox.addContent(new Element("east").addContent(east));
LatLonBox.addContent(new Element("west").addContent(west));
root.addContent(LatLonBox);
// get the time range
Element timeSpan = new Element("TimeSpan");
CalendarDateRange dr = ds.getCalendarDateCoverage();
timeSpan.addContent(new Element("begin").addContent(dr.getStart().toString()));
timeSpan.addContent(new Element("end").addContent(dr.getEnd().toString()));
root.addContent(timeSpan);
ThreddsMetadata.Variables cvs = (ThreddsMetadata.Variables) ds.getVariables().get(0);
List vl = cvs.getVariableList();
for (int j = 0; j < vl.size(); j++) {
ThreddsMetadata.Variable v = (ThreddsMetadata.Variable) vl.get(j);
Element variable = new Element("variable");
variable.setAttribute("name", v.getName());
root.addContent(variable);
}
// add pointer to the station list XML
/*
Element stnList = new Element("stationList");
stnList.setAttribute("title", "Available Stations", XMLEntityResolver.xlinkNS);
stnList.setAttribute("href", "/thredds/radarServer/"+ pathInfo +"/stations.xml",
XMLEntityResolver.xlinkNS);
root.addContent(stnList);
*/
//String[] stations = rns.stationsDS( dataLocation.get( ds.getPath() ));
//rns.printStations( stations );
// add accept list
Element a = new Element("AcceptList");
a.addContent(new Element("accept").addContent("xml"));
a.addContent(new Element("accept").addContent("html"));
root.addContent(a);
}
ServerMethods sm = new ServerMethods(log);
InputStream xslt = sm.getInputStream(contentPath + getPath() + "radar.xsl", RadarServer.class);
try {
// what's wrong here xslt = getXSLT( "radar.xsl" );
XSLTransformer transformer = new XSLTransformer(xslt);
Document html = transformer.transform(doc);
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
String infoString = fmt.outputString(html);
res.setContentType("text/html; charset=iso-8859-1");
pw = res.getWriter();
pw.println(infoString);
pw.flush();
} catch (Exception e) {
log.error("radarServer reading " + contentPath + getPath() + "radar.xsl");
log.error("radarServer XSLTransformer problem for web form ", e);
}
finally {
if (xslt != null) {
try {
xslt.close();
}
catch (IOException e) {
log.error("radarServer radar.xsl: error closing" +
contentPath + getPath() + "radar.xsl");
}
}
}
return;
}
/**
* Does the actual work of reading a catalog.
*
* @param factory use this InvCatalogFactory
* @param path reletive path starting from content root
* @param catalogFullPath absolute location on disk
* @return the InvCatalogImpl, or null if failure
*/
private InvCatalogImpl readCatalog(InvCatalogFactory factory, String path, String catalogFullPath) {
InvCatalogImpl acat;
try {
catURI = new URI("file:" + StringUtil2.escape(catalogFullPath, "/:-_.")); // LOOK needed ?
}
catch (URISyntaxException e) {
logServerStartup.info("radarServer readCatalog(): URISyntaxException=" + e.getMessage());
return null;
}
// read the catalog
logServerStartup.info("radarServer readCatalog(): full path=" + catalogFullPath + "; path=" + path);
FileInputStream ios = null;
try {
ios = new FileInputStream(catalogFullPath);
acat = factory.readXML(ios, catURI);
} catch (Throwable t) {
logServerStartup.error("radarServer readCatalog(): Exception on catalog=" +
catalogFullPath + " " + t.getMessage()); //+"\n log="+cat.getLog(), t);
return null;
}
finally {
if (ios != null) {
try {
ios.close();
}
catch (IOException e) {
logServerStartup.info("radarServer readCatalog(): error closing" + catalogFullPath);
}
}
}
return acat;
}
private InputStream getXSLT(String xslName) throws IOException {
return getClass().getResourceAsStream("/resources/xsl/" + xslName);
}
public static void main(String args[]) throws IOException {
// Function References
String path = "http://motherlode.ucar.edu:8081/thredds/radarServer/nexrad/level3/IDD/dataset.xml";
try {
catURI = new URI(StringUtil2.escape(path, "/:-_."));
}
catch (URISyntaxException e) {
System.out.println("radarServer main: URISyntaxException=" + e.getMessage());
return;
}
// read the catalog
System.out.println("radarServer main: full path=" + path);
InputStream ios = null;
try {
URL url = new URL(path);
ios = url.openStream();
//ios = new FileInputStream(path);
//acat = factory.readXML(ios, catURI );
BufferedReader dataIS =
new BufferedReader(new InputStreamReader(ios));
while (true) {
String line = dataIS.readLine();
if (line == null)
break;
System.out.println(line);
}
} catch (Throwable t) {
System.out.println("radarServer main: Exception on catalog=" +
path + " " + t.getMessage());
return;
}
finally {
if (ios != null) {
try {
ios.close();
}
catch (IOException e) {
System.out.println("radarServer main: error closing" + path);
}
}
}
return;
}
} // end RadarServer