All Downloads are FREE. Search and download functionalities are using the official Maven repository.

decodes.util.Pdt Maven / Gradle / Ivy

Go to download

A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.

The newest version!
/*
*  $Id$
*
*  This is open-source software written by ILEX Engineering, Inc., under
*  contract to the federal government. You are free to copy and use this
*  source code for your own purposes, except that no part of the information
*  contained in this file may be claimed to be proprietary.
*
*  Except for specific contractual terms between ILEX and the federal 
*  government, this source code is provided completely without warranty.
*  For more information contact: [email protected]
*/
package decodes.util;

import java.util.Date;
import java.util.HashMap;
import java.util.Collection;
import java.util.Random;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;

import ilex.util.EnvExpander;
import ilex.util.Logger;
import lrgs.common.DcpAddress;

/**
This class holds the Platform Description Table.
*/
public class Pdt
{
	/** Map of DCP Address to PDT Entry */
	private HashMap pdtMap;
	private static Pdt _instance = null;
	public static long downloadIntervalMsec = 24 * 3600 * 1000L; // 24 hours
	public static long fileCheckIntervalMsec = 30 * 60 * 1000L;  // 30 minutes
	public static boolean useLockForDownload = false;
	private boolean _isLoaded = false;
	private PdtMaintenanceThread mthread = null;
	private PdtLoadListener pdtLoadListener = null;

	/** For singleton, use instance() method. */
	public Pdt()
	{
		pdtMap = new HashMap();
	}

	/** @return the singleton instance of the PDT. */
	public static Pdt instance()
	{
		if (_instance == null)
			_instance = new Pdt();
		return _instance;
	}

	/**
	 * Loads a PDT file into memory.
	 * If an IO error occurs, the pdtSched is restored to what is was before
	 * the call to this method.
	 * @param file the file to load.
	 * @return true if load was successful, false if not.
	 */
	public synchronized boolean load(File file)
	{
		Logger.instance().debug1("Loading PDT from file '" + file.getPath() + "'");

		HashMap tmpPdtMap = new HashMap();

		int badLines = 0;
		try
		{
			LineNumberReader lnr = new LineNumberReader(new FileReader(file));
			String line;
			while( (line = lnr.readLine() ) != null)
			{
				if (line.length() > 0 && line.charAt(0) == '\f')
					line = line.substring(1);
				try
				{
					PdtEntry pdtEntry = new PdtEntry(line);
					tmpPdtMap.put(pdtEntry.dcpAddress, pdtEntry);
				}
				catch(BadPdtEntryException ex)
				{
					Logger.instance().warning(
						"Bad PDT line " + lnr.getLineNumber() + ": " + ex);
					badLines++;
				}
			}
			lnr.close();
		}
		catch(IOException ex)
		{
			Logger.instance().warning("IO Error reading PDT File '" 
				+ file.getPath() + "': " + ex + " -- Old PDT restored.");
			return false;
		}
		pdtMap = tmpPdtMap;
		Logger.instance().debug1(
			"Parsed PDT File '" + file.getPath() + "' - " + pdtMap.size()
			+ " entries, " + badLines + " unparsable lines.");
		_isLoaded = true;
		return true;
	}
	
	public boolean isLoaded() { return _isLoaded; }

	
	/**
	 * Adds a new entry to the hashmap.
	 * @param entry
	 */
	public void put(PdtEntry entry)
	{
		pdtMap.put(entry.dcpAddress, entry);
	}

	/**
	 * Retrieve the PdtSchedEntry for the specified DCP address.
	 * @param dcpAddr the DCP Address
	 * @return the PdtSchedEntry or null if there is none.
	*/
	public synchronized PdtEntry find(DcpAddress dcpAddr)
	{
		return pdtMap.get(dcpAddr);
	}

	public int size() { return pdtMap.size(); }
	
	/**
	 * Starts a background thread the periodically downloads the PDT from
	 * a specified URL and independently checks a local file for changes.
	 * The URL is downloaded every 2 hours.
	 * The file is checked for changes every 10 minutes.
	 * @param pdtUrl the URL to download PDT from, null if no download.
	 * @param localfn - the local file to check periodically for changes.
	 */
	public void startMaintenanceThread(String pdtUrl, String localfn)
	{
		if (mthread == null)
		{
			mthread = new PdtMaintenanceThread(this, pdtUrl, localfn);
			mthread.start();
		}
	}
	
	/**
	 * Stops the maintenance thread and destroys the singleton instance.
	 */
	public void stopMaintenanceThread()
	{
		Logger.instance().debug3("Pdt.stopMaintenanceThread()");
		if (mthread != null)
		{
			mthread.shutdown = true;
			mthread.interrupt();
		}
		mthread = null;
		_instance = null;
	}

	private class PdtMaintenanceThread extends Thread
	{
		String localfn;
		File pdtfile;
		String url;
		long lastLoad = 0L;
		long lastDownload = 0L;
		Pdt pdt = null;
		boolean shutdown = false;
		Random random = new Random();

		PdtMaintenanceThread(Pdt pdt, String url, String fn)
		{
			super("PdtMaintenanceThread");
			this.pdt = pdt;
			this.localfn = fn;
			pdtfile = new File(EnvExpander.expand(fn));
			this.url = url;
		}

		public void run()
		{
			if (pdtfile.canRead())
				lastDownload = pdtfile.lastModified();

			Logger.instance().debug1("Starting PDT Maintenance Thread, url='"
				+ url + "', localfile='" + localfn + "'"
				+ ", localpath=" + pdtfile.getPath() + ", lastDownload=" 
				+ new Date(lastDownload) + ", shutdown="+shutdown);
			
			while(!shutdown)
			{
				if (pdtfile.lastModified() > lastLoad)
				{
					lastLoad = System.currentTimeMillis();
					pdt.load(pdtfile);
				}
				long now = System.currentTimeMillis();
				if (url != null && url.length() > 0 && !url.equals("-")
				 && now - lastDownload > downloadIntervalMsec)
				{
Logger.instance().debug1("Starting download. lastDownload=" + lastDownload
+ ", now=" + now + ", intv=" + downloadIntervalMsec + ", dT=" + (now - lastDownload));
					lastDownload = System.currentTimeMillis();
					DownloadPdtThread lpt = 
						new DownloadPdtThread(url, localfn, pdt);
					lpt.start();
				}
				else if (pdtLoadListener != null)
				{
					pdtLoadListener.pdtLoaded();
					pdtLoadListener = null;
				}
				
				// 30 min + random # seconds between 0...31
				long interval = fileCheckIntervalMsec + (random.nextInt() & 0x1f) * 1000L;
				try { sleep(interval); }
				catch(InterruptedException ex) {}
			}
		}
	}
	
	public void printMe()
	{
		System.out.println("Total Lines: "+ pdtMap.size());
		for(PdtEntry pdtEntry : pdtMap.values())
			System.out.println(pdtEntry.printToLine());
	}
	
	public synchronized void save(File myfile)
	{
		try
		{
			FileWriter mywriter = new FileWriter(myfile);
			int nlines = 0;
			for(PdtEntry pdtEntry : pdtMap.values())
			{
				mywriter.write(pdtEntry.printToLine());
				mywriter.write("\n");
				nlines++;
			}
			mywriter.close();
			Logger.instance().info(
			    "Pdt saved to '" + myfile.getPath() + "' -- " + nlines
			        + " entries.");
		}
		catch (Exception ex)
		{
			String msg = "Pdt.save failed: " + ex;
			Logger.instance().failure(msg);
			System.err.println(msg);
			ex.printStackTrace(System.err);
		}
	}
	
	/**
	 * Returns the collection of PDT Entries, in no particular order.
	 * The collection here is stored inside a HashMap, so do not modify the 
	 * returned collection.
	 * @return the collection of PDT Entries, in no particular order.
	 */
	public Collection getEntries()
	{
		return pdtMap.values();
	}

	/**
	 * args: url localfile
	 */
	public static void main(String args[])
		throws Exception
	{
		Logger.instance().setMinLogPriority(Logger.E_DEBUG3);
		Pdt pdt = instance();
		pdt.startMaintenanceThread(args[0], args[1]);
		while(!pdt._isLoaded)
		{
			try { Thread.sleep(1000L); } catch(InterruptedException ex) {}
			System.out.println("Awaiting _isLoaded");
		}
		
		System.out.println("Press enter to exit ...");
		System.console().readLine();
		pdt.stopMaintenanceThread();
		
		System.exit(0);
	}

	public void setPdtLoadListener(PdtLoadListener pdtLoadListener)
	{
		this.pdtLoadListener = pdtLoadListener;
	}

	public PdtLoadListener getPdtLoadListener()
	{
		return pdtLoadListener;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy