package edu.sc.seis.sod.source.network; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; import org.w3c.dom.Element; import edu.iris.Fissures.IfNetwork.ChannelNotFound; import edu.iris.Fissures.IfNetwork.Instrumentation; import edu.iris.Fissures.IfNetwork.NetworkId; import edu.iris.Fissures.IfNetwork.NetworkNotFound; import edu.iris.Fissures.model.ISOTime; import edu.iris.Fissures.model.MicroSecondDate; import edu.iris.Fissures.model.QuantityImpl; import edu.iris.Fissures.model.TimeInterval; import edu.iris.Fissures.model.UnitImpl; import edu.iris.Fissures.network.ChannelIdUtil; import edu.iris.Fissures.network.ChannelImpl; import edu.iris.Fissures.network.InstrumentationImpl; import edu.iris.Fissures.network.NetworkAttrImpl; import edu.iris.Fissures.network.NetworkIdUtil; import edu.iris.Fissures.network.StationIdUtil; import edu.iris.Fissures.network.StationImpl; import edu.sc.seis.fissuresUtil.cache.CacheNetworkAccess; import edu.sc.seis.fissuresUtil.display.configuration.DOMHelper; import edu.sc.seis.fissuresUtil.sac.InvalidResponse; import edu.sc.seis.fissuresUtil.stationxml.ChannelSensitivityBundle; import edu.sc.seis.fissuresUtil.stationxml.StationChannelBundle; import edu.sc.seis.fissuresUtil.stationxml.StationXMLToFissures; import edu.sc.seis.fissuresUtil.time.MicroSecondTimeRange; import edu.sc.seis.fissuresUtil.time.RangeTool; import edu.sc.seis.seisFile.fdsnws.FDSNStationQueryParams; import edu.sc.seis.seisFile.fdsnws.stationxml.Channel; import edu.sc.seis.seisFile.fdsnws.stationxml.FDSNStationXML; import edu.sc.seis.seisFile.fdsnws.stationxml.Network; import edu.sc.seis.seisFile.fdsnws.stationxml.NetworkIterator; import edu.sc.seis.seisFile.fdsnws.stationxml.Station; import edu.sc.seis.seisFile.fdsnws.stationxml.StationIterator; import edu.sc.seis.seisFile.fdsnws.stationxml.StationXMLException; import edu.sc.seis.sod.ConfigurationException; import edu.sc.seis.sod.SodUtil; @Deprecated public class StationXML extends AbstractNetworkSource implements NetworkSource { public StationXML(Element config) throws ConfigurationException { super(config); if (DOMHelper.hasElement(config, URL_ELEMENT)) { url = SodUtil.getNestedText(SodUtil.getElement(config, URL_ELEMENT)); checkForOldIrisWebService(url); } if(DOMHelper.hasElement(config, AbstractNetworkSource.REFRESH_ELEMENT)) { refreshInterval = SodUtil.loadTimeInterval(SodUtil.getElement(config, AbstractNetworkSource.REFRESH_ELEMENT)); } else { refreshInterval = new TimeInterval(1, UnitImpl.FORTNIGHT); } parseURL(); } private void checkForOldIrisWebService(String url2) throws ConfigurationException { if (url2.startsWith("http://www.iris.edu/ws")) { throw new ConfigurationException("This URL appears to point to the deprecated IRIS DMC station web service. " +"This uses the older stationXML schema which is no longer supported by SOD. Please use the new FDSN Station " +"web service with the <fdsnStation> network source."); } } void parseURL() throws ConfigurationException { try { parsedURL = new URI(url); List<String> split = new ArrayList<String>(); if (parsedURL.getQuery() != null) { String[] splitArray = parsedURL.getQuery().split("&"); for (String s : splitArray) { String[] nvSplit = s.split("="); if (!nvSplit[0].equals("level")) { // zap level as we do that ourselves split.add(s); } } String newQuery = ""; boolean first = true; for (String s : split) { if (!first) { newQuery += "&"; } newQuery += s; first = false; } parsedURL = new URI(parsedURL.getScheme(), parsedURL.getUserInfo(), parsedURL.getHost(), parsedURL.getPort(), parsedURL.getPath(), newQuery, parsedURL.getFragment()); } } catch(URISyntaxException e) { throw new ConfigurationException("Invalid <url> element found.", e); } } public String getDNS() { return url; } public String getName() { return this.getClass().getName(); } public TimeInterval getRefreshInterval() { return refreshInterval; } public CacheNetworkAccess getNetwork(NetworkAttrImpl attr) { return new CacheNetworkAccess(null, attr); } public List<? extends CacheNetworkAccess> getNetworkByName(String name) throws NetworkNotFound { throw new NetworkNotFound(); } public List<? extends NetworkAttrImpl> getNetworks() { checkNetsLoaded(); return Collections.unmodifiableList(networks); } public List<? extends StationImpl> getStations(NetworkAttrImpl net) { checkChansLoaded(NetworkIdUtil.toStringNoDates(net)); List<StationChannelBundle> bundles = staChanMap.get(NetworkIdUtil.toStringNoDates(net)); List<StationImpl> out = new ArrayList<StationImpl>(); for (StationChannelBundle b : bundles) { out.add(b.getStation()); } return out; } public List<? extends ChannelImpl> getChannels(StationImpl station) { checkChansLoaded(NetworkIdUtil.toStringNoDates(station.getNetworkAttr())); List<StationChannelBundle> bundles = staChanMap.get(NetworkIdUtil.toStringNoDates(station.getNetworkAttr())); for (StationChannelBundle b : bundles) { if (StationIdUtil.areEqual(station, b.getStation())) { List<ChannelImpl> out = new ArrayList<ChannelImpl>(); for (ChannelSensitivityBundle chanSens : b.getChanList()) { out.add(chanSens.getChan()); } return out; } } return new ArrayList<ChannelImpl>(); } public QuantityImpl getSensitivity(ChannelImpl chan) throws ChannelNotFound, InvalidResponse { checkChansLoaded(NetworkIdUtil.toStringNoDates(chan.getId().network_id)); List<StationChannelBundle> bundles = staChanMap.get(NetworkIdUtil.toStringNoDates(chan.getId().network_id)); for (StationChannelBundle b : bundles) { if (chan.getId().station_code.equals( b.getStation().get_code())) { for (ChannelSensitivityBundle chanSens : b.getChanList()) { if (ChannelIdUtil.areEqual(chan.getId(), chanSens.getChan().get_id()) && chanSens.getSensitivity() != null) { return chanSens.getSensitivity(); } } } } throw new ChannelNotFound(); } public Instrumentation getInstrumentation(ChannelImpl chan) throws ChannelNotFound, InvalidResponse { MicroSecondDate chanBegin = new MicroSecondDate(chan.getId().begin_time); String newQuery = FDSNStationQueryParams.NETWORK+"="+chan.getId().network_id.network_code+ "&"+FDSNStationQueryParams.STATION+"="+chan.getId().station_code+ "&"+FDSNStationQueryParams.LOCATION+"="+chan.getId().site_code+ "&"+FDSNStationQueryParams.CHANNEL+"="+chan.getId().channel_code+ "&"+FDSNStationQueryParams.STARTTIME+"="+toDateString(chanBegin)+ "&"+FDSNStationQueryParams.ENDTIME+"="+toDateString(chanBegin.add(ONE_DAY)); try { URI chanUri = new URI(parsedURL.getScheme(), parsedURL.getUserInfo(), parsedURL.getHost(), parsedURL.getPort(), parsedURL.getPath(), newQuery, parsedURL.getFragment()); FDSNStationXML sm = retrieveXML(chanUri, "resp"); NetworkIterator netIt = sm.getNetworks(); while (netIt.hasNext()) { Network n = netIt.next(); StationIterator staIt = n.getStations(); while (staIt.hasNext()) { Station s = staIt.next(); for (Channel c : s.getChannelList()) { InstrumentationImpl inst = StationXMLToFissures.convertInstrumentation(c); if (RangeTool.areOverlapping(new MicroSecondTimeRange(inst.effective_time), new MicroSecondTimeRange(chanBegin.add(ONE_SECOND), chanBegin.add(ONE_DAY)))) { return inst; } logger.debug("Skipping as wrong start time "+ChannelIdUtil.toString(chan.getId())+" "+inst.effective_time.start_time.date_time+" "+inst.effective_time.end_time.date_time); } } } } catch(URISyntaxException e) { throw new InvalidResponse("StationXML URL is not valid, should not happen but it did.", e); } catch(XMLStreamException e) { throw new InvalidResponse("Problem getting response via stationxml.", e); } catch(StationXMLException e) { throw new InvalidResponse("Problem getting response via stationxml.", e); } catch(IOException e) { throw new InvalidResponse("Problem getting response via stationxml.", e); } throw new ChannelNotFound(); } public void setConstraints(NetworkQueryConstraints constraints) { this.constraints = constraints; } synchronized void checkNetsLoaded() { if (networks == null) { try { parseNets(); } catch(Exception e) { throw new RuntimeException(e); } } } synchronized void checkChansLoaded(String netCode) { if (staChanMap.get(netCode) == null) { checkNetsLoaded(); try { parse(); } catch(Exception e) { throw new RuntimeException(e); } } } static FDSNStationXML retrieveXML(URI u, String level) throws XMLStreamException, StationXMLException, IOException, URISyntaxException { URI chanUri = new URI(u.getScheme(), u.getUserInfo(), u.getHost(), u.getPort(), u.getPath(), u.getQuery()+"&level="+level, u.getFragment()); logger.info("Retrieve from "+chanUri); URLConnection urlConn = chanUri.toURL().openConnection(); if (urlConn instanceof HttpURLConnection) { HttpURLConnection conn = (HttpURLConnection)urlConn; if (conn.getResponseCode() == 204) { // no data return retrieveXML(new ByteArrayInputStream(EMPTY_STATIONXML)); } else if (conn.getResponseCode() != 200) { String out = ""; BufferedReader errReader = null; try { errReader = new BufferedReader(new InputStreamReader(conn.getErrorStream())); for (String line; (line = errReader.readLine()) != null;) { out += line + "\n"; } } finally { if (errReader != null) try { errReader.close(); conn.disconnect(); } catch (IOException e) { throw e; } } throw new StationXMLException("Error in connection with url: "+chanUri+" "+out); } } return retrieveXML(urlConn.getInputStream()); } static FDSNStationXML retrieveXML(InputStream inStream) throws XMLStreamException, StationXMLException, IOException, URISyntaxException { InputStream in = new BufferedInputStream(inStream); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader r = factory.createXMLEventReader(in); XMLEvent e = r.peek(); while(! e.isStartElement()) { e = r.nextEvent(); // eat this one e = r.peek(); // peek at the next } return new FDSNStationXML(r); } synchronized void parseNets() throws XMLStreamException, StationXMLException, IOException, URISyntaxException { networks = new ArrayList<NetworkAttrImpl>(); logger.info("Parsing networks from "+parsedURL); FDSNStationXML stationXML = retrieveXML(parsedURL, FDSNStationQueryParams.LEVEL_NETWORK); NetworkIterator netIt = stationXML.getNetworks(); while (netIt.hasNext()) { Network net = netIt.next(); networks.add(StationXMLToFissures.convert(net)); StationIterator it = net.getStations(); // should be empty, but just make sure while(it.hasNext()) { Station s = it.next(); } } stationXML.closeReader(); logger.info("found "+networks.size()+" networks after parse"); } synchronized void parse() throws XMLStreamException, StationXMLException, IOException, URISyntaxException { staChanMap.clear(); int numChannels = 0; logger.info("Parsing channels from "+parsedURL); FDSNStationXML stationXML = retrieveXML(parsedURL, FDSNStationQueryParams.LEVEL_CHANNEL); lastLoadDate = stationXML.getCreated(); NetworkIterator netIt = stationXML.getNetworks(); while (netIt.hasNext()) { Network net = netIt.next(); String key = NetworkIdUtil.toStringNoDates(StationXMLToFissures.convert(net).getId()); if ( ! staChanMap.containsKey(key)) { staChanMap.put(key, new ArrayList<StationChannelBundle>()); } StationIterator it = net.getStations(); while(it.hasNext()) { Station s = it.next(); try { numChannels += processStation(networks, s); } catch (StationXMLException ee) { logger.error("Skipping "+s.getNetworkCode()+"."+s.getCode()+" "+ ee.getMessage()); } } } logger.info("found "+ numChannels+" channels in "+networks.size()+" networks after parse "); } int processStation(List<NetworkAttrImpl> netList, Station s) throws StationXMLException { int numChannels = 0; for (String ignore : ignoreNets) { if (s.getNetworkCode().equals(ignore)) { // not sure what AB network is, skip it for now return 0; } } List<StationChannelBundle> bundles = StationXMLToFissures.convert(s, netList, true); for (StationChannelBundle b : bundles) { String staKey = NetworkIdUtil.toStringNoDates(b.getStation().getNetworkAttr()); if ( ! staChanMap.containsKey(staKey)) { staChanMap.put(staKey, new ArrayList<StationChannelBundle>()); } staChanMap.get(staKey).add(b); numChannels += b.getChanList().size(); } return numChannels; } public static String toDateString(MicroSecondDate msd) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); df.setTimeZone(ISOTime.UTC); return df.format(msd); } NetworkQueryConstraints constraints; static String[] ignoreNets = new String[] {"AB", "AI", "BN"}; List<NetworkAttrImpl> knownNetworks = new ArrayList<NetworkAttrImpl>();; List<NetworkAttrImpl> networks; Map<String, List<StationChannelBundle>> staChanMap = new HashMap<String, List<StationChannelBundle>>(); String url; URI parsedURL; TimeInterval refreshInterval; String lastLoadDate; public static final TimeInterval ONE_SECOND = new TimeInterval(1, UnitImpl.SECOND); public static final TimeInterval ONE_DAY = new TimeInterval(1, UnitImpl.DAY); private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(StationXML.class); public static final String URL_ELEMENT = "url"; public static final byte[] EMPTY_STATIONXML = "<StaMessage xsi:schemaLocation=\"http://www.data.scec.org/xml/station/20120307/ http://www.data.scec.org/xml/station/20120307/station.xsd http://www.iris.edu/ws/schemas/station/2012/03/22/ http://www.iris.edu/ws/schemas/station/2012/03/22/station_comments.xsd\"/>".getBytes(); }