package com.jbidwatcher.app;
/*
* Copyright (c) 2000-2007, CyberFOX Software, Inc. All Rights Reserved.
*
* Developed by mrs (Morgan Schweers)
*/
import com.cyberfox.util.platform.Path;
import com.cyberfox.util.platform.Platform;
import com.cyberfox.util.platform.osx.NoNap;
import com.google.inject.*;
import com.jbidwatcher.auction.*;
import com.jbidwatcher.auction.server.AuctionServerFactory;
import com.jbidwatcher.auction.server.AuctionStats;
import com.jbidwatcher.platform.*;
import com.jbidwatcher.auction.server.AuctionServer;
import com.jbidwatcher.auction.server.AuctionServerManager;
import com.jbidwatcher.ui.commands.UserActions;
import com.jbidwatcher.ui.config.JConfigFrame;
import com.jbidwatcher.search.SearchManager;
import com.jbidwatcher.ui.*;
import com.jbidwatcher.ui.util.JBidFrame;
import com.jbidwatcher.ui.util.JMouseAdapter;
import com.jbidwatcher.ui.util.RuntimeInfo;
import com.jbidwatcher.util.Constants;
import com.jbidwatcher.scripting.JRubyPreloader;
import com.jbidwatcher.util.config.JBErrorManagement;
import com.jbidwatcher.util.config.JConfig;
import com.jbidwatcher.util.db.ActiveRecord;
import com.jbidwatcher.util.services.ActivityMonitor;
import com.jbidwatcher.scripting.Scripting;
import com.jbidwatcher.util.queue.*;
import com.jbidwatcher.util.services.AudioPlayer;
import com.jbidwatcher.util.services.SyncService;
import com.jbidwatcher.util.xml.XMLElement;
import com.jbidwatcher.*;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.*;
/**
* @file JBidWatch.java
* @author Morgan Schweers <cyberfox@jbidwatcher.com>
* @date Fri Oct 11 17:54:21 2002
*
* @brief The startup class, that prepares the UI, and starts all the
* threads.
*
* Perfection is reached, not when there is no longer anything to add, but
* when there is no longer anything to take away.
* -- Antoine de Saint-Exupery
*
* Primary class which holds the main, and prepares and launches
* all the subclasses and threads. It also holds the general purpose
* constants. It implements JConfigListener, which just means that
* the updateConfiguration() function will be called when the config
* changes.
* @noinspection FeatureEnvy,Singleton
*/
public final class JBidWatch implements JConfig.ConfigListener {
/** This ClassLoader is only REALLY necessary if we're loading the
* initial display.cfg and JBidWatch.cfg from the distribution .jar
* file. See JConfig.java for more details.
*/
private static ClassLoader urlCL = (ClassLoader)JBidWatch.class.getClassLoader();
private final Provider<UIBackbone> backboneProvider;
private final Provider<JConfigFrame> configFrameProvider;
@Inject
private AuctionServerFactory serverFactory;
@Inject
private PopupMenuFactory menuFactory;
private final FilterManager filters;
private final ListManager listManager;
private final AuctionsManager auctionsManager;
private SearchManager searchManager;
private EntryFactory entryFactory;
private EntryCorral corral;
private AuctionServerManager serverManager;
private Injector injector;
private final Object memInfoSynch = new Object();
private MacFriendlyFrame mainFrame;
private JTabManager jtmAuctions;
private SyncService mServiceAdvertiser;
private RuntimeInfo _rti = null;
private static boolean sUSB = false;
private boolean ebayLoaded = false;
private final Object mScriptCompletion = new Object();
private UserActions userActions;
/**
* @brief Try to guarantee a directory for saving 'cached copies'
* and eventually configuration information.
*
* First we try the passed in directory, if it doesn't exist, try to
* create it. If that fails, then try to create a 'default'
* directory. If THAT fails, we're in trouble. We return null, and
* expect everything else to handle it properly. (Not try to save
* anything there.) We also note an error in the logs...
*
* @param inPath - The 'preferred' path to use.
*
* @return - a String identifying the path to our save directory.
*/
private static String makeSaveDirectory(String inPath) {
return Path.makeStandardDirectory(inPath, "auctionsave", "jbidwatcher");
}
private static String makePlatformDirectory(String inPath) {
return Path.makeStandardDirectory(inPath, "platform", "jbidwatcher");
}
private void getUserSetup() {
JConfig.setConfiguration("config.firstrun", "true");
configFrameProvider.get().spinWait();
}
/**
* @brief Load a configuration, if possible; if not, load the
* configuration from the .jar file.
*
* The full logic sequence is as follows:
* Find the best location for the file.
* If it's not any of those, load it from the .jar file.
*
* @param inConfig - The configuration file to try to load.
*
* @return - The input stream corresponding with the best version of the provided file we can find.
*/
private static InputStream checkConfig(String inConfig) {
JConfig.setConfigurationFile(inConfig);
return JConfig.bestSource(urlCL, inConfig);
}
private static void loadConfig(InputStream configStream) {
JConfig.load(configStream);
// This MUST be run before any UI objects are addressed, if at all possible.
// In the case of the initial configuration, unfortunately it's not possible.
Platform.setupMacUI();
if(Platform.isMac()) {
NoNap.dontNapMeBro();
}
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
String dispFile = Path.getCanonicalFile("display.cfg", "jbidwatcher", true);
JConfig.loadDisplayConfig(dispFile, urlCL, screensize.width, screensize.height);
if(sUSB) JConfig.fixupPaths(JConfig.getHomeDirectory());
String aucSave = makeSaveDirectory(JConfig.queryConfiguration("auctions.savepath"));
if(aucSave != null) {
JConfig.setConfiguration("auctions.savepath", aucSave);
}
String platform = makePlatformDirectory(JConfig.queryConfiguration("platform.path"));
if(platform != null) {
JConfig.setConfiguration("platform.path", platform);
if(Platform.supportsTray()) {
Platform.setTrayEnabled(true);
}
}
}
/**
* @brief Check to see if the user tried to use any of the help parameters.
* Check for a parameter (--help or -h) to show help for.
*
* @param inArgs - The arguments passed into the command line.
*
* @return - true if the usage was displayed, false otherwise.
*/
private static boolean checkArguments(String[] inArgs) {
boolean rval = false;
for(String arg : inArgs) {
rval |= handleArgument(arg);
}
return rval;
}
private static boolean handleArgument(String arg) {
if (arg.startsWith("--help") || arg.startsWith("-h")) {
//noinspection UseOfSystemOutOrSystemErr
System.out.println("usage: java JBidWatch [{cfg-file}]");
JOptionPane.showMessageDialog(null, "<html><body>usage:<br><center>java JBidWatch [{cfg-file}]</center><br>Default user home: " +
Path.getHome() + "</body></html>", "Help display", JOptionPane.PLAIN_MESSAGE);
return true;
} else if (arg.startsWith("--usb")) {
Path.setHome(System.getProperty("user.dir"));
sUSB = true;
} else if (arg.startsWith("--testImpl")) {
System.out.println("Impl Version: " + Constants.REVISION());
System.exit(1);
}
return false;
}
/**
* @brief Set the proxy values if they are indicated by the configuration.
*
* @param inProps - The properties list to check.
*
* @return - true if proxies were set, false otherwise.
*/
private static boolean EstablishProxy(Properties inProps) {
String webProxyHost = JConfig.queryConfiguration("proxy.host", null);
String webProxyPort = JConfig.queryConfiguration("proxy.port", null);
if(JConfig.queryConfiguration("proxyfirewall", "none").equals("proxy")) {
if (webProxyHost != null && webProxyPort != null) {
inProps.setProperty("http.proxySet", "true");
inProps.setProperty("http.proxyHost", webProxyHost);
inProps.setProperty("http.proxyPort", webProxyPort);
inProps.setProperty("proxySet", "true");
inProps.setProperty("proxyHost", webProxyHost);
inProps.setProperty("proxyPort", webProxyPort);
setProxyAuthenticator();
return true;
}
}
return false;
}
private static boolean EstablishHTTPSProxy(Properties inProps) {
if(JConfig.queryConfiguration("proxy.https.set", "false").equals("true")) {
String secureProxyHost = JConfig.queryConfiguration("proxy.https.host");
String secureProxyPort = JConfig.queryConfiguration("proxy.https.port");
if(secureProxyHost != null && secureProxyPort != null) {
inProps.setProperty("https.proxySet", "true");
inProps.setProperty("https.proxyHost", secureProxyHost);
inProps.setProperty("https.proxyPort", secureProxyPort);
setProxyAuthenticator();
return true;
}
}
return false;
}
/**
* @brief Set the firewall values, if they are indicated by the configuration.
*
* @param inProps - The properties list to check.
*
* @return - true if the firewall info was set, false otherwise.
*/
private static boolean EstablishFirewall(Properties inProps) {
if(JConfig.queryConfiguration("proxyfirewall", "none").equals("firewall")) {
String socksHost = JConfig.queryConfiguration("firewall.host", null);
String socksPort = JConfig.queryConfiguration("firewall.port", "1080");// Default SOCKS port.
if(socksHost != null) {
inProps.setProperty("socksProxyHost", socksHost);
inProps.setProperty("socksProxyPort", socksPort);
setProxyAuthenticator();
return true;
}
}
return false;
}
private static void setProxyAuthenticator() {
if (!sProxyAuthenticatorAlreadySet) {
final String user = JConfig.queryConfiguration("proxy.user", null);
final String pass = JConfig.queryConfiguration("proxy.pass", null);
if (user != null && pass != null) {
Authenticator.setDefault(new Authenticator() {
@SuppressWarnings({"RefusedBequest"})
protected PasswordAuthentication getPasswordAuthentication() {
String host = getRequestingHost();
// If talking to my.jbidwatcher.com, JBidwatcher handles authentication itself.
if(host == null || !host.contains("jbidwatcher")) {
return (new PasswordAuthentication(user, pass.toCharArray()));
}
return null;
}
});
sProxyAuthenticatorAlreadySet = true;
}
}
}
private static boolean sProxyAuthenticatorAlreadySet = false;
/**
* @brief Set the UI to be used for the Swing L&F.
*
* @param whichUI - The name of the UI to use.
*
* @param inFrame - The base frame of the application, to indicate
* that the UI has changed.
*
* @param lafList - The list of look-and-feels from which to choose.
*/
private static void setUI(String whichUI, JFrame inFrame, UIManager.LookAndFeelInfo[] lafList) {
String whatLaF = null;
if(whichUI != null) {
for (UIManager.LookAndFeelInfo aLafList : lafList) {
if (whichUI.equals(aLafList.getName())) whatLaF = aLafList.getClassName();
}
}
// If we still haven't chosen a L&F, set to the system default.
if(whatLaF == null) {
if( (whatLaF = System.getProperty("swing.defaultlaf")) == null) {
whatLaF = UIManager.getSystemLookAndFeelClassName();
}
}
Platform.checkLaF(whatLaF);
if(Platform.isMac() && Platform.setQuaquaFeel(inFrame)) {
whatLaF = null;
}
if(whatLaF != null) {
try {
UIManager.setLookAndFeel(whatLaF);
} catch (Exception exMe) {
JConfig.log().handleException("Exception in setUI, failure to set " + whatLaF + ": " + exMe, exMe);
// Don't try to update the frame with the new UI.
inFrame = null;
}
}
if (inFrame != null) {
SwingUtilities.updateComponentTreeUI(inFrame);
}
}
/**
* @brief Checks for help, loads the configuration, sets up system
* properties like proxies or firewalls, initializes the splash
* screen, and launches the application proper.
*
* @param args Command line arguments.
*/
public static void main(String[] args) {
JConfig.setLogger(new JBErrorManagement());
if (checkArguments(args)) {
System.exit(0);
}
Path.setHomeDirectory("jbidwatcher");
JConfig.setVersion(Constants.PROGRAM_VERS);
System.setProperty("sun.net.client.defaultConnectTimeout", "5000");
System.setProperty("sun.net.client.defaultReadTimeout", "15000");
java.security.Security.setProperty("networkaddress.cache.ttl", "-1");
JBidWatch program = getApplication();
program.configure(args);
program.startDatabase();
program.setupSearches();
program.loadProxySettings();
JSplashScreen splashScreen = prepSplashScreen();
try {
program.run(splashScreen);
Thread.currentThread().join();
program.repaint();
} catch (Exception e) {
JConfig.log().handleException("JBidwatcher: " + e, e);
}
}
private static JBidWatch getApplication() {
AbstractModule guiceModule = new JBidwatcherModule();
Injector inject = Guice.createInjector(guiceModule);
return inject.getInstance(JBidWatch.class);
}
private void configure(String[] args) {
// Pass a parameter (other than --help or -h) to launch that as a
// configuration file.
String cfgLoad = "JBidWatch.cfg";
if (args.length != 0) {
if (args[0].charAt(0) != '-') {
cfgLoad = args[0];
}
}
cfgLoad = Path.getCanonicalFile(cfgLoad, "jbidwatcher", false);
cfgLoad = lookForNewerMacConfig(cfgLoad);
InputStream configStream = checkConfig(cfgLoad);
boolean firstRun;
boolean needUserSetup = (configStream == null);
if (needUserSetup) {
setUI(null, null, UIManager.getInstalledLookAndFeels());
// Preload the eBay server, must be done before Configuration setup
// could happen, to get the configuration tab for eBay.
eBayServerSetup();
ebayLoaded = true;
Platform.setupMacUI();
JConfig.setConfiguration("first.run", "true");
firstRun = true;
getUserSetup();
configStream = checkConfig(cfgLoad);
} else {
JConfig.setConfiguration("first.run", "false");
firstRun = false;
}
JConfig.setConfiguration("temp.cfg.load", cfgLoad);
loadConfig(configStream);
JConfig.setConfiguration("first.run", firstRun ? "true" : "false");
setUI(null, null, UIManager.getInstalledLookAndFeels());
JConfig.log().logMessage(Constants.PROGRAM_NAME + " " + Constants.PROGRAM_VERS + "-" + Constants.REVISION());
JConfig.log().logMessage(System.getProperty("java.vendor") + " Java, version " + System.getProperty("java.version") + " on " + System.getProperty("os.name"));
if (JConfig.queryConfiguration("mac", "false").equals("true")) {
JConfig.setConfiguration("temp.cfg.load", Path.getCanonicalFile("JBidWatch.cfg", "jbidwatcher", false));
}
String logFileName = JConfig.log().getLog();
if (logFileName != null) JConfig.log().logMessage("Logging to " + logFileName);
if (JConfig.queryConfiguration("show.badhtml", "false").equals("true")) {
XMLElement.rejectBadHTML(true);
}
}
private void startDatabase() {
try {
// boolean creatingDB = JConfig.queryConfiguration("jbidwatcher.created_db", "false").equals("false");
Upgrader.upgrade();
// if (creatingDB && JConfig.queryConfiguration("jbidwatcher.created_db", "false").equals("true")) {
// Ignored - We just created the database.
// }
} catch (Exception e) {
if (e.getMessage().matches("^Failed to start database.*")) {
JConfig.log().handleException("JBidwatcher can't access it's database.", e);
JOptionPane.showMessageDialog(null, "JBidwatcher can't access its database.\nPlease check to see if you are running another instance.", "Can't access auction database", JOptionPane.PLAIN_MESSAGE);
JConfig.stopMetrics(Constants.PROGRAM_VERS);
System.exit(0);
}
JConfig.log().handleException("Upgrading error", e);
}
}
@Inject
public JBidWatch(SearchManager searcher, EntryFactory entryMaker, EntryCorral holdingCell,
AuctionServerManager serverManager,
FilterManager filterManager, JTabManager tabManager, ListManager listManager, AuctionsManager auctionsManager,
UserActions userActions, Injector inject,
Provider<UIBackbone> uiBackboneProvider, Provider<JConfigFrame> configFrameProvider) {
this.searchManager = searcher;
this.corral = holdingCell;
this.entryFactory = entryMaker;
this.serverManager = serverManager;
this.filters = filterManager;
this.jtmAuctions = tabManager;
this.listManager = listManager;
this.auctionsManager = auctionsManager;
this.backboneProvider = uiBackboneProvider;
this.userActions = userActions;
this.injector = inject;
this.configFrameProvider = configFrameProvider;
AuctionEntry.addObserver(entryFactory);
MultiSnipe.setCorral(corral);
}
private void setupSearches() {// We need to load searches before adding the eBay server, so
// that it knows that a My eBay search already exists and doesn't
// try to recreate it.
searchManager.loadSearches();
if(!ebayLoaded) {
eBayServerSetup();
}
searchManager.setDestinationQueue(this.serverManager.getServer().getFriendlyName());
}
private static JSplashScreen prepSplashScreen() {// Show splash screen and progress bar.
Calendar rightNow = Calendar.getInstance();
int _mon = rightNow.get(Calendar.MONTH);
int _day = rightNow.get(Calendar.DAY_OF_MONTH);
URL imageURL;
if( (_day == 1 && _mon == Calendar.APRIL) &&
!JConfig.queryConfiguration("sniperkitty", "false").equals("true")) {
imageURL = JConfig.getResource("/jbidwatch_apr1.jpg");
JConfig.setConfiguration("sniperkitty", "true");
} else {
imageURL = JConfig.getResource(JConfig.queryConfiguration("splash", "jbidwatch.jpg"));
}
return new JSplashScreen(new ImageIcon(imageURL));
}
private static String lookForNewerMacConfig(String cfgLoad) {
if (System.getProperty("mrj.version") != null) {
String sep = System.getProperty("file.separator");
String macHome = Path.getMacHomeDirectory("jbidwatcher");
String macCfg = macHome + sep + "JBidWatch.cfg";
File mac = new File(macCfg);
File cfg = new File(cfgLoad);
if(mac.lastModified() > cfg.lastModified()) cfgLoad = macCfg;
}
return cfgLoad;
}
private void eBayServerSetup() {
boolean nonUS = JConfig.queryConfiguration("ebay.non_us", Boolean.toString(!Platform.isUSBased())).equals("true");
String homeSite = nonUS ? JConfig.queryConfiguration("ebay.alternate", "ebay.co.uk") : "ebay.com";
AuctionServer ebay = serverFactory.create(homeSite, null, null);
serverManager.setServer(ebay);
}
private void repaint() {
mainFrame.repaint();
}
private void loadProxySettings() {
Properties sysProps = System.getProperties();
boolean proxied = EstablishProxy(sysProps);
boolean firewalled = EstablishFirewall(sysProps);
boolean secured = EstablishHTTPSProxy(sysProps);
boolean sysPropsChanged = proxied || firewalled || secured;
if(sysPropsChanged) System.setProperties(sysProps);
}
/**
* @brief Callback called by JConfig when the configuration changes.
*
* Also, we check the background color, and set it appropriately.
*/
public final void updateConfiguration() {
String savedBGColor = JConfig.queryConfiguration("background", "false");
if(!savedBGColor.equals("false")) {
listManager.setBackground(Color.decode('#' + savedBGColor));
}
// Enable the internal server, if it's set.
if(JConfig.queryConfiguration("server.enabled", "false").equals("true")) {
mServiceAdvertiser = new SyncService(9099);
mServiceAdvertiser.advertise();
} else {
if(mServiceAdvertiser != null) {
mServiceAdvertiser.stopAdvertising();
}
}
loadProxySettings();
synchronized (memInfoSynch) {
if (JConfig.queryConfiguration("debug.memory", "false").equals("true")) {
if (_rti == null) {
_rti = new RuntimeInfo();
} else {
_rti.setVisible(true);
}
} else {
if (_rti != null) _rti.setVisible(false);
}
}
}
private MacFriendlyFrame buildFrame() {
URL iconURL = JConfig.getResource(JConfig.queryConfiguration("icon", "jbidwatch64.jpg"));
JMouseAdapter myFrameAdapter = injector.getInstance(JBidFrameMouse.class);
return new MacFriendlyFrame(injector.getInstance(JBidToolBar.class), "JBidwatcher", myFrameAdapter, iconURL, jtmAuctions);
}
/**
* @brief Load the saved auctions, build the UI frame, close down
* the splash screen, and start the monitor and update threads.
*
* @param inSplash Splash screen with a status bar, to be updated during startup.
*
* @noinspection CallToThreadStartDuringObjectConstruction
*/
private void run(final JSplashScreen inSplash) {
inSplash.message("Initializing Monitors");
ActivityMonitor.start();
MQFactory.getConcrete("login").registerListener(new MessageQueue.Listener() {
public void messageAction(Object deQ) {
MQFactory.getConcrete("Swing").enqueue("LOGINSTATUS " + deQ.toString());
}
});
ThumbnailLoader.start();
inSplash.message("Initializing Scripting");
JRubyPreloader preloader = new JRubyPreloader(mScriptCompletion);
Thread scriptLoading = new Thread(preloader);
scriptLoading.start();
inSplash.message("Initializing Database");
Initializer.setup(jtmAuctions, listManager, menuFactory);
filters.loadFilters();
inSplash.message("Loading Auctions");
auctionsManager.loadAuctionsFromDatabase();
serverManager.getDefaultServerTime();
JConfig.registerListener(this);
Browser.start();
MQFactory.getConcrete("user").registerListener(userActions);
inSplash.message("Building Interface");
JBidFrame.setDefaultMenuBar(JBidMenuBar.getInstance(menuFactory, jtmAuctions.getTabs(), jtmAuctions, "Search Editor"));
mainFrame = buildFrame();
mainFrame.setLocation(JConfig.screenx, JConfig.screeny);
mainFrame.setSize(JConfig.width, JConfig.height);
backboneProvider.get().setMainFrame(mainFrame);
try {
preloader.finish(inSplash, serverManager, auctionsManager, filters, ()->inSplash.message("Starting scripts"));
} catch(Exception e) {
JOptionPane.showMessageDialog(null, "<html><body>JBidwatcher is unable to load its scripting layer; as of 3.0<br>" +
"(and pre-releases) and later, scripting is a core part of JBidwatcher<br>" +
"and it will not run without it.</body</html>", "Scripting Error", JOptionPane.ERROR_MESSAGE);
}
inSplash.close();
jtmAuctions.sortDefault();
mainFrame.setVisible(true);
// Construct the tray object, so that we can interact with the system tray.
if(Platform.supportsTray()) {
Tray.start();
if(JConfig.queryConfiguration("windows.tray", "true").equals("true")) {
MQFactory.getConcrete("tray").enqueue("TRAY on");
}
}
// Start any servers if necessary, and set the background colors,
// and anything else we need to load from the configuration file.
updateConfiguration();
MQFactory.getConcrete("run-script").registerListener(new MessageQueue.Listener() {
@Override
public void messageAction(Object deQ) {
Scripting.ruby((String) deQ);
}
});
SuperQueue sq = SuperQueue.getInstance();
preQueueServices(sq);
final TimerHandler timeQueue = sq.start();
// This is how we shut down cleanly.
MQFactory.getConcrete("jbidwatcher").registerListener(new MessageQueue.Listener() {
public void messageAction(Object deQ) {
timeQueue.interrupt();
}
});
UpdateManager.start();
AudioPlayer.start();
synchronized(memInfoSynch) { if(_rti == null && JConfig.queryConfiguration("debug.memory", "false").equals("true")) _rti = new RuntimeInfo(); }
try {
// Don't leave this thread until the timeQueue has completed; i.e. the program is exiting.
timeQueue.join();
} catch (InterruptedException e) {
JConfig.log().handleException("timeQueue interrupted", e);
}
internal_shutdown();
JConfig.stopMetrics(Constants.PROGRAM_VERS);
System.exit(0);
}
private void preQueueServices(SuperQueue q) {
long now = System.currentTimeMillis();
if (JConfig.queryConfiguration("updates.enabled", "true").equals("true")) {
q.preQueue("AUTOMATIC", "update", now + (Constants.ONE_SECOND * 10));
}
//noinspection MultiplyOrDivideByPowerOfTwo
if (JConfig.queryConfiguration("timesync.enabled", "true").equals("true")) {
q.preQueue("TIMECHECK", "auction_manager", now + (Constants.ONE_SECOND * 2), Constants.THIRTY_MINUTES);
}
// TODO mrs - This is where things start to suck. Can this become a single VERB+NOUN operation?
q.preQueue(new AuctionQObject(AuctionQObject.MENU_CMD, AuctionServer.UPDATE_LOGIN_COOKIE, null),
serverManager.getServer().getFriendlyName(),
now + Constants.ONE_SECOND * 3,
481 * Constants.ONE_MINUTE + Constants.ONE_SECOND * 17);
q.preQueue("ALLOW_UPDATES", "Swing", now + (Constants.ONE_SECOND * 20));
establishMetrics(q, now);
q.preQueue("JBidwatcher.after_startup", "run-script", now + (Constants.ONE_SECOND * 2));
}
/**
* Prompt the user to allow metrics, if they haven't been asked before. Set up a daily flush, which only happens if the user
* opts in.
*
* @param q - The super-queue to put the one-time and recurring events on.
* @param now - What time is it now, so everything starts with the same time-base.
*/
private void establishMetrics(SuperQueue q, long now) {
// Ask the user to allow anonymized statistics gathering.
if(JConfig.queryConfiguration("metrics.optin", "ask").equals("ask")) {
q.preQueue("Metrics", "user", now + (Constants.ONE_SECOND * 5));
}
MQFactory.getConcrete("metrics").registerListener(new MessageQueue.Listener() {
public void messageAction(Object deQ) {
if(JConfig.sendMetricsAllowed(Constants.PROGRAM_VERS)) {
try {
JConfig.getMetrics().flush();
} catch (IOException e) {
JConfig.log().handleDebugException("Couldn't flush analytics to the server.", e);
}
}
}
});
q.preQueue("Flush Metrics", "metrics", now + Constants.ONE_DAY, Constants.ONE_DAY);
}
/**
* @return A property table of all the table column header information, suitable for saving.
* @brief Obtains a 'property list' of all the column widths, names,
* etc., in order to save them off so the UI can remain
* approximately the same between executions.
*/
private Properties getColumnProperties() {
Properties colProps = new Properties();
colProps = listManager.extractProperties(colProps);
return (colProps);
}
private void internal_shutdown() {
// Shut down internal timers
try {
if(mServiceAdvertiser != null) mServiceAdvertiser.stop();
for (Object o : JConfig.getTimers()) {
((TimerHandler) o).interrupt();
try { ((TimerHandler) o).join(); } catch (InterruptedException ignored) {}
}
Properties colProps = getColumnProperties();
searchManager.saveSearchDisplay();
Properties displayProps = UISnapshot.snapshotLocation(mainFrame);
String dispFile = Path.getCanonicalFile("display.cfg", "jbidwatcher", false);
JConfig.saveDisplayConfig(dispFile, displayProps, colProps);
// Save it to the original file, if it was provided at runtime,
// otherwise to the appropriate default.
String cfgLoad = JConfig.queryConfiguration("temp.cfg.load", "JBidWatch.cfg");
String cfgFilename = cfgLoad.equals("JBidWatch.cfg") ? Path.getCanonicalFile(cfgLoad, "jbidwatcher", false) : cfgLoad;
// TODO -- Need to save searches in the database too... Right now they're still hanging around in XML form.
searchManager.saveSearches();
AuctionStats as = serverManager.getStats();
JConfig.setConfiguration("last.auctioncount", Integer.toString(as.getCount()));
JConfig.saveConfiguration(cfgFilename);
ActiveRecord.shutdown();
} catch(Exception e) {
JConfig.log().handleException("Threw an error during shutdown! Shutting down anyway!", e);
} finally {
JConfig.log().logMessage("Shutting down JBidwatcher.");
JConfig.log().closeLog();
}
}
}