Package com.jbidwatcher.auction.server.ebay

Source Code of com.jbidwatcher.auction.server.ebay.ebayServer$SnipeListener

package com.jbidwatcher.auction.server.ebay;

/*
* Copyright (c) 2000-2007, CyberFOX Software, Inc. All Rights Reserved.
*
* Developed by mrs (Morgan Schweers)
*/

//  This is the concrete implementation of AuctionServer to handle
//  parsing eBay auction pages.  There should be *ZERO* eBay specific
//  logic outside this class.  A pipe-dream, perhaps, but it seems
//  mostly doable.

import com.jbidwatcher.util.config.*;
import com.jbidwatcher.util.Externalized;
import com.jbidwatcher.auction.server.ServerMenu;
import com.jbidwatcher.util.queue.*;
import com.jbidwatcher.util.queue.TimerHandler;
import com.jbidwatcher.util.html.JHTML;
import com.jbidwatcher.util.http.CookieJar;
import com.jbidwatcher.util.*;
import com.jbidwatcher.util.Currency;
import com.jbidwatcher.search.Searcher;
import com.jbidwatcher.search.SearchManager;
import com.jbidwatcher.search.SearchManagerInterface;
import com.jbidwatcher.auction.*;
import com.jbidwatcher.auction.server.AuctionServer;

import java.net.URL;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.FileNotFoundException;

/** @noinspection OverriddenMethodCallInConstructor*/
public final class ebayServer extends AuctionServer implements MessageQueue.Listener,JConfig.ConfigListener {
  private final static ebayCurrencyTables sCurrencies = new ebayCurrencyTables();
  private TT T;

  /** @noinspection FieldAccessedSynchronizedAndUnsynchronized*/
  private eBayTimeQueueManager _etqm;
  private Searcher mSellerSearch;
  private ebaySearches mSearcher;
  private ebayLoginManager mLogin;
  private SnipeListener mSnipeQueue;

  /** @noinspection FieldCanBeLocal*/
  private TimerHandler eQueue;

  /**< The full amount of time it takes to request a single page from this site. */
  private long mPageRequestTime;

  /**< The amount of time to adjust the system clock by, to make it be nearly second-accurate to eBay time. */
  private long mOfficialServerTimeDelta;

  /**< The time zone the auction server is in (for eBay this will be PST or PDT). */
  private TimeZone mOfficialServerTimeZone;
  private Date mNow = new Date();
  private GregorianCalendar mCal;
  private ebayCleaner mCleaner;
  private Bidder mBidder;

  public Currency getMinimumBidIncrement(Currency currentBid, int bidCount) {
    return sCurrencies.getMinimumBidIncrement(currentBid, bidCount);
  }

  public void updateConfiguration() {
    boolean isUpdated = mLogin.updateLogin(getName());

    if(!mLogin.isDefault()) {
      if (isUpdated) forceLogin();

      Searcher s = SearchManager.getInstance().getSearchByName("My Selling Items");
      if(s == null) {
        mSellerSearch = SearchManager.getInstance().buildSearch(System.currentTimeMillis(), "Seller", "My Selling Items", mLogin.getUserId(), getName(), null, 1);
        mSellerSearch.setCategory("selling");
        SearchManager.getInstance().addSearch(mSellerSearch);
      } else {
        s.setSearch(mLogin.getUserId());
      }
    }
  }

  private class eBayTimeQueueManager extends TimeQueueManager {
    /**
     * Don't start checking until the server time delta becomes non-zero,
     * i.e. we've done a time-check.  Unless the user has disabled time
     * check, in which case, just...go with it. :)
     *
     * @return Should be false.  Always false.
     */
    public boolean check() {
      return (JConfig.queryConfiguration("timesync.enabled", "true").equals("false") || getServerTimeDelta() != 0) && super.check();
    }

    /**
     * Adjust the current time by the difference in time between localtime and servertime, including page request time.
     *
     * @return As close an approximation of 'time at the eBay server' as can be done.
     */
    public long getCurrentTime() {
      return super.getCurrentTime() + getServerTimeDelta();
    }
  }

