/*
* 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.
*/
/**
* User: rkambic
* Date: Oct 14, 2010
* Time: 12:15:33 PM
*/
package thredds.server.radarServer;
import org.springframework.stereotype.Component;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import thredds.catalog.InvCatalogFactory;
import thredds.catalog.InvCatalogImpl;
import thredds.catalog.InvDataset;
import thredds.catalog.InvDatasetScan;
import thredds.catalog.query.Location;
import thredds.catalog.query.SelectStation;
import thredds.catalog.query.Station;
import thredds.server.config.TdsContext;
import thredds.servlet.DataRootHandler;
import ucar.unidata.util.StringUtil2;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component
public class DatasetRepository {
public enum RadarType {
nexrad, terminal
}
static private org.slf4j.Logger startupLog = org.slf4j.LoggerFactory.getLogger("serverStartup");
static public InvCatalogImpl cat = null;
static public URI catURI;
static public HashMap<String, RadarDatasetCollection> datasetMap = new HashMap<>();
static public HashMap<String, String> dataLocation = new HashMap<>();
static private final String catName = "/radar/radarCollections.xml";
static private final String nexradStations = "/radar/RadarNexradStations.xml";
static private final String terminalStations = "/radar/RadarTerminalStations.xml";
static public List<Station> nexradList = new ArrayList<Station>();
static public List<Station> terminalList = new ArrayList<Station>();
static public HashMap<String, Station> nexradMap;
static public HashMap<String, Station> terminalMap;
static private boolean init;
static private Object lock = new Object();
/*
* Reads the Radar Server catalog and Radar Station information, called by bean
*/
static public void init(TdsContext tdsContext) {
if (init) return;
String contentPath = tdsContext.getContentDirectory().getPath();
// read in radarCollections.xml catalog no validation
InvCatalogFactory factory = InvCatalogFactory.getDefaultFactory(false);
cat = readCatalog(factory, catName, contentPath + catName);
if (cat == null) {
throw new IllegalStateException("Radar DatasetRepository cant open " + contentPath + catName);
}
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);
List datasets = top.getDatasets(); // dataset scans
for (int j = 0; j < datasets.size(); j++) {
InvDatasetScan ds = (InvDatasetScan) datasets.get(j);
if (ds.getPath() != null) {
String locationWithAliasRemoved = DataRootHandler.getInstance().expandAliasForDataRoot(ds.getScanLocation());
dataLocation.put(ds.getPath(), locationWithAliasRemoved);
startupLog.info("Radar DatasetRepository added path =" + ds.getPath() + " location =" + locationWithAliasRemoved);
}
ds.setXlinkHref(ds.getPath() + "/dataset.xml");
}
}
if (nexradList.size() == 0) {
nexradList = readStations(contentPath + nexradStations);
if (nexradList == null) {
startupLog.error("Station initialization problem using " + contentPath + nexradStations);
throw new IllegalStateException("Radar DatasetRepository cant open " + contentPath + nexradStations);
}
terminalList = readStations(contentPath + terminalStations);
if (terminalList == null) {
startupLog.error("Station initialization problem using " + contentPath + terminalStations);
throw new IllegalStateException("Radar DatasetRepository cant open " + contentPath + terminalStations);
}
nexradMap = getStationMap(nexradList);
terminalMap = getStationMap(terminalList);
}
startupLog.info("DatasetRepository initialization done - ");
init = true;
}
public static class RadarDatasetCollectionReturn {
RadarDatasetCollection rdc;
String err;
public RadarDatasetCollectionReturn(RadarDatasetCollection rdc) {
this.rdc = rdc;
}
public RadarDatasetCollectionReturn(String err) {
this.err = err;
}
}
/**
* Reads/stores requested dataset
*
* @param key dataset location
* @param var if level3, the var dataset
* @return Datset Collection
*/
public static RadarDatasetCollectionReturn getRadarDatasetCollection(String key, String var) {
String dmkey = key;
if (var != null)
dmkey = key + var;
RadarDatasetCollection rdc = datasetMap.get(dmkey);
boolean reread = false;
if (rdc != null)
reread = rdc.previousDayNowAvailable();
if (rdc == null || reread) { // need to read or reread dataset
synchronized (lock) {
if (reread) { // remove dataset
datasetMap.remove(dmkey);
rdc = null;
} else {
rdc = datasetMap.get(dmkey);
}
if (rdc != null)
return new RadarDatasetCollectionReturn(rdc);
String tdir = dataLocation.get(key);
if (tdir == null)
return new RadarDatasetCollectionReturn("No dataset with key= "+key);
rdc = new RadarDatasetCollection(tdir, var);
if (rdc.yyyymmdd.size() == 0 )
return new RadarDatasetCollectionReturn("No dataset with yyyymmdd= "+rdc.yyyymmdd);
if (rdc.hhmm.size() == 0)
return new RadarDatasetCollectionReturn("No dataset with hhmm= "+rdc.hhmm);
datasetMap.put(dmkey, rdc);
}
}
return new RadarDatasetCollectionReturn(rdc);
}
/**
* Removes dataset
*
* @param key dataset location
* @param var if level3, the var dataset
*/
public static void removeRadarDatasetCollection(String key, String var) {
Object sync = new Object();
synchronized (sync) {
if (var != null)
datasetMap.remove(key + var);
else
datasetMap.remove(key);
}
}
public static InvCatalogImpl getRadarCatalog() {
return cat;
}
/**
* 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
*/
public static InvCatalogImpl readCatalog(InvCatalogFactory factory, String path, String catalogFullPath) {
InvCatalogImpl acat;
try {
catURI = new URI("file:" + StringUtil2.escape(catalogFullPath, "/:-_.")); // LOOK needed ?
//catURI = new URI("file:" + catalogFullPath);
} catch (URISyntaxException e) {
startupLog.info("radarServer readCatalog(): URISyntaxException=" + e.getMessage());
return null;
}
// read the catalog
startupLog.info("radarServer readCatalog(): full path=" + catalogFullPath + "; path=" + path);
FileInputStream ios = null;
try {
ios = new FileInputStream(catalogFullPath);
acat = factory.readXML(ios, catURI);
} catch (Throwable t) {
startupLog.info("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) {
startupLog.info("radarServer readCatalog(): error closing" + catalogFullPath);
}
}
}
return acat;
}
/**
* Returns the stations from a (nexrad|terminal)Stations.xml file
*
* @param stnLocation TDS servers location
* @return
*/
public static List<Station> readStations(String stnLocation) {
List<Station> stationList = new ArrayList<Station>();
DocumentBuilder parser;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
SelectStation parent = new SelectStation(); //kludge to use Station
InputStream is = null;
try {
parser = factory.newDocumentBuilder();
//stnLocation = stnLocation.replaceAll( " ", "%20");
is = new FileInputStream(stnLocation);
org.w3c.dom.Document doc = parser.parse(is);
//System.out.println( "root=" + doc.getDocumentElement().getTagName() );
NodeList stns = doc.getElementsByTagName("station");
for (int i = 0; i < stns.getLength(); i++) {
//System.out.println( "node=" + d.item( i ).getNodeName() );
NamedNodeMap attr = stns.item(i).getAttributes();
String name = "", value = "", state = "", country = "";
for (int j = 0; j < attr.getLength(); j++) {
if (attr.item(j).getNodeName().equals("value")) {
value = attr.item(j).getNodeValue();
} else if (attr.item(j).getNodeName().equals("name")) {
name = attr.item(j).getNodeValue();
} else if (attr.item(j).getNodeName().equals("state")) {
state = attr.item(j).getNodeValue();
} else if (attr.item(j).getNodeName().equals("country")) {
country = attr.item(j).getNodeValue();
}
}
NodeList child = stns.item(i).getChildNodes(); //Children of station
Location location = null;
for (int j = 0; j < child.getLength(); j++) {
//System.out.println( "child =" + child.item( j ).getNodeName() );
if (child.item(j).getNodeName().equals("location3D")) {
NamedNodeMap ca = child.item(j).getAttributes();
String latitude = "", longitude = "", elevation = "";
for (int k = 0; k < ca.getLength(); k++) {
if (ca.item(k).getNodeName().equals("latitude")) {
latitude = ca.item(k).getNodeValue();
} else if (ca.item(k).getNodeName().equals("longitude")) {
longitude = ca.item(k).getNodeValue();
} else if (ca.item(k).getNodeName().equals("elevation")) {
elevation = ca.item(k).getNodeValue();
}
}
location = new Location(latitude, longitude, elevation, null, null, null);
}
}
Station station = new Station(parent, name, value, state, country, null);
station.setLocation(location);
stationList.add(station);
}
} catch (SAXException e) {
e.printStackTrace();
stationList = null;
} catch (IOException e) {
e.printStackTrace();
stationList = null;
} catch (ParserConfigurationException e) {
e.printStackTrace();
stationList = null;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
startupLog.error("radarServer getStations(): error closing" + stnLocation);
}
}
}
return stationList;
}
/**
* creates a HashMap of Stations from a List
*
* @param list
* @return Stations HashMap
*/
public static HashMap<String, Station> getStationMap(List<Station> list) {
HashMap<String, Station> stationMap = new HashMap<String, Station>();
for (Station station : list) {
stationMap.put(station.getValue(), station);
}
return stationMap;
}
}