avatar

955

Fix a few TODO's and some general cleanup. by mrs, 14 Jun, 2009 09:30 AM
Diff this changeset:
AuctionsManager.java
mrs      1   package com.jbidwatcher.ui;
cyberfox 2   /*
cyberfox 3    * Copyright (c) 2000-2007, CyberFOX Software, Inc. All Rights Reserved.
cyberfox 4    *
cyberfox 5    * Developed by mrs (Morgan Schweers)
cyberfox 6    */
cyberfox 7   
cyberfox 8   /*!@class AuctionsManager
cyberfox 9    * @brief AuctionsManager abstracts group functionality for all
cyberfox 10   * managed groups of auctions
cyberfox 11   *
cyberfox 12   * So, for example, it supports searching all groups of auctions for
cyberfox 13   * outstanding snipes, for snipes that need to fire, for removing,
cyberfox 14   * verifying, adding, and retrieving auctions, and similar features
cyberfox 15   */
cyberfox 16  
mrs      17  import com.jbidwatcher.util.config.*;
mrs      18  import com.jbidwatcher.util.queue.*;
mrs      19  import com.jbidwatcher.util.xml.XMLElement;
mrs      20  import com.jbidwatcher.util.xml.XMLParseException;
mrs      21  import com.jbidwatcher.util.Constants;
cyberfox 22  import com.jbidwatcher.auction.server.AuctionServerManager;
cyberfox 23  import com.jbidwatcher.auction.server.AuctionStats;
mrs      24  import com.jbidwatcher.auction.server.AuctionServer;
mrs      25  import com.jbidwatcher.auction.*;
cyberfox 26  
cyberfox 27  import java.io.*;
cyberfox 28  import java.util.*;
cyberfox 29  import java.text.SimpleDateFormat;
cyberfox 30  
cyberfox 31  /** @noinspection Singleton*/
mrs      32  public class AuctionsManager implements TimerHandler.WakeupProcess, EntryManager, JConfig.ConfigListener {
mrs      33    private static AuctionsManager mInstance = null;
mrs      34    private FilterManager mFilter;
cyberfox 35  
cyberfox 36    //  Checkpoint (save) every N minutes where N is configurable.
mrs      37    private long mCheckpointFrequency;
mrs      38    private long mLastCheckpointed = 0;
cyberfox 39    private static final int AUCTIONCOUNT = 100;
cyberfox 40    private static final int MAX_PERCENT = AUCTIONCOUNT;
mrs      41    private static TimerHandler sTimer;
cyberfox 42  
cyberfox 43    /**
cyberfox 44     * @brief AuctionsManager is a singleton, there should only be one
cyberfox 45     * in the system.
cyberfox 46     */
cyberfox 47    private AuctionsManager() {
cyberfox 48      //  This should be loaded from the configuration settings.
mrs      49      mCheckpointFrequency = 10 * Constants.ONE_MINUTE;
mrs      50      mLastCheckpointed = System.currentTimeMillis();
cyberfox 51  
mrs      52      mFilter = FilterManager.getInstance();
cyberfox 53    }
cyberfox 54  
cyberfox 55    static {
mrs      56      mInstance = new AuctionsManager();
cyberfox 57    }
cyberfox 58  
cyberfox 59    /**
cyberfox 60     * @brief The means of getting access to the functions of
cyberfox 61     * AuctionsManager, as a Singleton.
cyberfox 62     * 
cyberfox 63     * @return The one reference to this object.
cyberfox 64     */
cyberfox 65    public static AuctionsManager getInstance() {
mrs      66      return mInstance;
cyberfox 67    }
cyberfox 68  
cyberfox 69    /////////////////////////////////////////////////////////
cyberfox 70    //  Mass-equivalents for Auction-list specific operations
cyberfox 71  
cyberfox 72    /**
cyberfox 73     * @brief Check if it's time to save the auctions out yet.
cyberfox 74     */
cyberfox 75    private void checkSnapshot() {
mrs      76      if( (mLastCheckpointed + mCheckpointFrequency) < System.currentTimeMillis() ) {
mrs      77        mLastCheckpointed = System.currentTimeMillis();
cyberfox 78        saveAuctions();
mrs      79        System.gc();
cyberfox 80      }
cyberfox 81    }
cyberfox 82  
cyberfox 83    /**
cyberfox 84     * @brief Check all the auctions for active events, and check if we
cyberfox 85     * should snapshot the auctions off to disk.
cyberfox 86     * 
cyberfox 87     * @return True if it's time to update in one of the auction
cyberfox 88     * collections.
cyberfox 89     */
cyberfox 90    public boolean check() {
cyberfox 91      //  The auctions themselves will decide which action this is,
cyberfox 92      //  snipe-checks, or updating.
mrs      93      boolean retval = ListManager.getInstance().checkEachList();
cyberfox 94  
cyberfox 95      checkSnapshot();
cyberfox 96  
cyberfox 97      return retval;
cyberfox 98    }
cyberfox 99  
cyberfox 100   /**
cyberfox 101    * @brief Verify that an auction entry exists.
cyberfox 102    *
cyberfox 103    * This should query the filter manager instead of doing it itself.
cyberfox 104    * This would let FilterManager handle all this, and AuctionsManager
cyberfox 105    * wouldn't need to know anything too much about the items in the
cyberfox 106    * auction lists.
cyberfox 107    *
cyberfox 108    * @note Both Verify and Get should proxy to FilterManager!
cyberfox 109    * FUTURE FEATURE -- mrs: 29-September-2001 14:59
cyberfox 110    * 
cyberfox 111    * @param id - The auction id to search for.
cyberfox 112    * 
cyberfox 113    * @return - True if the item exists someplace in our list of Auctions.
cyberfox 114    */
cyberfox 115   public boolean verifyEntry(String id) {
mrs      116     return AuctionEntry.findByIdentifier(id) != null;
cyberfox 117   }
cyberfox 118 
cyberfox 119   /**
cyberfox 120    * @brief Add a new auction entry to the set.
cyberfox 121    *
cyberfox 122    * This is complex mainly because the splash screen needs to be
cyberfox 123    * updated if we're loading from XML at startup, and because the new
cyberfox 124    * auction type needs to be split across the hardcoded auction
cyberfox 125    * collection types.
cyberfox 126    *
cyberfox 127    * @param ae - The auction entry to add.
cyberfox 128    */
cyberfox 129   public void addEntry(AuctionEntry ae) {
mrs      130     mFilter.addAuction(ae);
cyberfox 131   }
cyberfox 132 
cyberfox 133   /**
cyberfox 134    * @brief Delete from ALL auction lists!
cyberfox 135    *
cyberfox 136    * The FilterManager does this, as it needs to be internally
cyberfox 137    * self-consistent.
cyberfox 138    * 
cyberfox 139    * @param ae - The auction entry to delete.
cyberfox 140    */
cyberfox 141   public void delEntry(AuctionEntry ae) {
cyberfox 142     String id = ae.getIdentifier();
mrs      143     DeletedEntry.create(id);
cyberfox 144     ae.cancelSnipe(false);
mrs      145     mFilter.deleteAuction(ae);
mrs      146     ae.delete();
cyberfox 147   }
cyberfox 148 
cyberfox 149   /**
cyberfox 150    * @brief Return an iterator useful for iterating over all the
cyberfox 151    * auction lists managed.
cyberfox 152    * 
cyberfox 153    * @return An iterator pointing to the first auction list.
cyberfox 154    */
cyberfox 155   public static Iterator<AuctionEntry> getAuctionIterator() {
cyberfox 156     return FilterManager.getInstance().getAuctionIterator();
cyberfox 157   }
cyberfox 158 
mrs      159   public void loadAuctionsFromDB() {
mrs      160     int auctionTotal = AuctionServerManager.getInstance().getServer().getCount();
mrs      161     MQFactory.getConcrete("splash").enqueue("SET 0");
mrs      162     MQFactory.getConcrete("splash").enqueue("WIDTH " + auctionTotal);
mrs      163 
mrs      164     MQFactory.getConcrete("splash").enqueue("SET 100");
mrs      165   }
mrs      166 
cyberfox 167   /**
cyberfox 168    * @brief Load auctions from a save file, with a pretty splash
cyberfox 169    * screen and everything, if necessary.
cyberfox 170    * 
cyberfox 171    * I'd like to abstract this, and make it work with arbitrary
cyberfox 172    * streams, so that we could send an XML file of auctions over a
cyberfox 173    * network to sync between JBidwatcher instances.
cyberfox 174    */
mrs      175   public void loadAuctions() {
cyberfox 176     XMLElement xmlFile = new XMLElement(true);
cyberfox 177     String loadFile = JConfig.queryConfiguration("savefile", "auctions.xml");
cyberfox 178     String oldLoad = loadFile;
cyberfox 179 
cyberfox 180     loadFile = JConfig.getCanonicalFile(loadFile, "jbidwatcher", true);
cyberfox 181     if(!loadFile.equals(oldLoad)) {
cyberfox 182       JConfig.setConfiguration("savefile", loadFile);
cyberfox 183     }
cyberfox 184 
cyberfox 185     File toLoad = new File(loadFile);
cyberfox 186     if(toLoad.exists() && toLoad.length() != 0) {
cyberfox 187       try {
cyberfox 188         loadXMLFromFile(loadFile, xmlFile);
cyberfox 189       } catch(IOException ioe) {
mrs      190         JConfig.log().handleException("A serious problem occurred trying to load from auctions.xml.", ioe);
cyberfox 191         MQFactory.getConcrete("Swing").enqueue("ERROR Failure to load your saved auctions.  Some or all items may be missing.");
cyberfox 192       } catch(XMLParseException xme) {
mrs      193         JConfig.log().handleException("Trying to load from auctions.xml.", xme);
cyberfox 194         MQFactory.getConcrete("Swing").enqueue("ERROR Failure to load your saved auctions.  Some or all items may be missing.");
cyberfox 195       }
cyberfox 196     } else {
cyberfox 197       //  This is a common thing, and we don't want to frighten new
cyberfox 198       //  users, who are most likely to see it.
mrs      199       JConfig.log().logDebug("JBW: Failed to load saved auctions, the auctions file is probably not there yet.");
mrs      200       JConfig.log().logDebug("JBW: This is not an error, unless you're constantly getting it.");
cyberfox 201     }
cyberfox 202   }
cyberfox 203 
mrs      204   public int loadAuctionsFromDatabase() {
mrs      205     int totalCount = AuctionInfo.count();
mrs      206     int activeCount = AuctionEntry.activeCount();
mrs      207 
mrs      208     MQFactory.getConcrete("splash").enqueue("WIDTH " + activeCount);
mrs      209     MQFactory.getConcrete("splash").enqueue("SET 0");
mrs      210 
mrs      211     AuctionServer newServer = AuctionServerManager.getInstance().getServer();
mrs      212     AuctionServerManager.setEntryManager(this);
mrs      213     if (totalCount == 0) {
mrs      214       if(JConfig.queryConfiguration("stats.auctions") == null) JConfig.setConfiguration("stats.auctions", "0");
mrs      215       return totalCount;
mrs      216     }
mrs      217 
mrs      218     AuctionServerManager.getInstance().loadAuctionsFromDB(newServer);
mrs      219     AuctionStats as = AuctionServerManager.getInstance().getStats();
mrs      220 
mrs      221     int savedCount = Integer.parseInt(JConfig.queryConfiguration("last.auctioncount", "-1"));
mrs      222     if (as != null) {
mrs      223       if (as.getCount() != activeCount || (savedCount != -1 && as.getCount() != savedCount)) {
mrs      224         MQFactory.getConcrete("Swing").enqueue("NOTIFY Failed to load all auctions.");
mrs      225       }
mrs      226     }
mrs      227 
mrs      228     return activeCount;
mrs      229   }
mrs      230 
cyberfox 231   private void loadXMLFromFile(String loadFile, XMLElement xmlFile) throws IOException {
cyberfox 232     InputStreamReader isr = new InputStreamReader(new FileInputStream(loadFile));
mrs      233     MQFactory.getConcrete("splash").enqueue("WIDTH " + MAX_PERCENT);
mrs      234     MQFactory.getConcrete("splash").enqueue("SET " + MAX_PERCENT / 2);
cyberfox 235 
cyberfox 236     xmlFile.parseFromReader(isr);
mrs      237     MQFactory.getConcrete("splash").enqueue("SET " + MAX_PERCENT);
cyberfox 238 
mrs      239     String formatVersion = xmlFile.getProperty("FORMAT", "0101");
mrs      240     XMLElement auctionsXML = xmlFile.getChild("auctions");
cyberfox 241     JConfig.setConfiguration("savefile.format", formatVersion);
cyberfox 242     //  set the width of the splash progress bar based on the number
cyberfox 243     //  of auctions that will be loaded!
cyberfox 244     if (auctionsXML == null) {
cyberfox 245       throw new XMLParseException(xmlFile.getTagName(), "AuctionsManager requires an <auctions> tag!");
cyberfox 246     }
cyberfox 247     String auctionQuantity = auctionsXML.getProperty("COUNT", null);
cyberfox 248 
cyberfox 249     int auctionTotal = 0;
cyberfox 250     if(auctionQuantity != null) {
cyberfox 251       auctionTotal = Integer.parseInt(auctionQuantity);
mrs      252       MQFactory.getConcrete("splash").enqueue("SET 0");
mrs      253       MQFactory.getConcrete("splash").enqueue("WIDTH " + auctionTotal);
cyberfox 254     }
cyberfox 255 
cyberfox 256     AuctionServerManager.setEntryManager(this);
cyberfox 257     AuctionServerManager.getInstance().fromXML(auctionsXML);
mrs      258 
cyberfox 259     AuctionStats as = AuctionServerManager.getInstance().getStats();
mrs      260 
cyberfox 261     int savedCount = Integer.parseInt(JConfig.queryConfiguration("last.auctioncount", "-1"));
mrs      262     if(as != null) {
mrs      263       if(as.getCount() != auctionTotal || (savedCount != -1 && as.getCount() != savedCount)) {
mrs      264         MQFactory.getConcrete("Swing").enqueue("NOTIFY Failed to load all auctions.");
mrs      265       }
cyberfox 266     }
cyberfox 267   }
cyberfox 268 
mrs      269   //  Reuse a single save buffer when writing out the auctions.xml file.
cyberfox 270   private static final int ONEK = 1024;
cyberfox 271   private static final StringBuffer _saveBuf = new StringBuffer(AUCTIONCOUNT *ONEK);
cyberfox 272 
cyberfox 273   /**
cyberfox 274    * @brief Save auctions out to the savefile, in XML format.
cyberfox 275    *
cyberfox 276    * Similar to the loadAuctions code, this would be nice if it were
cyberfox 277    * abstracted to write to any outputstream, allowing us to write to
cyberfox 278    * a remote node to update it with our auctions and snipes.
cyberfox 279    * 
cyberfox 280    * @return - true if it successfully saved, false if an error occurred.
cyberfox 281    */
cyberfox 282   public boolean saveAuctions() {
cyberfox 283     XMLElement auctionsData = AuctionServerManager.getInstance().toXML();
cyberfox 284     String oldSave = JConfig.queryConfiguration("savefile", "auctions.xml");
cyberfox 285     String saveFilename = JConfig.getCanonicalFile(JConfig.queryConfiguration("savefile", "auctions.xml"), "jbidwatcher", false);
cyberfox 286     String newSave=saveFilename;
cyberfox 287 
mrs      288     //  If there's no data to save, then pretend we did it.
mrs      289     if(auctionsData == null) return true;
mrs      290 
cyberfox 291     ensureDirectories(saveFilename);
cyberfox 292 
cyberfox 293     boolean swapFiles = needSwapSaves(saveFilename);
cyberfox 294 
cyberfox 295     if(!saveFilename.equals(oldSave)) {
cyberfox 296       JConfig.setConfiguration("savefile", saveFilename);
cyberfox 297     }
cyberfox 298 
cyberfox 299     //  If we already have a save file, preserve its name, and write
cyberfox 300     //  the new one to '.temp'.
cyberfox 301     if(swapFiles) {
cyberfox 302       newSave = saveFilename + ".temp";
cyberfox 303       File newSaveFile = new File(newSave);
cyberfox 304       if(newSaveFile.exists()) newSaveFile.delete();
cyberfox 305     }
cyberfox 306 
mrs      307     StringBuffer buf = buildSaveBuffer(auctionsData, null);
cyberfox 308     boolean saveDone = true;
cyberfox 309 
cyberfox 310     //  Dump the save file out!
cyberfox 311     try {
cyberfox 312       PrintStream ps = new PrintStream(new FileOutputStream(newSave));
mrs      313       ps.println(buf);
cyberfox 314       ps.close();
cyberfox 315     } catch(IOException e) {
mrs      316       JConfig.log().handleException("Failed to save auctions.", e);
cyberfox 317       saveDone = false;
cyberfox 318     }
cyberfox 319 
cyberfox 320     //  If the save was complete, and we have to swap old/new files,
cyberfox 321     //  then [remove prior '.old' file if necessary], save current XML
cyberfox 322     //  as '.old', and move most recent save file to be just a normal
cyberfox 323     //  save file.
cyberfox 324     if(saveDone && swapFiles) {
cyberfox 325       preserveFiles(saveFilename);
cyberfox 326     }
cyberfox 327 
cyberfox 328     return saveDone;
cyberfox 329   }
cyberfox 330 
mrs      331   public int clearDeleted() {
mrs      332     int rval = DeletedEntry.clear();
mrs      333 
mrs      334     saveAuctions();
mrs      335     System.gc();
mrs      336 
mrs      337     return rval;
mrs      338   }
mrs      339 
cyberfox 340   private static void ensureDirectories(String saveFilename) {
cyberfox 341     //  Thanks to Gabor Liptak for this recommendation...
cyberfox 342     File saveParent = new File(saveFilename);
cyberfox 343     saveParent = saveParent.getParentFile();
cyberfox 344     if(!saveParent.exists()) saveParent.mkdirs(); //  This can fail, but we don't mind.
cyberfox 345   }
cyberfox 346 
cyberfox 347   public static StringBuffer buildSaveBuffer(XMLElement auctionsData, XMLElement deletedData) {
cyberfox 348     synchronized(_saveBuf) {
cyberfox 349       _saveBuf.setLength(0);
cyberfox 350       _saveBuf.append("<?xml version=\"1.0\"?>\n\n");
mrs      351       _saveBuf.append(Constants.XML_SAVE_DOCTYPE);
cyberfox 352       _saveBuf.append('\n');
cyberfox 353       _saveBuf.append("<jbidwatcher format=\"0101\">\n");
cyberfox 354       auctionsData.toStringBuffer(_saveBuf, 1);
cyberfox 355       if(deletedData != null) {
cyberfox 356         deletedData.toStringBuffer(_saveBuf, 1);
cyberfox 357       }
cyberfox 358       _saveBuf.append("</jbidwatcher>");
cyberfox 359     }
cyberfox 360     return _saveBuf;
cyberfox 361   }
cyberfox 362 
cyberfox 363   private static boolean needSwapSaves(String saveName) {
cyberfox 364     File oldFile = new File(saveName);
cyberfox 365     return oldFile.exists();
cyberfox 366   }
cyberfox 367 
cyberfox 368   private static void preserveFiles(String filename) {
cyberfox 369     File oldFile = new File(filename);
cyberfox 370     File saveFile = new File(filename + ".temp");
cyberfox 371     SimpleDateFormat sdf = new SimpleDateFormat("ddMMMyy_HHmm");
cyberfox 372     String nowStr = sdf.format(new Date());
cyberfox 373     String retainFilename = makeBackupFilename(filename, nowStr);
cyberfox 374     File retainFile = new File(retainFilename);
cyberfox 375     if(retainFile.exists()) retainFile.delete();
cyberfox 376 
cyberfox 377     String oldestSave = JConfig.queryConfiguration("save.file.4", "");
cyberfox 378     if(oldestSave.length() != 0) {
cyberfox 379       File oldest = new File(oldestSave);
cyberfox 380       if(oldest.exists()) {
cyberfox 381         backupByDate(filename, oldest);
cyberfox 382       }
cyberfox 383     }
cyberfox 384 
cyberfox 385     for(int i=4; i>0; i--) {
cyberfox 386       JConfig.setConfiguration("save.file." + i, JConfig.queryConfiguration("save.file." + (i-1), ""));
cyberfox 387     }
cyberfox 388 
cyberfox 389     File keepFile = new File(retainFilename);
cyberfox 390     if(!oldFile.renameTo(keepFile)) {
mrs      391       JConfig.log().logDebug("Renaming the old file (" + oldFile + ") to the retain file (" + keepFile + ") failed!");
cyberfox 392     }
cyberfox 393     JConfig.setConfiguration("save.file.0", retainFilename);
cyberfox 394 
cyberfox 395     File standard = new File(filename);
cyberfox 396     if(!saveFile.renameTo(standard)) {
mrs      397       JConfig.log().logDebug("Renaming the new file (" + saveFile + ") to the standard filename (" + standard + ") failed!");
cyberfox 398     }
cyberfox 399   }
cyberfox 400 
cyberfox 401   private static void backupByDate(String filename, File oldest) {
cyberfox 402     SimpleDateFormat justDateFmt = new SimpleDateFormat("ddMMMyy");
cyberfox 403     String justDate = justDateFmt.format(new Date());
cyberfox 404     String oldBackup = makeBackupFilename(filename, justDate);
cyberfox 405     File oldDateBackup = new File(oldBackup);
cyberfox 406     if(oldDateBackup.exists()) {
cyberfox 407       oldDateBackup.delete();
cyberfox 408       File newDateBackup = new File(oldBackup);
cyberfox 409       oldest.renameTo(newDateBackup);
cyberfox 410     } else {
cyberfox 411       oldest.renameTo(oldDateBackup);
cyberfox 412       String oldestByDate = JConfig.queryConfiguration("save.bydate.4", "");
cyberfox 413       for(int i=4; i>0; i--) {
cyberfox 414         JConfig.setConfiguration("save.bydate." + i, JConfig.queryConfiguration("save.bydate." + (i-1), ""));
cyberfox 415       }
cyberfox 416       JConfig.setConfiguration("save.bydate.0", oldBackup);
cyberfox 417       File deleteMe = new File(oldestByDate);
cyberfox 418       deleteMe.delete();
cyberfox 419     }
cyberfox 420   }
cyberfox 421 
cyberfox 422   private static String makeBackupFilename(String filename, String toInsert) {
cyberfox 423     int lastSlash = filename.lastIndexOf(System.getProperty("file.separator"));
cyberfox 424     if(lastSlash == -1) {
mrs      425       JConfig.log().logDebug("Filename has no separators: " + filename);
cyberfox 426       lastSlash = 0;
cyberfox 427     }
cyberfox 428     int firstDot = filename.indexOf('.', lastSlash);
cyberfox 429     if(firstDot == -1) {
mrs      430       JConfig.log().logDebug("Filename has no dot/extension: " + filename);
cyberfox 431       firstDot = filename.length();
cyberfox 432     }
cyberfox 433 
cyberfox 434     return filename.substring(0, firstDot) + '-' + toInsert + filename.substring(firstDot);
cyberfox 435   }
mrs      436 
mrs      437   public static void start() {
mrs      438     if(sTimer == null) {
mrs      439       sTimer = new TimerHandler(getInstance());
mrs      440       sTimer.setName("Updates");
mrs      441       sTimer.start();
mrs      442     }
mrs      443     JConfig.registerListener(getInstance());
mrs      444   }
mrs      445 
mrs      446   public void updateConfiguration() {
mrs      447     String newSnipeTime = JConfig.queryConfiguration("snipemilliseconds");
mrs      448     if(newSnipeTime != null) {
mrs      449       AuctionEntry.setDefaultSnipeTime(Long.parseLong(newSnipeTime));
mrs      450     }
mrs      451   }
cyberfox 452 }

Check out the code: svn co jbidwatcher/trunk/src/com/jbidwatcher/ui/AuctionsManager.java