  /**
   * @brief Build a menu that can be added to the JBidwatcher standard
   * menu, to do eBay-specific things.
   *
   */
  public ServerMenu establishMenu() {
    ServerMenu esm = new ebayServerMenu(this.getFriendlyName(), Constants.EBAY_DISPLAY_NAME, 'b');
    esm.initialize();

    return esm;
  }

  public void updateHighBid(String auctionId) {
    String bidHistory = Externalized.getString("ebayServer.protocol") + T.s("ebayServer.bidHost") + Externalized.getString("ebayServer.V3file") + Externalized.getString("ebayServer.viewBidsCGI") + auctionId;
    CookieJar cj = mLogin.getNecessaryCookie(false);
    String userCookie = null;
    if (cj != null) userCookie = cj.toString();
    JHTML htmlDocument = new JHTML(bidHistory, userCookie, mCleaner);

    if(htmlDocument.isLoaded()) {
      List<JHTML.Table> bidderTables = htmlDocument.extractTables();
      for (JHTML.Table t : bidderTables) {
        if (t.rowCellMatches(0, "^(Bidder|User ID).*")) {
          int bidCount = t.getRowCount() - 1; // 1 for the header

          AuctionEntry ae = (AuctionEntry) EntryCorral.getInstance().takeForWrite(auctionId);

          try {
            // -1 for the starting price
            if(t.rowCellMatches(bidCount, "Starting Price")) bidCount -= 1;
            if(t.rowCellMatches(bidCount, "(No purchases have been made.|No bids have been placed.)")) {
              ae.setNumBids(0);
              return;
            }

            if(ae.getNumBidders() == 0) ae.setNumBids(bidCount);
            int myMostRecentRow = -1;
            for(int i=1; i < bidCount+1; i++) {
              if(t.getCell(0, i).equals(mLogin.getUserId())) {
                myMostRecentRow = i;
                break;
              }
            }
            if(myMostRecentRow != -1) {
              String newCurrency = t.getCell(1, myMostRecentRow);
              if (newCurrency != null) {
                Currency highBid = Currency.getCurrency(newCurrency);
                try {
                  if (!ae.isBidOn() || ae.getBid().less(highBid)) {
                    ae.setBid(highBid);
                    ae.setBidQuantity(bidCount);
                    ae.saveDB();
                  }
                } catch (Currency.CurrencyTypeException cte) {
                  //  Bad things happen here.  Ignore it for now.
                }
              }
            }
            if(bidCount > 0) {
              AuctionInfo ai = ae.getAuction();
              String highBidder = t.getCell(0, 1);
              int feedbackStart = highBidder.indexOf(" (");
              if(feedbackStart != -1) {
                highBidder = highBidder.substring(0, feedbackStart);
              }
              if(highBidder.startsWith("private listing")) {
                ai.setPrivate(true);
                ai.setHighBidder("(private)");
              } else {
                Pattern p = Pattern.compile("Member Id: (.*)");
                Matcher m = p.matcher(highBidder);
                if(m.matches()) highBidder = m.group(1);
                ai.setHighBidder(highBidder);
              }
              ai.saveDB();
              return;
            }
          } finally {
            EntryCorral.getInstance().release(auctionId);
          }
        }
      }
    }
  }

