- ~
- jbidwatcher
- trunk
- src
- com
- jbidwatcher
- ui
- AuctionsManager.java
| 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
