From: Sasa M. <sa...@us...> - 2004-07-12 13:35:26
|
Update of /cvsroot/jrobin/src/org/jrobin/cmd In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17030/org/jrobin/cmd Added Files: RrdCmdScanner.java RrdCommander.java RrdCreateCmd.java RrdDumpCmd.java RrdFetchCmd.java RrdLastCmd.java RrdToolCmd.java RrdUpdateCommand.java TimeParser.java TimeScanner.java TimeSpec.java TimeToken.java Log Message: RRDTool like command parser added. So far, supported RRDTool commands are: - create - dump - update - fetch - last Also, some minor improvements to core FILE and NIO backends. --- NEW FILE: TimeSpec.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.Util; import java.util.GregorianCalendar; import java.util.Date; import java.util.Calendar; class TimeSpec { public static final int TYPE_ABSOLUTE = 0; public static final int TYPE_START = 1; public static final int TYPE_END = 2; int type = TYPE_ABSOLUTE; int year, month, day, hour, min, sec; int wday; int dyear, dmonth, dday, dhour, dmin, dsec; String dateString; TimeSpec context; public TimeSpec(String dateString) { this.dateString = dateString; } void localtime(long timestamp) { GregorianCalendar date = new GregorianCalendar(); date.setTime(new Date(timestamp * 1000L)); year = date.get(Calendar.YEAR) - 1900; month = date.get(Calendar.MONTH); day = date.get(Calendar.DAY_OF_MONTH); hour = date.get(Calendar.HOUR_OF_DAY); min = date.get(Calendar.MINUTE); sec = date.get(Calendar.SECOND); wday = date.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY; } GregorianCalendar getTime() throws RrdException { GregorianCalendar gc; // absoulte time, this is easy if(type == TYPE_ABSOLUTE) { gc = new GregorianCalendar(year + 1900, month, day, hour, min, sec); } // relative time, we need a context to evaluate it else if(context != null && context.type == TYPE_ABSOLUTE) { gc = context.getTime(); } // how would I guess what time it was? else { throw new RrdException("Relative times like '" + dateString + "' require proper absolute context to be evaluated"); } gc.add(Calendar.YEAR, dyear); gc.add(Calendar.MONTH, dmonth); gc.add(Calendar.DAY_OF_MONTH, dday); gc.add(Calendar.HOUR_OF_DAY, dhour); gc.add(Calendar.MINUTE, dmin); gc.add(Calendar.SECOND, dsec); return gc; } long getTimestamp() throws RrdException { return Util.getTimestamp(getTime()); } String dump() { return (type == TYPE_ABSOLUTE? "ABSTIME": type == TYPE_START? "START": "END") + ": " + year + "/" + month + "/" + day + "/" + hour + "/" + min + "/" + sec + " (" + dyear + "/" + dmonth + "/" + dday + "/" + dhour + "/" + dmin + "/" + dsec + ")"; } static GregorianCalendar[] getTimes(TimeSpec spec1, TimeSpec spec2) throws RrdException { if(spec1.type == TYPE_START || spec2.type == TYPE_END) { throw new RrdException("Recursive time specifications not allowed"); } spec1.context = spec2; spec2.context = spec1; return new GregorianCalendar[] { spec1.getTime(), spec2.getTime() }; } static long[] getTimestamps(TimeSpec spec1, TimeSpec spec2) throws RrdException { GregorianCalendar[] gcs = getTimes(spec1, spec2); return new long[] { Util.getTimestamp(gcs[0]), Util.getTimestamp(gcs[1]) }; } } --- NEW FILE: RrdFetchCmd.java --- package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.RrdDb; import org.jrobin.core.FetchRequest; import org.jrobin.core.FetchData; import java.io.IOException; class RrdFetchCmd extends RrdToolCmd { static final String DEFAULT_START = "end-1day"; static final String DEFAULT_END = "now"; public RrdFetchCmd(RrdCmdScanner cmdScanner) { super(cmdScanner); } String getCmdType() { return "fetch"; } Object execute() throws RrdException, IOException { // --start String startStr = cmdScanner.getOptionValue("s", "start", DEFAULT_START); TimeSpec spec1 = new TimeParser(startStr).parse(); // --end String endStr = cmdScanner.getOptionValue("e", "end", DEFAULT_END); TimeSpec spec2 = new TimeParser(endStr).parse(); long[] timestamps = TimeSpec.getTimestamps(spec1, spec2); // --resolution String resolutionStr = cmdScanner.getOptionValue("r", "resolution"); long resolution = 1; if(resolutionStr != null) { resolution = parseLong(resolutionStr); } // other words String[] tokens = cmdScanner.getRemainingWords(); if(tokens.length != 3) { throw new RrdException("Invalid rrdfetch syntax"); } String path = tokens[1]; String consolFun = tokens[2]; RrdDb rrdDb = getRrdDbReference(path); try { FetchRequest fetchRequest = rrdDb.createFetchRequest( consolFun, timestamps[0], timestamps[1], resolution); System.out.println(fetchRequest.dump()); FetchData fetchData = fetchRequest.fetchData(); println(fetchData.toString()); return fetchData; } finally { releaseRrdDbReference(rrdDb); } } } --- NEW FILE: RrdCreateCmd.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.RrdDef; import org.jrobin.core.RrdDb; import java.io.IOException; class RrdCreateCmd extends RrdToolCmd { static final String DEFAULT_START = "now-10s"; static final String DEFAULT_STEP = "300"; private RrdDef rrdDef; public RrdCreateCmd(RrdCmdScanner cmdScanner) { super(cmdScanner); } String getCmdType() { return "create"; } Object execute() throws RrdException, IOException { String startStr = cmdScanner.getOptionValue("b", "start", DEFAULT_START); TimeSpec spec = new TimeParser(startStr).parse(); long start = spec.getTimestamp(); String stepStr = cmdScanner.getOptionValue("s", "step", DEFAULT_STEP); long step = parseLong(stepStr); String[] words = cmdScanner.getRemainingWords(); if(words.length < 2) { throw new RrdException("RRD file path not specified"); } String path = words[1]; rrdDef = new RrdDef(path, start, step); for(int i = 2; i < words.length; i++) { if(words[i].startsWith("DS:")) { parseDef(words[i]); } else if(words[i].startsWith("RRA:")) { parseRra(words[i]); } else { throw new RrdException("Invalid word in the rrdcreate syntax: " + words[i]); } } return createRrdDb(); } private void parseDef(String word) throws RrdException { // DEF:name:type:heratbeat:min:max String[] tokens = word.split(":"); if(tokens.length < 6) { throw new RrdException("Invalid DS definition: " + word); } String dsName = tokens[1]; String dsType = tokens[2]; long heartbeat = parseLong(tokens[3]); double min = parseDouble(tokens[4]); double max = parseDouble(tokens[5]); rrdDef.addDatasource(dsName, dsType, heartbeat, min, max); } private void parseRra(String word) throws RrdException { // RRA:cfun:xff:steps:rows String[] tokens = word.split(":"); if(tokens.length < 5) { throw new RrdException("Invalid RRA definition: " + word); } String cf = tokens[1]; double xff = parseDouble(tokens[2]); int steps = parseInt(tokens[3]); int rows = parseInt(tokens[4]); rrdDef.addArchive(cf, xff, steps, rows); } private String createRrdDb() throws IOException, RrdException { RrdDb rrdDb = getRrdDbReference(rrdDef); releaseRrdDbReference(rrdDb); return rrdDef.getPath(); } } --- NEW FILE: RrdCommander.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; /** * Class to be used to execute various RRDTool commands (original syntax of RRDTool must be used). */ public class RrdCommander { /** * Checks if the output from any RRDTool command will be visible on the standard output device * (console). Default setting is <code>true</code>. * * @return true, if the output will be visible on the standard output device; false, otherwise. */ public static synchronized boolean isStandardOutUsed() { return RrdToolCmd.isStandardOutUsed(); } /** * Method used to control access to stdout (System.out, console) for all RRDTool commands. By default, * all RRDTool commands are allowed to print results to stdout, in a form used by RRDTool. * * @param standardOutUsed <code>true</code> if the output should be visible on the * standard output device, <code>false</code> otherwise. */ public static synchronized void setStandardOutUsed(boolean standardOutUsed) { RrdToolCmd.setStandardOutUsed(standardOutUsed); } /** * Checks if the class uses {@link org.jrobin.core.RrdDbPool} internally while executing * RRDTool commands. * @return true if the pool is used, false otherwise */ public static synchronized boolean isRrdDbPoolUsed() { return RrdToolCmd.isRrdDbPoolUsed(); } /** * Forces or prohibits {@link org.jrobin.core.RrdDbPool} usage internally while executing * RRDTool commands * @param rrdDbPoolUsed true, to force pool usage, false otherwise. */ public static synchronized void setRrdDbPoolUsed(boolean rrdDbPoolUsed) { RrdToolCmd.setRrdDbPoolUsed(rrdDbPoolUsed); } /** * Executes single RRDTool command. The command string should start with some * well known RRDTool command word (create, update, fetch, graph...)<p> * @param command RRDTool command like: <p> * <pre> * create test.rrd --start "noon yesterday" --step 300 DS:x:GAUGE:600:U:U RRA:0.5:5:1000 * update test.rrd N:1000 * last test.rrd * ... * </pre> * @return Result of specific RRDTool command. It is guaranteed that the result of any * successfully executed command will be always different from null. * Unsuccessfully executed commands will always throw * an exception, so you need not check for null results.<p> * Exact type of the result depends from the * type of executed RRDTool command:<p> * <ul> * <li><b>create</b>: returns java.lang.String containing path to the newly created RRD file. * <li><b>last</b>: returns java.lang.Long representing timestamp of the last update. * <li><b>update</b>: returns java.lang.Long representing timestamp of the last update. *<li><b>dump</b>: returns (very long) java.lang.String representing the content of a RRD file * in XML format. * </ul> * @throws IOException thrown in case of I/O error * @throws RrdException thrown for all other errors (parsing errors, * unknown RRDTool syntax/command/option, internal RRD errors...) */ public static synchronized Object execute(String command) throws IOException, RrdException { RrdCmdScanner cmdScanner = new RrdCmdScanner(command); RrdToolCmd[] commanders = { new RrdCreateCmd(cmdScanner), new RrdLastCmd(cmdScanner), new RrdUpdateCommand(cmdScanner), new RrdDumpCmd(cmdScanner), new RrdFetchCmd(cmdScanner) }; for(int i = 0; i < commanders.length; i++) { Object result = commanders[i].go(); if(result != null) { return result; } } throw new RrdException("Unknown RRDTool command: " + command); } public static void main(String[] args) { BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); while (true) { try { String s = r.readLine(); if(s.equals(".")) { System.exit(0); } System.out.println(execute(s)); } catch (IOException e) { System.err.println(e); } catch (RrdException e) { System.err.println(e); } } } } --- NEW FILE: TimeToken.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; class TimeToken { public static final int MIDNIGHT = 1; public static final int NOON = 2; public static final int TEATIME = 3; public static final int PM = 4; public static final int AM = 5; public static final int YESTERDAY = 6; public static final int TODAY = 7; public static final int TOMORROW = 8; public static final int NOW = 9; public static final int START = 10; public static final int END = 11; public static final int SECONDS = 12; public static final int MINUTES = 13; public static final int HOURS = 14; public static final int DAYS = 15; public static final int WEEKS = 16; public static final int MONTHS = 17; public static final int YEARS = 18; public static final int MONTHS_MINUTES = 19; public static final int NUMBER = 20; public static final int PLUS = 21; public static final int MINUS = 22; public static final int DOT = 23; public static final int COLON = 24; public static final int SLASH = 25; public static final int ID = 26; public static final int JUNK = 27; public static final int JAN = 28; public static final int FEB = 29; public static final int MAR = 30; public static final int APR = 31; public static final int MAY = 32; public static final int JUN = 33; public static final int JUL = 34; public static final int AUG = 35; public static final int SEP = 36; public static final int OCT = 37; public static final int NOV = 38; public static final int DEC = 39; public static final int SUN = 40; public static final int MON = 41; public static final int TUE = 42; public static final int WED = 43; public static final int THU = 44; public static final int FRI = 45; public static final int SAT = 46; public static final int EOF = -1; final String value; /* token name */ final int id; /* token id */ public TimeToken(String value, int id) { this.value = value; this.id = id; } public String toString() { return value + " [" + id + "]"; } } --- NEW FILE: RrdDumpCmd.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.RrdDb; import java.io.IOException; class RrdDumpCmd extends RrdToolCmd { RrdDumpCmd(RrdCmdScanner cmdScanner) { super(cmdScanner); } String getCmdType() { return "dump"; } Object execute() throws RrdException, IOException { String[] words = cmdScanner.getRemainingWords(); if(words.length != 2) { throw new RrdException("Invalid rrddump syntax"); } String path = words[1]; RrdDb rrdDb = getRrdDbReference(path); try { String xml = rrdDb.getXml(); println(xml); return xml; } finally { releaseRrdDbReference(rrdDb); } } } --- NEW FILE: RrdLastCmd.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.RrdDb; import java.io.IOException; class RrdLastCmd extends RrdToolCmd { RrdLastCmd(RrdCmdScanner cmdScanner) { super(cmdScanner); } String getCmdType() { return "last"; } Object execute() throws RrdException, IOException { String[] words = cmdScanner.getRemainingWords(); if(words.length != 2) { throw new RrdException("Invalid rrdlast syntax"); } String path = words[1]; RrdDb rrdDb = getRrdDbReference(path); try { long lastUpdateTime = rrdDb.getLastUpdateTime(); println(lastUpdateTime + ""); return new Long(lastUpdateTime); } finally { releaseRrdDbReference(rrdDb); } } } --- NEW FILE: RrdToolCmd.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.RrdDb; import org.jrobin.core.RrdDbPool; import org.jrobin.core.RrdDef; import java.io.IOException; abstract class RrdToolCmd { static boolean rrdDbPoolUsed = true; static boolean standardOutUsed = true; static boolean isRrdDbPoolUsed() { return rrdDbPoolUsed; } static void setRrdDbPoolUsed(boolean rrdDbPoolUsed) { RrdToolCmd.rrdDbPoolUsed = rrdDbPoolUsed; } static boolean isStandardOutUsed() { return standardOutUsed; } static void setStandardOutUsed(boolean standardOutUsed) { RrdToolCmd.standardOutUsed = standardOutUsed; } RrdCmdScanner cmdScanner; RrdToolCmd(RrdCmdScanner cmdScanner) { this.cmdScanner = cmdScanner; } abstract String getCmdType(); Object go() throws IOException, RrdException { if(!getCmdType().equals(cmdScanner.getCmdType())) { return null; } return execute(); } abstract Object execute() throws RrdException, IOException; static long parseLong(String value) throws RrdException { try { return Long.parseLong(value); } catch(NumberFormatException nfe) { throw new RrdException(nfe); } } static int parseInt(String value) throws RrdException { try { return Integer.parseInt(value); } catch(NumberFormatException nfe) { throw new RrdException(nfe); } } static double parseDouble(String value) throws RrdException { if(value.equals("U")) { return Double.NaN; } try { return Double.parseDouble(value); } catch(NumberFormatException nfe) { throw new RrdException(nfe); } } static void print(String s) { if(standardOutUsed) { System.out.print(s); } } static void println(String s) { if(standardOutUsed) { System.out.println(s); } } static RrdDb getRrdDbReference(String path) throws IOException, RrdException { if(rrdDbPoolUsed) { return RrdDbPool.getInstance().requestRrdDb(path); } else { return new RrdDb(path); } } static RrdDb getRrdDbReference(String path, String xmlPath) throws IOException, RrdException { if(rrdDbPoolUsed) { return RrdDbPool.getInstance().requestRrdDb(path, xmlPath); } else { return new RrdDb(path, xmlPath); } } static RrdDb getRrdDbReference(RrdDef rrdDef) throws IOException, RrdException { if(rrdDbPoolUsed) { return RrdDbPool.getInstance().requestRrdDb(rrdDef); } else { return new RrdDb(rrdDef); } } static void releaseRrdDbReference(RrdDb rrdDb) throws IOException, RrdException { if(rrdDbPoolUsed) { RrdDbPool.getInstance().release(rrdDb); } else { rrdDb.close(); } } } --- NEW FILE: TimeScanner.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; class TimeScanner { private String dateString; private int pos, pos_save; private TimeToken token, token_save; static final TimeToken[] WORDS = { new TimeToken("midnight", TimeToken.MIDNIGHT), /* 00:00:00 of today or tomorrow */ new TimeToken("noon", TimeToken.NOON), /* 12:00:00 of today or tomorrow */ new TimeToken("teatime", TimeToken.TEATIME), /* 16:00:00 of today or tomorrow */ new TimeToken("am", TimeToken.AM), /* morning times for 0-12 clock */ new TimeToken("pm", TimeToken.PM), /* evening times for 0-12 clock */ new TimeToken("tomorrow", TimeToken.TOMORROW), new TimeToken("yesterday", TimeToken.YESTERDAY), new TimeToken("today", TimeToken.TODAY), new TimeToken("now", TimeToken.NOW), new TimeToken("n", TimeToken.NOW), new TimeToken("start", TimeToken.START), new TimeToken("s", TimeToken.START), new TimeToken("end", TimeToken.END), new TimeToken("e", TimeToken.END), new TimeToken("jan", TimeToken.JAN), new TimeToken("feb", TimeToken.FEB), new TimeToken("mar", TimeToken.MAR), new TimeToken("apr", TimeToken.APR), new TimeToken("may", TimeToken.MAY), new TimeToken("jun", TimeToken.JUN), new TimeToken("jul", TimeToken.JUL), new TimeToken("aug", TimeToken.AUG), new TimeToken("sep", TimeToken.SEP), new TimeToken("oct", TimeToken.OCT), new TimeToken("nov", TimeToken.NOV), new TimeToken("dec", TimeToken.DEC), new TimeToken("january", TimeToken.JAN), new TimeToken("february", TimeToken.FEB), new TimeToken("march", TimeToken.MAR), new TimeToken("april", TimeToken.APR), new TimeToken("may", TimeToken.MAY), new TimeToken("june", TimeToken.JUN), new TimeToken("july", TimeToken.JUL), new TimeToken("august", TimeToken.AUG), new TimeToken("september", TimeToken.SEP), new TimeToken("october", TimeToken.OCT), new TimeToken("november", TimeToken.NOV), new TimeToken("december", TimeToken.DEC), new TimeToken("sunday", TimeToken.SUN), new TimeToken("sun", TimeToken.SUN), new TimeToken("monday", TimeToken.MON), new TimeToken("mon", TimeToken.MON), new TimeToken("tuesday", TimeToken.TUE), new TimeToken("tue", TimeToken.TUE), new TimeToken("wednesday", TimeToken.WED), new TimeToken("wed", TimeToken.WED), new TimeToken("thursday", TimeToken.THU), new TimeToken("thu", TimeToken.THU), new TimeToken("friday", TimeToken.FRI), new TimeToken("fri", TimeToken.FRI), new TimeToken("saturday", TimeToken.SAT), new TimeToken("sat", TimeToken.SAT), new TimeToken(null, 0) /*** SENTINEL ***/ }; static TimeToken[] MULTIPLIERS = { new TimeToken("second", TimeToken.SECONDS), /* seconds multiplier */ new TimeToken("seconds", TimeToken.SECONDS), /* (pluralized) */ new TimeToken("sec", TimeToken.SECONDS), /* (generic) */ new TimeToken("s", TimeToken.SECONDS), /* (short generic) */ new TimeToken("minute", TimeToken.MINUTES), /* minutes multiplier */ new TimeToken("minutes", TimeToken.MINUTES), /* (pluralized) */ new TimeToken("min", TimeToken.MINUTES), /* (generic) */ new TimeToken("m", TimeToken.MONTHS_MINUTES), /* (short generic) */ new TimeToken("hour", TimeToken.HOURS), /* hours ... */ new TimeToken("hours", TimeToken.HOURS), /* (pluralized) */ new TimeToken("hr", TimeToken.HOURS), /* (generic) */ new TimeToken("h", TimeToken.HOURS), /* (short generic) */ new TimeToken("day", TimeToken.DAYS), /* days ... */ new TimeToken("days", TimeToken.DAYS), /* (pluralized) */ new TimeToken("d", TimeToken.DAYS), /* (short generic) */ new TimeToken("week", TimeToken.WEEKS), /* week ... */ new TimeToken("weeks", TimeToken.WEEKS), /* (pluralized) */ new TimeToken("wk", TimeToken.WEEKS), /* (generic) */ new TimeToken("w", TimeToken.WEEKS), /* (short generic) */ new TimeToken("month", TimeToken.MONTHS), /* week ... */ new TimeToken("months", TimeToken.MONTHS), /* (pluralized) */ new TimeToken("mon", TimeToken.MONTHS), /* (generic) */ new TimeToken("year", TimeToken.YEARS), /* year ... */ new TimeToken("years", TimeToken.YEARS), /* (pluralized) */ new TimeToken("yr", TimeToken.YEARS), /* (generic) */ new TimeToken("y", TimeToken.YEARS), /* (short generic) */ new TimeToken(null, 0) /*** SENTINEL ***/ }; TimeToken[] specials = WORDS; public TimeScanner(String dateString) { this.dateString = dateString; } void setContext(boolean parsingWords) { specials = parsingWords? WORDS: MULTIPLIERS; } TimeToken nextToken() { StringBuffer buffer = new StringBuffer(""); while (pos < dateString.length()) { char c = dateString.charAt(pos++); if (Character.isWhitespace(c) || c == '_' || c == ',') { continue; } buffer.append(c); if (Character.isDigit(c)) { // pick as many digits as possible while (pos < dateString.length()) { char next = dateString.charAt(pos); if (Character.isDigit(next)) { buffer.append(next); pos++; } else { break; } } String value = buffer.toString(); return token = new TimeToken(value, TimeToken.NUMBER); } if (Character.isLetter(c)) { // pick as many letters as possible while (pos < dateString.length()) { char next = dateString.charAt(pos); if (Character.isLetter(next)) { buffer.append(next); pos++; } else { break; } } String value = buffer.toString(); return token = new TimeToken(value, parseToken(value)); } switch (c) { case ':': return token = new TimeToken(":", TimeToken.COLON); case '.': return token = new TimeToken(".", TimeToken.DOT); case '+': return token = new TimeToken("+", TimeToken.PLUS); case '-': return token = new TimeToken("-", TimeToken.MINUS); case '/': return token = new TimeToken("/", TimeToken.SLASH); default: pos--; return token = new TimeToken(null, TimeToken.EOF); } } return token = new TimeToken(null, TimeToken.EOF); } TimeToken resolveMonthsMinutes(int newId) { assert token.id == TimeToken.MONTHS_MINUTES; assert newId == TimeToken.MONTHS || newId == TimeToken.MINUTES; return token = new TimeToken(token.value, newId); } void saveState() { token_save = token; pos_save = pos; } TimeToken restoreState() { pos = pos_save; return token = token_save; } private int parseToken(String arg) { for (int i = 0; specials[i].value != null; i++) { if (specials[i].value.equalsIgnoreCase(arg)) { return specials[i].id; } } return TimeToken.ID; } } --- NEW FILE: TimeParser.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ /* * Java port of Tobi's original parsetime.c routine */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import org.jrobin.core.Util; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; class TimeParser { public static final int PREVIOUS_OP = -1; TimeToken token; TimeScanner scanner; TimeSpec spec; int op = TimeToken.PLUS; int prev_multiplier = -1; public TimeParser(String dateString) { scanner = new TimeScanner(dateString); spec = new TimeSpec(dateString); } private void expectToken(int desired, String errorMessage) throws RrdException { token = scanner.nextToken(); if (token.id != desired) { throw new RrdException(errorMessage); } } private void plusMinus(int doop) throws RrdException { if (doop >= 0) { op = doop; expectToken(TimeToken.NUMBER, "There should be number after " + (op == TimeToken.PLUS ? '+' : '-')); prev_multiplier = -1; /* reset months-minutes guessing mechanics */ } int delta = Integer.parseInt(token.value); token = scanner.nextToken(); if (token.id == TimeToken.MONTHS_MINUTES) { /* hard job to guess what does that -5m means: -5mon or -5min? */ switch (prev_multiplier) { case TimeToken.DAYS: case TimeToken.WEEKS: case TimeToken.MONTHS: case TimeToken.YEARS: token = scanner.resolveMonthsMinutes(TimeToken.MONTHS); break; case TimeToken.SECONDS: case TimeToken.MINUTES: case TimeToken.HOURS: token = scanner.resolveMonthsMinutes(TimeToken.MINUTES); break; default: if (delta < 6) { token = scanner.resolveMonthsMinutes(TimeToken.MONTHS); } else { token = scanner.resolveMonthsMinutes(TimeToken.MINUTES); } } } prev_multiplier = token.id; delta *= (op == TimeToken.PLUS) ? +1 : -1; switch (token.id) { case TimeToken.YEARS: spec.dyear += delta; return; case TimeToken.MONTHS: spec.dmonth += delta; return; case TimeToken.WEEKS: delta *= 7; /* FALLTHRU */ case TimeToken.DAYS: spec.dday += delta; return; case TimeToken.HOURS: spec.dhour += delta; return; case TimeToken.MINUTES: spec.dmin += delta; return; case TimeToken.SECONDS: default: // default is 'seconds' spec.dsec += delta; return; } // unreachable statement // throw new RrdException("Well-known time unit expected after " + delta); } private void timeOfDay() throws RrdException { int hour, minute = 0; /* save token status in case we must abort */ scanner.saveState(); /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */ if (token.value.length() > 2) { return; } hour = Integer.parseInt(token.value); token = scanner.nextToken(); if (token.id == TimeToken.SLASH || token.id == TimeToken.DOT) { /* guess we are looking at a date */ token = scanner.restoreState(); return; } if (token.id == TimeToken.COLON) { expectToken(TimeToken.NUMBER, "Parsing HH:MM syntax, expecting MM as number, got none"); minute = Integer.parseInt(token.value); if (minute > 59) { throw new RrdException("Parsing HH:MM syntax, got MM = " + minute + " (>59!)"); } token = scanner.nextToken(); } /* check if an AM or PM specifier was given */ if (token.id == TimeToken.AM || token.id == TimeToken.PM) { if (hour > 12) { throw new RrdException("There cannot be more than 12 AM or PM hours"); } if (token.id == TimeToken.PM) { if (hour != 12) { /* 12:xx PM is 12:xx, not 24:xx */ hour += 12; } } else { if (hour == 12) { /* 12:xx AM is 00:xx, not 12:xx */ hour = 0; } } token = scanner.nextToken(); } else if (hour > 23) { /* guess it was not a time then ... */ token = scanner.restoreState(); return; } spec.hour = hour; spec.min = minute; spec.sec = 0; if (spec.hour == 24) { spec.hour = 0; spec.day++; } } private void assignDate(long mday, long mon, long year) throws RrdException { if (year > 138) { if (year > 1970) { year -= 1900; } else { throw new RrdException("Invalid year " + year + " (should be either 00-99 or >1900)"); } } else if (year >= 0 && year < 38) { year += 100; /* Allow year 2000-2037 to be specified as */ } /* 00-37 until the problem of 2038 year will */ /* arise for unices with 32-bit time_t */ if (year < 70) { throw new RrdException("Won't handle dates before epoch (01/01/1970), sorry"); } spec.year = (int) year; spec.month = (int) mon; spec.day = (int) mday; } private void day() throws RrdException { long mday = 0, wday, mon, year = spec.year; switch (token.id) { case TimeToken.YESTERDAY: spec.day--; /* FALLTRHU */ case TimeToken.TODAY: /* force ourselves to stay in today - no further processing */ token = scanner.nextToken(); break; case TimeToken.TOMORROW: spec.day++; token = scanner.nextToken(); break; case TimeToken.JAN: case TimeToken.FEB: case TimeToken.MAR: case TimeToken.APR: case TimeToken.MAY: case TimeToken.JUN: case TimeToken.JUL: case TimeToken.AUG: case TimeToken.SEP: case TimeToken.OCT: case TimeToken.NOV: case TimeToken.DEC: /* do month mday [year] */ mon = (token.id - TimeToken.JAN); expectToken(TimeToken.NUMBER, "the day of the month should follow month name"); mday = Long.parseLong(token.value); token = scanner.nextToken(); if (token.id == TimeToken.NUMBER) { year = Long.parseLong(token.value); token = scanner.nextToken(); } else { year = spec.year; } assignDate(mday, mon, year); break; case TimeToken.SUN: case TimeToken.MON: case TimeToken.TUE: case TimeToken.WED: case TimeToken.THU: case TimeToken.FRI: case TimeToken.SAT: /* do a particular day of the week */ wday = (token.id - TimeToken.SUN); spec.day += (wday - spec.wday); token = scanner.nextToken(); break; case TimeToken.NUMBER: /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY */ // int tlen = token.value.length(); mon = Long.parseLong(token.value); if (mon > 10L * 365L * 24L * 60L * 60L) { spec.localtime(mon); token = scanner.nextToken(); break; } if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */ year = mon / 10000; mday = mon % 100; mon = (mon / 100) % 100; token = scanner.nextToken(); } else { token = scanner.nextToken(); if (mon <= 31 && (token.id == TimeToken.SLASH || token.id == TimeToken.DOT)) { int sep = token.id; expectToken(TimeToken.NUMBER, "there should be " + (sep == TimeToken.DOT ? "month" : "day") + " number after " + (sep == TimeToken.DOT ? '.' : '/')); mday = Long.parseLong(token.value); token = scanner.nextToken(); if (token.id == sep) { expectToken(TimeToken.NUMBER, "there should be year number after " + (sep == TimeToken.DOT ? '.' : '/')); year = Long.parseLong(token.value); token = scanner.nextToken(); } /* flip months and days for European timing */ if (sep == TimeToken.DOT) { long x = mday; mday = mon; mon = x; } } } mon--; if (mon < 0 || mon > 11) { throw new RrdException("Did you really mean month " + (mon + 1)); } if (mday < 1 || mday > 31) { throw new RrdException("I'm afraid that " + mday + " is not a valid day of the month"); } assignDate(mday, mon, year); break; } } TimeSpec parse() throws RrdException { long now = Util.getTime(); int hr = 0; /* this MUST be initialized to zero for midnight/noon/teatime */ /* establish the default time reference */ spec.localtime(now); token = scanner.nextToken(); switch (token.id) { case TimeToken.PLUS: case TimeToken.MINUS: break; /* jump to OFFSET-SPEC part */ case TimeToken.START: spec.type = TimeSpec.TYPE_START; /* FALLTHRU */ case TimeToken.END: if (spec.type != TimeSpec.TYPE_START) { spec.type = TimeSpec.TYPE_END; } spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0; /* FALLTHRU */ case TimeToken.NOW: int time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } if (time_reference != TimeToken.NOW) { throw new RrdException("Words 'start' or 'end' MUST be followed by +|- offset"); } else if (token.id != TimeToken.EOF) { throw new RrdException("If 'now' is followed by a token it must be +|- offset"); } break; /* Only absolute time specifications below */ case TimeToken.NUMBER: timeOfDay(); if (token.id != TimeToken.NUMBER) { break; } /* fix month parsing */ case TimeToken.JAN: case TimeToken.FEB: case TimeToken.MAR: case TimeToken.APR: case TimeToken.MAY: case TimeToken.JUN: case TimeToken.JUL: case TimeToken.AUG: case TimeToken.SEP: case TimeToken.OCT: case TimeToken.NOV: case TimeToken.DEC: day(); if (token.id != TimeToken.NUMBER) { break; } timeOfDay(); break; /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized * hr to zero up above, then fall into this case in such a * way so we add +12 +4 hours to it for teatime, +12 hours * to it for noon, and nothing at all for midnight, then * set our rettime to that hour before leaping into the * month scanner */ case TimeToken.TEATIME: hr += 4; /* FALLTHRU */ case TimeToken.NOON: hr += 12; /* FALLTHRU */ case TimeToken.MIDNIGHT: spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; default: throw new RrdException("Unparsable time: " + token.value); } /* * the OFFSET-SPEC part * * (NOTE, the sc_tokid was prefetched for us by the previous code) */ if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { scanner.setContext(false); while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS || token.id == TimeToken.NUMBER) { if (token.id == TimeToken.NUMBER) { plusMinus(PREVIOUS_OP); } else { plusMinus(token.id); } token = scanner.nextToken(); /* We will get EOF eventually but that's OK, since token() will return us as many EOFs as needed */ } } /* now we should be at EOF */ if (token.id != TimeToken.EOF) { throw new RrdException("Unparsable trailing text: " + token.value); } return spec; } public static void main(String[] args) throws IOException { BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); while (true) { String s = r.readLine(); try { TimeParser p = new TimeParser(s); TimeSpec spec = p.parse(); System.out.println(spec.getTime().getTime()); // System.out.println(spec.dump()); } catch (RrdException e) { System.err.println("ERROR: " + e); } } } } --- NEW FILE: RrdCmdScanner.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.RrdException; import java.util.LinkedList; import java.util.regex.Pattern; import java.util.regex.Matcher; class RrdCmdScanner { private static final Pattern PATTERN = Pattern.compile("([^\"\\s]*\"[^\"]*\")|([^\"\\s]+)"); private LinkedList words = new LinkedList(); private String cmdType; RrdCmdScanner(String[] cmdWords) { for(int i = 0; i < cmdWords.length; i++) { words.add(cmdWords[i]); if(words.size() == 1) { cmdType = cmdWords[i]; } } } RrdCmdScanner(String command) { parseWords(command); } private void parseWords(String command) { Matcher m = PATTERN.matcher(command); while(m.find()) { String word = m.group(); word = word.replaceAll("\"", ""); // System.out.println("Adding: [" + word + "]"); words.add(word); if(words.size() == 1) { cmdType = word; } } } String getCmdType() { return cmdType; } String getOptionValue(String shortForm, String longForm) throws RrdException { for(int i = 0; i < words.size(); i++) { String word = (String) words.get(i); if((shortForm != null && word.equals("-" + shortForm)) || (longForm != null && word.equals("--" + longForm))) { // match found if(i < words.size() - 1) { // value available String value = (String) words.get(i + 1); words.remove(i + 1); words.remove(i); return value; } else { throw new RrdException("Option found but value is not available"); } } } return null; } String getOptionValue(String shortForm, String longForm, String defaultValue) throws RrdException { String value = getOptionValue(shortForm, longForm); return value != null? value: defaultValue; } boolean getBooleanOption(String shortForm, String longForm) throws RrdException { for(int i = 0; i < words.size(); i++) { String word = (String) words.get(i); if((shortForm != null && word.equals("-" + shortForm)) || (longForm != null && word.equals("--" + longForm))) { // match found words.remove(i); return true; } } return false; } String[] getRemainingWords() { return (String[]) words.toArray(new String[0]); } } --- NEW FILE: RrdUpdateCommand.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.cmd; import org.jrobin.core.*; import java.io.IOException; class RrdUpdateCommand extends RrdToolCmd { private String[] dsNames; RrdUpdateCommand(RrdCmdScanner cmdScanner) { super(cmdScanner); } String getCmdType() { return "update"; } Object execute() throws RrdException, IOException { String template = cmdScanner.getOptionValue("t", "template"); if (template != null) { dsNames = template.split(":"); } String[] words = cmdScanner.getRemainingWords(); if (words.length < 3) { throw new RrdException("Insufficent number of parameters for rrdupdate"); } String path = words[1]; RrdDb rrdDb = getRrdDbReference(path); try { if (dsNames != null) { // template specified, check datasource names for (int i = 0; i < dsNames.length; i++) { rrdDb.getDsIndex(dsNames[i]); // will throw exception if not found } } // parse update strings long timestamp = -1; for (int i = 2; i < words.length; i++) { String[] tokens = words[i].split(":"); if (dsNames != null && dsNames.length + 1 != tokens.length) { throw new RrdException("Template required " + dsNames.length + " values, " + (tokens.length - 1) + " value(s) found in: " + words[i]); } int dsCount = rrdDb.getHeader().getDsCount(); if (dsNames == null && dsCount + 1 != tokens.length) { throw new RrdException("Expected " + dsCount + " values, " + (tokens.length - 1) + " value(s) found in: " + words[i]); } TimeSpec spec = new TimeParser(tokens[0]).parse(); timestamp = spec.getTimestamp(); Sample sample = rrdDb.createSample(timestamp); for (int j = 1; j < tokens.length; j++) { if (dsNames == null) { sample.setValue(j - 1, parseDouble(tokens[j])); } else { sample.setValue(dsNames[j - 1], parseDouble(tokens[j])); } } sample.update(); } return new Long(timestamp); } finally { releaseRrdDbReference(rrdDb); } } } |