  /**
   * @brief Process an action, based on messages passed through our internal queues.
   */
  public void messageAction(Object deQ) {
    AuctionQObject ac = (AuctionQObject)deQ;
    String failString = null;

    switch(ac.getCommand()) {
      case AuctionQObject.LOAD_URL:
        mSearcher.loadAllFromURLString(SearchManager.getSearchById((Long) ac.getData()), ac.getLabel());
        return;
      case AuctionQObject.LOAD_SEARCH:
        mSearcher.loadSearchString(SearchManager.getSearchById((Long) ac.getData()), ac.getLabel(), false);
        return;
      case AuctionQObject.LOAD_TITLE:
        mSearcher.loadSearchString(SearchManager.getSearchById((Long)ac.getData()), ac.getLabel(), true);
        return;
      case AuctionQObject.LOAD_SELLER:
        doGetSelling(SearchManager.getSearchById((Long) ac.getData()), ac.getLabel());
        return;
      case AuctionQObject.LOAD_MYITEMS:
        if(mLogin.isDefault()) {
          failString = Externalized.getString("ebayServer.cantLoadWithoutUsername1") + " " + getName() + Externalized.getString("ebayServer.cantLoadWithoutUsername2");
        } else {
          doMyEbaySynchronize(ac.getLabel());
          return;
        }
        break;
      case AuctionQObject.BID:
        bidMsg(ac);
        return;
      default:
        //  It's okay if we don't recognize it.
    }

    if(ac.getData() != null) {
      if(ac.getData().equals("Get My eBay Items")) {
        if(mLogin.isDefault()) {
          failString = Externalized.getString("ebayServer.cantLoadWithoutUsername1") + " " + getName() + Externalized.getString("ebayServer.cantLoadWithoutUsername2");
        } else {
          SearchManager.getInstance().getSearchByName("My eBay").execute();
          return;
        }
      }

      /**
       * Get items this user is selling.
       */
      if(ac.getData().equals("Get Selling Items")) {
        if(mLogin.isDefault()) {
          failString = Externalized.getString("ebayServer.cantLoadSellerWithoutUser1") + " " + getName() + Externalized.getString("ebayServer.cantLoadWithoutUsername2");
        } else {
          if(mSellerSearch == null) updateConfiguration();
          if(mSellerSearch != null) mSellerSearch.execute();
          return;
        }
      }

      /**
       * Update the login cookie, that contains session and adult information, for example.
       */
      if(ac.getData().equals(UPDATE_LOGIN_COOKIE)) {
        if(mLogin.isDefault()) {
          failString = Externalized.getString("ebayServer.cantUpdateCookieWithoutUser1") + " " + getName() + Externalized.getString("ebayServer.cantLoadWithoutUsername2");
        } else {
          forceLogin();
          return;
        }
      }

      if(ac.getData().equals("Dump eBay Activity Queue")) {
        _etqm.dumpQueue(T.getBundle());
        return;
      }
    }

    /**
     * If we've made a failure string, and we're using the default
     * user, then display the error, otherwise indicate that we got an
     * unexpected command.
     */
    if(failString != null && failString.length() != 0 && mLogin.isDefault()) {
      MQFactory.getConcrete("Swing").enqueue("NOACCOUNT " + failString);
    } else {
      if (ac.getData() instanceof String) {
        String acData = (String) ac.getData();
        JConfig.log().logMessage("Dequeue'd unexpected command or fell through: " + ac.getCommand() + ':' + acData);
      } else {
        //noinspection ObjectToString
        JConfig.log().logMessage("Can't recognize ebay-queued data: " + ac.getData());
      }
    }
  }

  public void forceLogin() {
    mLogin.resetCookie();
    mLogin.getNecessaryCookie(true);
  }

  private void bidMsg(AuctionQObject ac) {
    AuctionAction ab = (AuctionAction)ac.getData();
    String bidResultString = ab.activate();
    String configBidMsg;

    if(ab.isSuccessful()) {
      configBidMsg = "prompt.hide_bidalert";
    } else {
      configBidMsg = "prompt.hide_bidfailalert";
    }

    MQFactory.getConcrete("Swing").enqueue("IGNORE " + configBidMsg + ' ' + bidResultString);
  }

  private static final int THIRTY_SECONDS = 30 * Constants.ONE_SECOND;
  private static final long TWO_MINUTES = Constants.ONE_MINUTE * 2;
  private static final long FIVE_MINUTES = Constants.ONE_MINUTE * 5;

