📄 abstractlogger.java
字号:
package net.sf.dz.daemon.logger;import java.io.BufferedReader;import java.io.File;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedList;import java.util.List;import java.util.Map;import java.util.Set;import java.util.TreeSet;import org.freehold.jukebox.conf.Configuration;import org.freehold.jukebox.logger.LogChannel;import org.freehold.jukebox.service.ActiveService;import net.sf.dz.daemon.Server;import net.sf.dz.daemon.ServerModule;import net.sf.dz.daemon.onewire.OneWireServer;import net.sf.dz.daemon.onewire.OneWireContainerListener;import net.sf.dz.daemon.onewire.SwitchContainerListener;import net.sf.dz.daemon.onewire.TemperatureContainerListener;abstract public class AbstractLogger extends ActiveService implements ServerModule, OneWireContainerListener, TemperatureContainerListener { public static final LogChannel CH_LOGGER = new LogChannel("Logger/Abstract"); /** * The server we're attached to. */ protected Server server; /** * Set of known device addresses. * * @see #deviceArrived * @see #deviceDeparted */ protected Set deviceSet = new TreeSet(); /** * List of device addresses. * * This list defines the order in which the data from the devices gets * logged. */ private List deviceOrder = new LinkedList(); /** * Name of device order file. * * <p> * * It should either be created manually (recommended), or it will be * automatically created on the first run, if the permissions are set * properly. If the permissions are not sufficient, the daemon will stop * working. * * <p> * * If you decide to let the daemon collect the device addresses, make * sure it runs long enough so all the devices have time to arrive on * the bus (usually takes no longer than a minute, in the worst case). * * <p> * * Also, keep in mind that if the device order file content doesn't * match with the RRD database structure, we'll screw up logging * altogether (VT: FIXME: Well, maybe just automate the hell out of it * anyway?). */ private String deviceOrderFile; /** * This flag is true if the logger is allowed to create or update the * device order file and regenerate the RRD database when it is * necessary. * * <p> * * The default value is <code>true</code>, but be careful - regenerating * the RRD database may take quite a while. */ private boolean autocreate = true; /** * The data map. * * The key is the device address, the value is the last known reading. */ private Map dataMap = new HashMap(); /** * The RRD write interval. * * The RRD database doesn't like it when the data gets written more * often than specified at the time of RRD database creation, this is * why it is necessary. * */ private long rrdInterval; /** * The graph creation interval, in minutes. * * <p> * * With <code>rrdtool</code>, it is easier to use a cron job to create * the graphs. With JRobin, this means invoking Java application, which * is not a pretty sight (until we see as JDK 1.5 performs), so I'd * rather create the graphs right here. * * <p> * * Set this parameter to 0 to disable creating graphs by this object. * Default is 5 minutes. */ private long graphInterval; /** * The RRD database location. */ private String rrdDB; /** * The RRD trace file location. */ private String rrdTrace; protected String getRrdLocation() { return rrdDB; } protected String getTraceLocation() { return rrdTrace; } public final void attach(Server server) { if ( this.server != null ) { throw new IllegalStateException("Already attached to " + server); } this.server = server; } protected void configure() throws Throwable { Configuration cf = getConfiguration(); String cfroot = getConfigurationRoot(); autocreate = cf.getBoolean(cfroot + ".rrd_logger.autocreate.enabled", true); deviceOrderFile = cf.getString(cfroot + ".rrd_logger.device_order"); try { BufferedReader br = new BufferedReader(new FileReader(deviceOrderFile)); while ( true ) { String line = br.readLine(); if ( line == null ) { break; } deviceOrder.add(line.trim()); } } catch ( FileNotFoundException fnfex ) { if ( !autocreate ) { Throwable t = new IllegalStateException("Autocreate is not enabled, and there's no device order file"); t.initCause(fnfex); throw t; } dumpDeviceOrderFile(); } if ( deviceOrder.isEmpty() ) { complain(LOG_WARNING, CH_LOGGER, "The device order file (" + deviceOrderFile + ") doesn't contain any parseable entries"); } //rrdtool = cf.getString(cfroot + ".rrd_logger.tool"); rrdInterval = cf.getInteger(cfroot + ".rrd_logger.interval"); graphInterval = cf.getInteger(cfroot + ".rrd_logger.graph_interval", 5); rrdDB = cf.getString(cfroot + ".rrd_logger.database"); rrdTrace = cf.getString(cfroot + ".rrd_logger.tracefile"); //rrdRegenerate = cf.getString(cfroot + ".rrd_logger.regenerate"); } protected void startup() throws Throwable { // Check if we're configured getConfiguration(); if ( server == null ) { throw new IllegalStateException("Not attached to the server yet"); } // Register ourselves with the 1-Wire server[s] int count = 0; for ( Iterator i = server.getModuleMap().keySet().iterator(); i.hasNext(); ) { Object key = i.next(); Object module = server.getModuleMap().get(key); if ( module instanceof OneWireServer ) { complain(LOG_INFO, CH_LOGGER, "1-Wire server found as '" + key + "'"); OneWireServer s = (OneWireServer)module; count++; s.addListener(this); } } if ( count == 0 ) { throw new IllegalStateException("OneWireServer has to be defined in the configuration and operable before this module"); } } protected void execute() throws Throwable { long graphCreated = 0; while ( isEnabled() ) { Thread.sleep(rrdInterval * 1000); sync(); if ( graphCreated < (System.currentTimeMillis() - (graphInterval * 1000 * 60)) ) { try { graph(); } catch ( Throwable t ) { // Failure to create graphs in not an excuse to stop // working altogether complain(LOG_WARNING, CH_LOGGER, "Failed to create graphs:", t); } graphCreated = System.currentTimeMillis(); } } } protected void shutdown(Throwable cause) throws Throwable { } public synchronized void deviceArrived(String address, String type) { if ( (!"DS1820".equals(type)) && (!"DS1920".equals(type)) && (!"DS18B20".equals(type)) ) { complain(LOG_NOTICE, CH_LOGGER, "I guess we don't care about " + type + "?"); return; } deviceSet.add(address); if ( !deviceOrder.contains(address) ) { if ( autocreate ) { deviceOrder.add(address); try { dumpDeviceOrderFile(); } catch ( IOException ioex ) { // This is not good. Since the device order file // couldn't be updated, it doesn't make sense to // regenerate RRD. // VT: FIXME: It may even make sense to stop. complain(LOG_WARNING, CH_LOGGER, "Couldn't write device order file", ioex); return; } try { // Force an update so there's at least one record for // the regenerate script to feed - in case we're // processing the first arrival, there's no trace file // yet. sync(); regenerateRRD(); } catch ( Throwable t ) { // Damn! Now it is all screwed up... Don't even know what to do yet. complain(LOG_ALERT, CH_LOGGER, "Couldn't regenerate RRD - you'll have to check its status...", t); return; } } else { // Well, they didn't want it, but maybe they'll change their // mind if they know about it? complain(LOG_NOTICE, CH_LOGGER, "Autocreate disabled, device will not be logged: " + address); } } } public void deviceDeparted(String address) { deviceSet.remove(address); dataMap.remove(address); // VT: FIXME: Make sure we realize the device has departed } public void deviceFault(String address, String message) { // There's nothing we can do here } public void currentTemperatureChanged(String address, double temperature) { dataMap.put(address, new Double(temperature)); } private synchronized void sync() { if ( deviceOrder.isEmpty() ) { complain(LOG_INFO, CH_LOGGER, "sync(): nothing to write"); return; } // Now, let's make sure that we don't write the data twice within // the RRD allowed interval. If not enough time passed since last // sync, we'll just wait try { String entry = writeTraceEntry(); rrdupdate(entry); } catch ( Throwable t ) { complain(LOG_ERR, CH_LOGGER, "sync failed, cause:", t); } } /** * Write the trace file entry. * * This method is a compromise between clarity and reusability (at * least, until {@link RRDLogger RRDLogger} is retired. Since * <code>rrdtool</code> likes its command line exactly the way the trace * file entry is written (actually, it's the other way around - the * trace file entry was crafted to match <code>rrdtool</code> command * line, but never mind), this method will write the trace file entry * *and* produce a string that could be used to pass to * <code>rrdtool</code>. * * @return Trace file entry as string. */ private synchronized String writeTraceEntry() throws IOException { StringBuffer sb = new StringBuffer(); // Write the time String now = Long.toString(System.currentTimeMillis()/1000); sb.append(now); // Write the sensor data // VT: FIXME: Currently, it processes only the double values. // Has to be adjusted for the switch state. for ( Iterator i = deviceOrder.iterator(); i.hasNext(); ) { String address = (String)i.next(); Double value = (Double)dataMap.get(address); if ( value != null ) { sb.append(":" + value); } else { sb.append(":U"); } } String result = sb.toString(); complain(LOG_DEBUG, CH_LOGGER, "poll: " + result); // VT: FIXME: It is possible to call the rrdtool even if writing the // entry failed... // We should do it rarely enough so the overhead of opening and // closing the file, as well as running the RRD, is not too bad File file = new File(rrdTrace); boolean append = file.exists() ? true : false; FileWriter fw = new FileWriter(rrdTrace, append); PrintWriter pw = new PrintWriter(fw); pw.println(sb); pw.flush(); fw.flush(); fw.close(); return result; } /** * Dump the device order list into the device order file. */ private synchronized void dumpDeviceOrderFile() throws IOException { // Let's be paranoid if ( !autocreate ) { throw new IllegalStateException("autocreate is disabled, you idiot"); } if ( deviceOrder.isEmpty() ) { return; } PrintWriter pw = new PrintWriter(new FileWriter(deviceOrderFile)); for ( Iterator i = deviceOrder.iterator(); i.hasNext(); ) { pw.println(i.next().toString()); } pw.close(); complain(LOG_INFO, CH_LOGGER, "Written " + deviceOrder.size() + " item[s] into " + deviceOrderFile); } /** * Create a fresh instance of RRD database and try to reinitialize it * with the trace file content. */ abstract protected void regenerateRRD() throws Throwable; /** * Write the entry to RRD database. * * @param entry An RRD entry as <code>rrdtool</code> takes on a command line. */ abstract protected void rrdupdate(String entry) throws Throwable; /** * Create the graphs. */ abstract protected void graph() throws Throwable; /** * Get the iterator on the device order. */ protected Iterator iterator() { return deviceOrder.iterator(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -