package org.exist.xquery.modules.counter; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import org.apache.log4j.Logger; import org.exist.EXistException; /** * @author Jasper Linthorst (jasper.linthorst@gmail.com) * */ public class Counters { private final static Logger LOG = Logger.getLogger(Counters.class); private static Counters instance; public final static String COUNTERSTORE = "counters"; public final static String DELIMITER = ";"; private File store = null; private Hashtable<String, Long> counters = new Hashtable<String, Long>(); private Counters(String dataDir) throws EXistException { store = new File(dataDir, COUNTERSTORE); BufferedReader br = null; try { br = new BufferedReader(new FileReader(store)); } catch (FileNotFoundException e) { try { store.createNewFile(); br = new BufferedReader(new FileReader(store)); } catch (IOException e1) { // Failed to create an empty file, probably no write permission.. throw new EXistException("Unable to create counter store file."); } } try { if (store.exists() && store.canRead()) { String line = ""; while ((line = br.readLine()) != null) { //Use ; as a DELIMITER, counter names must be tested and rejected when they contain this character! String[] tokens = line.split(DELIMITER); counters.put(tokens[0], Long.parseLong(tokens[1])); } br.close(); } } catch (IOException e) { throw new EXistException("IOException occurred when reading counter store file."); } catch (NumberFormatException e) { throw new EXistException("Corrupt counter store file: " + store.getAbsolutePath()); } catch (ArrayIndexOutOfBoundsException e) { throw new EXistException("Corrupt counter store file: " + store.getAbsolutePath()); } } /** * Get singleton of Counters object. */ public static Counters getInstance(String dataDir) throws EXistException { if (instance == null) { LOG.debug("Initializing counters."); instance = new Counters(dataDir); } return instance; } /** * Creates a new Counter, initializes it to 0 and returns the current value in a long. * * @param counterName * @return the initial value of the newly created counter * @throws EXistException */ public long createCounter(String counterName) throws EXistException { return createCounter(counterName, (long) 0); } /** * Creates a new Counter, initializes it to initValue and returns the current value in a long. * If there already is a counter with the same name, the current value of this counter is returned. * * @param counterName * @param initValue * @return the current value of the named counter * @throws EXistException */ public synchronized long createCounter(String counterName, long initValue) throws EXistException { if (counters.containsKey(counterName)) { return counters.get(counterName); } else { counters.put(counterName, initValue); try { serializeTable(); } catch (FileNotFoundException e) { throw new EXistException("Unable to save to counter store file."); } return counters.get(counterName); } } /** * Removes a counter by the specified name. * * @param counterName * @return true if the counter is removed * @throws EXistException */ public synchronized boolean destroyCounter(String counterName) throws EXistException { if (counters.containsKey(counterName)) { counters.remove(counterName); try { serializeTable(); } catch (FileNotFoundException e) { throw new EXistException("Unable to remove counter from counter store file."); } return true; } else { return false; } } /** * Retrieves the next value of a counter (specified by name). * * @param counterName * @return the next counter value or -1 if the counter does not exist. * @throws EXistException */ public synchronized long nextValue(String counterName) throws EXistException { if (!counters.containsKey(counterName)) { return -1; } long c = counters.get(counterName); c++; counters.put(counterName, c); try { serializeTable(); } catch (FileNotFoundException e) { throw new EXistException("Unable to save to counter store file."); } return c; } /** * Returns all available counters in a Set of Strings. * * @return all available counters in a Set of Strings */ public Set<String> availableCounters() { return counters.keySet(); } /** * Serializes the Map with counters to the filesystem. * * @throws FileNotFoundException */ private synchronized void serializeTable() throws FileNotFoundException { PrintWriter p = new PrintWriter(store); Iterator<String> i = counters.keySet().iterator(); String k = ""; long v = 0; while (i.hasNext()) { k = (String) i.next(); v = counters.get(k); p.println(k + DELIMITER + v); } p.close(); } }