  public void setSnipe(String auctionId) {
    AuctionEntry ae = (AuctionEntry) EntryCorral.getInstance().takeForWrite(auctionId);
    try {
      Date endDate = ae.getEndDate();
      long snipeDelta = ae.getSnipeTime();
      //  If we already have a snipe set for it, first cancel the old one, and then set up the new.
      _etqm.erase(auctionId);
      //  Delete the identifier from the snipe queue, as it _may_ have already loaded the pre-snipe information,
      //  in which case the first snipe (the - TWO_MINUTES) one will actually _fire_ the snipe.  This would be bad.
      mSnipeQueue.delSnipe(auctionId);

      JConfig.log().logDebug("Establishing a snipe on " + auctionId + " for " + ae.getSnipeAmount());
      if (endDate != null && endDate != Constants.FAR_FUTURE) {
        ae.setLastStatus("Establishing a snipe for " + ae.getSnipeAmount());

        _etqm.add("TIMECHECK", "auction_manager", (endDate.getTime() - snipeDelta) - FIVE_MINUTES);
        _etqm.add(auctionId, mSnipeQueue.getQueueName(), (endDate.getTime() - snipeDelta) - TWO_MINUTES);
        _etqm.add(auctionId, mSnipeQueue.getQueueName(), (endDate.getTime() - snipeDelta));
        _etqm.add(auctionId, "drop",       endDate.getTime() + THIRTY_SECONDS);
      } else {
        JConfig.log().logMessage("Failing to set snipe for " + auctionId + ", endDate is null or in the far future (" + endDate + ")");
      }

      MQFactory.getConcrete("my").enqueue("SNIPE " + auctionId);
    } finally {
      EntryCorral.getInstance().release(auctionId);
    }
  }

  /**
   * Erase the pending snipe
   * @param identifier - The auction identifier of the listing whose snipe to cancel.
   */
  public void cancelSnipe(String identifier) {
    _etqm.erase(identifier);
    mSnipeQueue.delSnipe(identifier);
    MQFactory.getConcrete("my").enqueue("CANCEL " + identifier);
  }

  public ebayServer(String site, String username, String password) {
    if(site == null) site = JConfig.queryConfiguration(getName() + ".browse.site");
    if(site == null) site = "0";
    constructServer(site, username, password);
  }

  /**
   * @brief Constructor for the eBay server object.
   */
  public ebayServer() {
    String username = JConfig.queryConfiguration(getName() + ".user", "default");
    String siteNumber = JConfig.queryConfiguration(getName() + ".browse.site");
    String password = JConfig.queryConfiguration(getName() + ".password", "default");

    constructServer(siteNumber, username, password);
  }

  /**
   * @brief Constructor for the eBay server object.
   * @param country - The country site to create an ebay server for.
   */
  public ebayServer(String country) {
    String username = JConfig.queryConfiguration(getName() + ".user", "default");
    String password = JConfig.queryConfiguration(getName() + ".password", "default");

    constructServer(country, username, password);
  }

  private void constructServer(String site, String username, String password) {
    if(site == null) {
      T = new TT("ebay.com");
    } else if(StringTools.isNumberOnly(site)) {
      T = new TT("ebay.com");
//      String countrySite = Constants.SITE_CHOICES[Integer.parseInt(site)];
//      T = new TT(countrySite);
    } else {
      T = new TT(site);
    }
    mCleaner = new ebayCleaner();
    mLogin = new ebayLoginManager(T, Constants.EBAY_SERVER_NAME, password, username);
    mSearcher = new ebaySearches(mCleaner, mLogin);
    if(JConfig.queryConfiguration("ebay.mock_bidding", "false").equals("true")) {
      mBidder = new Bidder() {
        public int buy(AuctionEntry ae, int quantity) {
          return BID_BOUGHT_ITEM;
        }

        public int bid(AuctionEntry inEntry, Currency inBid, int inQuantity) {
          return BID_ERROR_OUTBID;
        }

        //  These two are called by sniping.
        public JHTML.Form getBidForm(CookieJar cj, AuctionEntry inEntry, Currency inCurr) throws BadBidException {
          return new JHTML.Form("<form action=\"http://example.com\">");
        }

        public int placeFinalBid(CookieJar cj, JHTML.Form bidForm, AuctionEntry inEntry, Currency inBid, int inQuantity) {
          return BID_ERROR_OUTBID;
        }
      };
    } else {
      mBidder = new ebayBidder(T, mLogin);
    }

    _etqm = new eBayTimeQueueManager();
    eQueue = new TimerHandler(_etqm);
    eQueue.setName("eBay SuperQueue");
    //noinspection CallToThreadStartDuringObjectConstruction
    eQueue.start();

    mSnipeQueue = new SnipeListener(getFriendlyName());
    MQFactory.getConcrete(mSnipeQueue.getQueueName()).registerListener(mSnipeQueue);
    MQFactory.getConcrete(getFriendlyName()).registerListener(this);

    JConfig.registerListener(this);
  }

  /**
   * @brief Given a standard URL, strip it apart, and find the items
   * identifier from the standard eBay 'ViewItem' URL.
   *
   * @param urlStyle - The string to parse the identifier out of.
   *
   * @return - The identifier for the auction referenced by the URL
   * string passed in, or null if no identifier could be found.
   */
  public String extractIdentifierFromURLString(String urlStyle) {
    Pattern url = Pattern.compile(Externalized.getString("ebayServer.itemNumberMatch"));
    Matcher urlMatch = url.matcher(urlStyle);
    if(urlMatch.find()) {
        String itemNum = urlMatch.group(2);
        if(StringTools.isNumberOnly(itemNum)) return itemNum;
    }
    URL siteAddr = StringTools.getURLFromString(urlStyle);

    if(siteAddr != null) {
      String lastPart = siteAddr.toString();
      if(lastPart.contains(Externalized.getString("ebayServer.viewCmd"))) {
        int index = lastPart.indexOf(Externalized.getString("ebayServer.viewCGI"));
        if(index != -1) {
          String aucId = lastPart.substring(index+ Externalized.getString("ebayServer.viewCGI").length());

          if (aucId.contains("&")) {
            aucId = aucId.substring(0, aucId.indexOf("&"));
          }

          if (aucId.contains("#")) {
            aucId = aucId.substring(0, aucId.indexOf("#"));
          }

          return(aucId);
        }
      }
    }

    try {
      URL pieces = new URL(urlStyle);
      String path = pieces.getPath();
      String digits = path.substring(path.lastIndexOf('/')+1);
      if(StringTools.isNumberOnly(digits)) return(digits);
    } catch (Exception e) {
      JConfig.log().logDebug("Failed to parse " + urlStyle + " as a URL");
    }

    JConfig.log().logDebug("extractIdentifierFromURLString failed.");
    return null;
  }

  /**
   * @brief Given a site-dependant item ID, get the string-form URL for that item.
   *
   * @param itemID - The item ID to get the URL for.
   *
   * @return - The real URL pointing to the item referenced by the passed in ID.
   */
  public String getStringURLFromItem(String itemID) {
    return Externalized.getString("ebayServer.protocol") + T.s("ebayServer.viewHost") + Externalized.getString("ebayServer.file") + '?' + Externalized.getString("ebayServer.viewCmd") + Externalized.getString("ebayServer.viewCGI") + itemID;
  }

  /**
   * @brief Get a string form URL that the user can browse to.
   *
   * This involves going to the users preferred country site.
   *
   * @param itemID - The item to browse w/r/t.
   *
   * @return - A string containing the way to browse to the users preferred international site.
   */
  public String getBrowsableURLFromItem(String itemID) {
    int browse_site = Integer.parseInt(JConfig.queryConfiguration(getName() + ".browse.site", "0"));

    return Externalized.getString("ebayServer.protocol") + Externalized.getString("ebayServer.browseHost") + Constants.SITE_CHOICES[browse_site] + Externalized.getString("ebayServer.file") + '?' + Externalized.getString("ebayServer.viewCmd") + Externalized.getString("ebayServer.viewCGI") + itemID;
  }

  /**
   * @brief Factory for generating an auction that contains the rules specific to eBay.
   *
   * @return - An object that can be used as an AuctionInfo object.
   */
  public SpecificAuction getNewSpecificAuction() {
    return new ebayAuction2(T);
  }

