Java tutorial
/* * Copyright (c) 2002-2010 Gargoyle Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gargoylesoftware.htmlunit.javascript.background; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebWindow; /** * An event loop to execute all the JavaScript jobs. * * @version $Revision: 5920 $ * @author Amit Manjhi * */ public class JavaScriptExecutor implements Runnable, Serializable { private static final long serialVersionUID = 8525230714555970165L; /** * A simple class to store a JavaScriptJobManager and its earliest job. */ protected final class JobExecutor { private final JavaScriptJobManager jobManager_; private final JavaScriptJob earliestJob_; private JobExecutor(final JavaScriptJobManager jobManager, final JavaScriptJob earliestJob) { jobManager_ = jobManager; earliestJob_ = earliestJob; } /** * Returns the earliest job. * @return the earliest job. */ protected JavaScriptJob getEarliestJob() { return earliestJob_; } /** * Returns the JavaScriptJobManager. * @return the JavaScriptJobManager. */ protected JavaScriptJobManager getJobManager() { return jobManager_; } } // TODO: is there utility in not having these as transient? private transient WeakReference<WebClient> webClient_; private transient List<WeakReference<JavaScriptJobManager>> jobManagerList_; private volatile boolean shutdown_ = false; private transient Thread eventLoopThread_ = null; /** Logging support. */ private static final Log LOG = LogFactory.getLog(JavaScriptExecutor.class); /** Creates an EventLoop for the webClient. * * @param webClient the provided webClient */ public JavaScriptExecutor(final WebClient webClient) { jobManagerList_ = new ArrayList<WeakReference<JavaScriptJobManager>>(); webClient_ = new WeakReference<WebClient>(webClient); } /** * Starts the eventLoopThread_. */ protected void startThreadIfNeeded() { if (eventLoopThread_ == null) { eventLoopThread_ = new Thread(this, "JS executor for " + webClient_.get()); eventLoopThread_.setDaemon(true); eventLoopThread_.start(); } } private void killThread() { if (eventLoopThread_ == null) { return; } try { eventLoopThread_.interrupt(); eventLoopThread_.join(10000); } catch (final InterruptedException e) { LOG.warn("InterruptedException while waiting for the eventLoop thread to join " + e); // ignore, this doesn't matter, we want to stop it } if (eventLoopThread_.isAlive()) { LOG.warn("Event loop thread " + eventLoopThread_.getName() + " still alive at " + System.currentTimeMillis()); } } /** * Returns the JobExecutor corresponding to the earliest job. * @return the JobExectuor with the earliest job. */ protected synchronized JobExecutor getEarliestJob() { JobExecutor jobExecutor = null; // iterate over the list and find the earliest job to run. for (WeakReference<JavaScriptJobManager> jobManagerRef : jobManagerList_) { final JavaScriptJobManager jobManager = jobManagerRef.get(); if (jobManager != null) { final JavaScriptJob newJob = jobManager.getEarliestJob(); if (newJob != null) { if (jobExecutor == null || jobExecutor.earliestJob_.getTargetExecutionTime() > newJob .getTargetExecutionTime()) { jobExecutor = new JobExecutor(jobManager, newJob); } } } } return jobExecutor; } /** * Executes the jobs in the eventLoop till timeoutMillis expires or the eventLoop becomes empty. * No use in non-GAE mode. * @param timeoutMillis the timeout in milliseconds * @return the number of jobs executed */ public int pumpEventLoop(final long timeoutMillis) { return 0; } /** Runs the eventLoop. */ public void run() { while (!shutdown_ && webClient_.get() != null) { if (LOG.isTraceEnabled()) { LOG.trace("started finding earliestJob at " + System.currentTimeMillis()); } final JobExecutor jobExecutor = getEarliestJob(); if (LOG.isTraceEnabled()) { LOG.trace("stopped finding earliestJob at " + System.currentTimeMillis()); } final long sleepInterval = 10; if (jobExecutor == null || jobExecutor.earliestJob_.getTargetExecutionTime() - System.currentTimeMillis() > sleepInterval) { try { Thread.sleep(sleepInterval); } catch (final InterruptedException e) { // nothing, probably a shutdown notification } } if (shutdown_ || webClient_.get() == null) { break; } if (jobExecutor != null) { // execute the earliest job. if (LOG.isTraceEnabled()) { LOG.trace("started executing job at " + System.currentTimeMillis()); } jobExecutor.jobManager_.runSingleJob(jobExecutor.earliestJob_); if (LOG.isTraceEnabled()) { LOG.trace("stopped executing job at " + System.currentTimeMillis()); } } } } /** * Register a window with the eventLoop. * @param newWindow the new web window */ public synchronized void addWindow(final WebWindow newWindow) { final JavaScriptJobManager jobManager = newWindow.getJobManager(); if (jobManager != null && !contains(jobManager)) { jobManagerList_.add(new WeakReference<JavaScriptJobManager>(jobManager)); startThreadIfNeeded(); } } private boolean contains(final JavaScriptJobManager newJobManager) { for (WeakReference<JavaScriptJobManager> jobManagerRef : jobManagerList_) { if (jobManagerRef.get() == newJobManager) { return true; } } return false; } /** Notes that this thread has been shutdown. */ public void shutdown() { shutdown_ = true; killThread(); } }