Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. The ASF licenses this file to You * 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. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. */ package org.apache.roller.weblogger.business.referrers; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.Weblogger; import org.apache.roller.weblogger.business.runnable.ContinuousWorkerThread; import org.apache.roller.weblogger.business.runnable.WorkerThread; import org.apache.roller.weblogger.config.WebloggerConfig; /** * The base implementation of the ReferrerQueueManager. * * This class is implemented using the singleton pattern to ensure that only * one instance exists at any given time. * * This implementation can be configured to handle referrers in 2 ways ... * 1. synchronously. referrers are processed immediately. * 2. asynchronously. referrers are queued for later processing. * * Users can control the referrer queue mode via properties in the static * roller.properties configuration file. * * In asynchronous processing mode we start some number of worker threads which * run continously to process any referrers that have been queued. Each worker * processes queued referrers until the queue is empty, then sleeps for a given * amount of time. The number of workers used and their sleep time can be set * via properties of the static roller.properties file. * * @author Allen Gilliland */ @com.google.inject.Singleton public class ReferrerQueueManagerImpl implements ReferrerQueueManager { private static Log mLogger = LogFactory.getLog(ReferrerQueueManagerImpl.class); private final Weblogger roller; private boolean asyncMode = false; private int numWorkers = 1; private int sleepTime = 10000; private List workers = null; private List referrerQueue = null; // private because we are a singleton @com.google.inject.Inject protected ReferrerQueueManagerImpl(Weblogger roller) { mLogger.info("Instantiating Referrer Queue Manager"); this.roller = roller; // lookup config options this.asyncMode = WebloggerConfig.getBooleanProperty("referrers.asyncProcessing.enabled"); mLogger.info("Asynchronous referrer processing = " + this.asyncMode); if (this.asyncMode) { String num = WebloggerConfig.getProperty("referrers.queue.numWorkers"); String sleep = WebloggerConfig.getProperty("referrers.queue.sleepTime"); try { this.numWorkers = Integer.parseInt(num); if (numWorkers < 1) this.numWorkers = 1; } catch (NumberFormatException nfe) { mLogger.warn("Invalid num workers [" + num + "], using default"); } try { // multiply by 1000 because we expect input in seconds this.sleepTime = Integer.parseInt(sleep) * 1000; } catch (NumberFormatException nfe) { mLogger.warn("Invalid sleep time [" + sleep + "], using default"); } // create the processing queue this.referrerQueue = Collections.synchronizedList(new ArrayList()); // start up workers this.workers = new ArrayList(); ContinuousWorkerThread worker = null; QueuedReferrerProcessingJob job = null; for (int i = 0; i < this.numWorkers; i++) { job = new QueuedReferrerProcessingJob(); worker = new ContinuousWorkerThread("ReferrerWorker" + i, job, this.sleepTime); workers.add(worker); worker.start(); } } } /** * Process an incoming referrer. * * If we are doing asynchronous referrer processing then the referrer will * just go into the queue for later processing. If not then we process it * now. */ public void processReferrer(IncomingReferrer referrer) { if (this.asyncMode) { mLogger.debug("QUEUING: " + referrer.getRequestUrl()); // add to queue this.enqueue(referrer); } else { // process now ReferrerProcessingJob job = new ReferrerProcessingJob(); // setup input HashMap inputs = new HashMap(); inputs.put("referrer", referrer); job.input(inputs); // execute job.execute(); try { // flush changes roller.flush(); } catch (WebloggerException ex) { mLogger.error("ERROR commiting referrer", ex); } } } /** * Place a referrer in the queue. */ public void enqueue(IncomingReferrer referrer) { this.referrerQueue.add(referrer); if (this.referrerQueue.size() > 250) { mLogger.warn("Referrer queue is rather full. queued=" + this.referrerQueue.size()); } } /** * Retrieve the next referrer in the queue. */ public synchronized IncomingReferrer dequeue() { if (!this.referrerQueue.isEmpty()) { return (IncomingReferrer) this.referrerQueue.remove(0); } return null; } /** * clean up. */ public void shutdown() { if (this.workers != null && this.workers.size() > 0) { mLogger.info("stopping all ReferrerQueue worker threads"); // kill all of our threads WorkerThread worker = null; Iterator it = this.workers.iterator(); while (it.hasNext()) { worker = (WorkerThread) it.next(); worker.interrupt(); } } } }