  public StringBuffer getAuction(String id) throws FileNotFoundException {
    long pre = System.currentTimeMillis();
    StringBuffer sb = getAuction(getURLFromItem(id));
    long post = System.currentTimeMillis();
    if (JConfig.queryConfiguration("timesync.enabled", "true").equals("true")) {
      mPageRequestTime = (post - pre);
    }

    return sb;
  }

  public long getPageRequestTime() {
    return mPageRequestTime;
  }

  public synchronized CookieJar getNecessaryCookie(boolean force) {
    return mLogin.getNecessaryCookie(force);
  }

  public int bid(String auctionId, Currency inBid, int inQuantity) {
    AuctionEntry inEntry = (AuctionEntry) EntryCorral.getInstance().takeForWrite(auctionId);
    try {
      if(inEntry == null) {
        JConfig.log().logMessage("Auction " + auctionId + " disappeared before the bid.");
        return AuctionServerInterface.BID_ERROR_AUCTION_GONE;
      } else {
        return mBidder.bid(inEntry, inBid, inQuantity);
      }
    } finally {
      EntryCorral.getInstance().release(auctionId);
    }
  }

  public int buy(String auctionId, int quantity) {
    AuctionEntry ae = (AuctionEntry) EntryCorral.getInstance().takeForWrite(auctionId);
    try {
      return mBidder.buy(ae, quantity);
    } finally {
      EntryCorral.getInstance().release(auctionId);
    }
  }

  public boolean isDefaultUser() {
    return mLogin.isDefault();
  }

  /**
   * @brief Get the user's ID for this auction server.
   *
   * @return - The user's ID, as they entered it.
   */
  public String getUserId() {
    return mLogin.getUserId();
  }

  /**
   *  @brief Clear the search queue.
   *
   *  This queue is basically only used for starting searches.
   */
  public void cancelSearches() {
    MQFactory.getConcrete(this.getFriendlyName()).clear();
  }

  /**
   * @brief Add search types to the search manager.
   *
   * Allows an auction server class to add unusual or site-specific
   * searches to the search manager.
   *
   * @param searchManager - The search manager to add these searches to.
   */
  public void addSearches(SearchManagerInterface searchManager) {
    Searcher s = searchManager.getSearchByName("My eBay");
    if(s == null) searchManager.addSearch("My Items", "My eBay", "", Constants.EBAY_SERVER_NAME, -1, 1);
  }

  private void doMyEbaySynchronize(String label) {
    MQFactory.getConcrete("Swing").enqueue("Synchronizing with My eBay...");
    mSearcher.getMyEbayItems(mLogin.getUserId(), label);
    MQFactory.getConcrete("Swing").enqueue("Done synchronizing with My eBay...");
  }

  /**
   * Load all items being sold by a given seller.
   *
   * @param searcher - The search to run
   * @param label - The category/label/tab to put it under
   */
  private void doGetSelling(Object searcher, String label) {
    String userId = ((Searcher)searcher).getSearch();
    MQFactory.getConcrete("Swing").enqueue("Getting Selling Items for " + userId);
    mSearcher.getSellingItems(userId, mLogin.getUserId(), label);
    MQFactory.getConcrete("Swing").enqueue("Done Getting Selling Items for " + userId);
  }

  private class SnipeListener implements MessageQueue.Listener {
    private String queueName = "sniper";
    private SnipeListener(String suffix) {
      queueName = suffix + " sniper";
    }

    public String getQueueName() { return queueName; }

    //  mSnipeMap maps identifiers to Snipe objects which contain sniping state.
    private Map<String, Snipe> mSnipeMap = new HashMap<String, Snipe>();

    public void delSnipe(String identifier) {
      mSnipeMap.remove(identifier);
    }

