3

I have a SwingWorker which initiates a LinkedBlockingQueue, passes it to another method (PortalDriver, below), then reads from it in the doInBackground() method. The LinkedBlockingQueue holds futures for a custom object (and it is definitely filled correctly). As a check, the objects that I am creating (via an ExecutorService), have a println(this) at the end of the constructor, so I know when they're being created.

The issue is, via the println() calls (both in doInBackground and in the constructor), after a number of successful iterations in the while loop in doInBackground, the line

System.out.println("future.isDone before get(): " + futureListing.isDone());

prints true, even though the constructor hasn't printed that it has been created (this is clear just by counting). The while loop blocks permanently, even though the rest of the objects are created.

private class ReadSchedWorker extends SwingWorker<Void, Listing> {
    private ChromeOptions options = new ChromeOptions();
    private WebDriver driver;
    private LinkedBlockingQueue<Future<Listing>> listingQueue;
    private LoginFactory loginFactory;
    private int year;
    private int month;
    private int day;

    public ReadSchedWorker(Login mediasiteLogin, Login tmsLogin,
                           int year, int month, int day) {
        System.setProperty("webdriver.chrome.driver", "\\\\private\\Home\\Desktop\\chromedriver.exe");
        listingQueue = new LinkedBlockingQueue<>();
        loginFactory = new LoginFactory(mediasiteLogin, tmsLogin);
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    protected Void doInBackground() throws Exception {
        this.options.addArguments("--disable-extensions");
        driver = new ChromeDriver(options);

        String portalUsername = loginFactory.getPortalUsername();
        String portalPassword = loginFactory.getPortalPassword();
        PortalDriver portalDriver = new PortalDriver(driver, listingQueue);
        portalDriver.getScheduleElements(portalUsername, portalPassword, year, month, day);

        while (!listingQueue.isEmpty()) {
            System.out.println("beginning");
            Future<Listing> futureListing = listingQueue.take();
            System.out.println("future.isDone before get(): " + futureListing.isDone());
            Listing listing = futureListing.get();
            System.out.println("future.isDone after get(): " + futureListing.isDone());
            System.out.println("before publish");
            publish(listing);
            System.out.println("after publish");
        }
        return null;
    }

    @Override
    protected void process(List<Listing> listings) {
        for (Listing listingItem : listings) {
            listingListModel.addElement(listingItem);
        }
    }
}

Just for completion, here is getScheduleElements, which fills up the LinkedBlockingQueue:

public void getScheduleElements(String username, String password, int year, int month, int day) {
    driver.get("https://rxsecure.umaryland.edu/apps/schedules/view/?type=search&searchtype=resource&id=100&start=" +
            year + "-" + month + "-" + day + "&scope=week");
    PortalLoginPage loginPage = PageFactory.initElements(driver, PortalLoginPage.class);
    PortalScheduleEventsWeekPage scheduleEventsWeekPage = loginPage.login(username, password);
    webElementsQueue = scheduleEventsWeekPage.initEventsQueue();

    // doesn't need to be a queue...
    // this is sequential....
    // could do a parallel stream
    while (webElementsQueue.peek() != null) {
        Callable<Listing> callable = new PortalListingCallable(webElementsQueue.poll());
        Future<Listing> future = executor.submit(callable);
        listingQueue.offer(future);
    }

    executor.shutdown();
}

Edit: An example of what I mean (the future not being done, but reporting that it is, via the println()s in doInBackground()):

beginning
future.isDone before get(): true //the future says it done, but the object being created is below
in listing contructor: PHAR580 Pharmacy Law;Specialty Pharmacy 2;Recorded in Mediasite PH S201 2015-04-27T10:00:00.000-04:00 2015-04-27T11:50:00.000-04:00
douglas
  • 186
  • 1
  • 3
  • 13

1 Answers1

4

prints true, even though the constructor hasn't printed that it has been created

Most likely you are getting an Exception or Error which you are not seeing. You have to call Future.get() to see it.

In the meantime I suggest you change

Future<Listing> future = executor.submit(callable);

to

Future<Listing> future = executor.submit(new Callable<Listing>() {
    public Listing call() throws Throwable {
        try {
            return callable.call();
        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        }
    }
});
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Ah, your code allowed me to print this (first line of exception): org.openqa.selenium.remote.UnreachableBrowserException: Error communicating with the remote browser. It may have died. Happy to give you the check, since this solved my issue technically. I don't use StackOverflow much, is that the edicate? And thank you. – douglas Jun 05 '15 at 19:20
  • You can accept the answer and if a much better one comes along, accept a different one later. – Peter Lawrey Jun 05 '15 at 19:25
  • Thanks. I think I'm able to figure out what's happening now. Do you suggest, in practice/production, using your code vs. my one-liner, i.e., `Future future = executor.submit(callable);`? – douglas Jun 05 '15 at 19:35
  • @douglas: AFAIK, you always should call `get()` on the future if only for this very reason -- to trap exceptions that might occur. – Hovercraft Full Of Eels Jun 05 '15 at 19:47
  • @HovercraftFullOfEels I'm confused; I was calling get: `Listing listing = futureListing.get();` Was it that I wasn't catching Exceptions? (I'm a novice. This is a sincere question). – douglas Jun 05 '15 at 19:51
  • @douglas: are you calling `get()` on the ReadSchedWorker instance? I don't see this, nor where you create the instance and call execute on it. I usually call `get()` from within a PropertyChangeListener that listens to the state property for `SwingWorker.StateValue.DONE`. – Hovercraft Full Of Eels Jun 06 '15 at 02:55
  • @HovercraftFullOfEels Ah, no. I'm not. I am using publish/process to update the GUI, and didn't call get because it was blocking the UI. I see now that I can override the done() method and call get there. Thank you. – douglas Jun 08 '15 at 12:42
  • @douglas: Please have a look at my code [here](http://stackoverflow.com/a/25963520/522444) for an example of using a PropertyChangeListener to be notified when the SwingWorker is done, so that `get()` can be called. I prefer this route rather than calling get within the `done()` method since this usually results in code that complies better with [Demeter's law](http://en.wikipedia.org/wiki/Law_of_Demeter). – Hovercraft Full Of Eels Jun 08 '15 at 17:01