    /**
     * Retrieve a stored Snipe object if one exists (containing the cookie information),
     * or create a new one if one doesn't exist.
     *
     * @param identifier - The auction identifier to create the snipe for.
     *
     * @return - A Snipe object, either with a cookie object, or w/o. Returns
     * null if the AuctionEntry associated with the identifier does not exist
     * or is not sniped.
     */
    private Snipe getSnipe(String identifier) {
      AuctionEntry ae = EntryCorral.getInstance().takeForRead(identifier);
      if (ae == null || !ae.isSniped()) return null;

      Snipe snipe;
      if (mSnipeMap.containsKey(identifier)) {
        snipe = mSnipeMap.get(identifier);
      } else {
        snipe = new Snipe(mLogin, mBidder, ae);
        mSnipeMap.put(identifier, snipe);
      }
      return snipe;
    }

    private boolean resnipe(String identifier, Snipe snipe, boolean force) {
      /**
       *  The formula for 'when' the next resnipe is, is a little complex.
       * It's all in the code, though.  If we're 3 seconds or less away,
       * give up.  Otherwise wait another 20% of the remaining time
       * (minimum of 3 seconds), and retry.
       */
      long timeLeft = snipe.getItem().getEndDate().getTime() - _etqm.getCurrentTime();
      long snipeTime = snipe.getItem().getSnipeTime();
      if (timeLeft > snipeTime) {
        _etqm.add(identifier, getQueueName(), _etqm.getCurrentTime() + (timeLeft - snipeTime));
      } else if (timeLeft > Constants.THREE_SECONDS) {
        long retry_wait = (timeLeft / 10) * 2;
        if (retry_wait < Constants.THREE_SECONDS) retry_wait = Constants.THREE_SECONDS;

        _etqm.add(identifier, getQueueName(), _etqm.getCurrentTime() + retry_wait);
        return true;
      } else if (force) {
        //  Requeue it for _now_
        MQFactory.getConcrete(getQueueName()).enqueue(identifier);
      }
      return false;
    }

    public void messageAction(Object deQ) {
      final String identifier = (String)deQ;
      Snipe snipe = getSnipe(identifier);
      if(snipe == null) return;

      int snipeResult = snipe.fire();
      switch (snipeResult) {
        case Snipe.RESNIPE:
          if(resnipe(identifier, snipe, false)) break;

          //  If there are less than 3 seconds left, give up by falling through to FAIL and DONE.
          JConfig.log().logDebug("Resnipes failed, and less than 3 seconds away.  Giving up.");
        case Snipe.FAIL:
          _etqm.erase(identifier);
          JConfig.log().logDebug("Snipe appears to have failed; cancelling.");
          snipe.getItem().snipeFailed();
          //  A failed snipe is a serious, hard error, and should fall through to being removed from any other lists.
        case Snipe.DONE:
          mSnipeMap.remove(identifier);
          break;
        case Snipe.SUCCESSFUL:
          if(!_etqm.contains(new TimeQueueManager.Matcher() {
            public boolean match(Object payload, Object queue, long when) {
              return payload.equals(identifier) && queue == mSnipeQueue;
            }})) {
            resnipe(identifier, snipe, true);
          }
        default:
          break;
      }
    }
  }

  public String getTime() {
    TimeZone serverTZ = getOfficialServerTimeZone();
    if (serverTZ != null) {
      if(mCal == null) {
        mCal = new GregorianCalendar(serverTZ);
        if(JConfig.queryConfiguration("display.ebayTime", "false").equals("true")) {
          Constants.remoteClockFormat.setCalendar(mCal);
        }
      }

      if (JConfig.queryConfiguration("timesync.enabled", "true").equals("true")) {
        mNow.setTime(System.currentTimeMillis() +
                getServerTimeDelta() +
                getPageRequestTime());
      } else {
        mNow.setTime(System.currentTimeMillis());
      }
      mCal.setTime(mNow);
      //  Just in case it changes because of the setup.
      mNow.setTime(mCal.getTimeInMillis());
      return mLogin.getUserId() + '@' + getName() + ": " + Constants.remoteClockFormat.format(mNow);
    } else {
      mNow.setTime(System.currentTimeMillis());
      return mLogin.getUserId() + '@' + getName() + ": " + Constants.localClockFormat.format(mNow);
    }
  }

  public long getAdjustedTime() {
    return System.currentTimeMillis() + getServerTimeDelta() + getPageRequestTime();
  }

  /**
   * Get the delta between local time and eBay server time.  If the computer
   * is ahead of eBay time (fast), this number will be negative.  If the
   * computer is behind eBay time (slow), then this number will be positive.
   *
   * @return - An amount that, when added to the local clock time, results in eBay's time.
   */
  public long getServerTimeDelta() {
    return mOfficialServerTimeDelta;
  }

  public TimeZone getOfficialServerTimeZone() {
    return mOfficialServerTimeZone;
  }

  public String getName() {
    return Constants.EBAY_SERVER_NAME;
  }

  public String getFriendlyName() {
    return T.getCountrySiteName();
  }

  public boolean validate(String username, String password) {
    return mLogin.validate(username, password);
  }

  /**
   * @brief Go to eBay and get their official time page, parse it, and
   * mark the difference between that time and our current time
   * internally, so we know how far off this machine's time is.
   *
   * @return - An object containing eBay's date, or null if we fail to
   *         load or parse the 'official time' page properly.
   */
  protected Date getOfficialTime() {
    UpdateBlocker.startBlocking();
    String timeRequest = Externalized.getString("ebayServer.timeURL");

    JHTML htmlDocument = new JHTML(timeRequest, null, mCleaner);
    long localDateAfterPage = System.currentTimeMillis();

    ZoneDate result = null;

    String pageStep = htmlDocument.getNextContent();
    while (result == null && pageStep != null) {
      if (pageStep.equals(T.s("ebayServer.timePrequel1")) || pageStep.equals(T.s("ebayServer.timePrequel2"))) {
        result = StringTools.figureDate(htmlDocument.getNextContent(), Externalized.getString("ebayServer.officialTimeFormat"), false, false);
      }
      pageStep = htmlDocument.getNextContent();
    }

    UpdateBlocker.endBlocking();

    //  If we couldn't get a number, clear the page request time.
    if (result == null || result.getDate() == null) {
      mPageRequestTime = 0;
      //  This is bad...
      JConfig.log().logMessage(getName() + ": Error, can't accurately set delta to server's official time.");
      mOfficialServerTimeDelta = 1;
      return null;
    } else {
      //  reqTime will always be positive; it's how long it took to request, receive, and pre-parse the eBay page.
//      long reqTime = localDateAfterPage - localDateBeforePage;

      //  It looks like the best time to use is the time _after_ we've loaded the page, under the presumption that the delays
      //  in returning the time page are front-loaded.  Specifically, we don't want to get the difference from the time before
      //  we requested the page, as that'll include socket startup time, request time, eBay's processing time, etc...  Instead,
      //  we use the time after the request has returned, which should just involve data transfer.  This should make determining
      //  clock skew more accurate.  This has nothing to do with page request time, which we handle separately.
      //
      //  Subtract what time eBay thinks it is, from what time the local computer thinks it is to get the amount we add to the local
      //  clock to get the server clock.
      mOfficialServerTimeDelta = (result.getDate().getTime() - localDateAfterPage);

//      //  Subtract half the request time, under the belief that eBay instantiated its date/time halfway through the request.
//      mOfficialServerTimeDelta -= reqTime / 2;

      //  mOSTD of 0 is a sentinel that we haven't gotten the official time yet, so if we magically get it, make it 1ms instead.
      if(mOfficialServerTimeDelta == 0) mOfficialServerTimeDelta = 1;
      if (result.getZone() != null) mOfficialServerTimeZone = (result.getZone());
      if(Math.abs(mOfficialServerTimeDelta) > Constants.ONE_DAY * 7) {
        MQFactory.getConcrete("Swing").enqueue("NOTIFY Your system time is off from eBay's by more than a week.");
      }
    }

    return result.getDate();
  }
}
TOP

Related Classes of com.jbidwatcher.auction.server.ebay.ebayServer$SnipeListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
', 'auto'); ga('send', 'pageview');