/*
* Copyright 2000-2004 The Apache Software Foundation.
*
* 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 org.apache.jetspeed.aggregator.impl;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jetspeed.util.Queue;
import org.apache.jetspeed.util.FIFOQueue;
/**
* The WorkerMonitor is responsible for dispatching jobs to workers
* It uses an Apache HTTPd configuration style of min/max/spare workers
* threads to throttle the rendering work.
* If jobs come in faster that processing, they are stored in a queue
* which is flushed periodically by a QueueMonitor.
*
* @author <a href="mailto:raphael@apache.org">Rapha\u00ebl Luta</a>
* @version $Id: WorkerMonitor.java,v 1.5 2005/03/19 12:47:14 sgala Exp $
*/
public class WorkerMonitor
{
/** Commons logging */
protected final static Log log = LogFactory.getLog(WorkerMonitor.class);
/** Static counters for identifying workers */
private static long sCount = 0;
/** Minimum number of wokers to create */
private int minWorkers = 5;
/** Maximum number of workers */
private int maxWorkers = 50;
/** Minimum amount of spare workers */
private int spareWorkers = 3;
/** Maximum of job processed by a worker before being released */
private int maxJobsPerWorker = 10;
/** Stack containing currently idle workers */
private Stack workers = new Stack();
/** The thread group used to group all worker threads */
private ThreadGroup tg = new ThreadGroup("Workers");
/** Job queue */
private Queue queue;
public void init()
{
addWorkers(this.minWorkers);
setQueue(new FIFOQueue());
}
public void setQueue(Queue queue)
{
this.queue = queue;
}
/**
* Create the request number of workers and add them to
* list of available workers.
*
* @param wCount the number of workers to create
*/
protected synchronized void addWorkers(int wCount)
{
int wCurrent = this.tg.activeCount();
if (wCurrent < maxWorkers)
{
if (wCurrent + wCount > maxWorkers)
{
wCount = maxWorkers - wCurrent;
}
log.info("Creating "+ wCount +" workers -> "+ (wCurrent + wCount));
for (int i = 0; i < wCount; ++i)
{
Worker worker = new Worker(this, this.tg, "WORKER_" + (++sCount));
worker.start();
workers.push(worker);
}
}
}
/**
* Retrieves an idle worker
*
* @return a Worker from the idle pool or null if non available
*/
public Worker getWorker()
{
synchronized(this.workers)
{
if (this.workers.size() < spareWorkers)
{
addWorkers(spareWorkers);
}
if (this.workers.size() == 0)
{
return null;
}
return (Worker)workers.pop();
}
}
/**
* Assign a job to a worker and execute it or queue the job if no
* worker is available.
*
* @param job the Job to process
*/
public void process(RenderingJob job)
{
Worker worker = this.getWorker();
AccessControlContext context = AccessController.getContext();
if (worker==null)
{
queue.push(job);
queue.push(context);
}
else
{
try
{
synchronized (worker)
{
worker.setJob(job, context);
worker.notify();
}
}
catch (Throwable t)
{
log.error("Worker exception", t);
}
}
}
/**
* Put back the worker in the idle queue unless there are pending jobs and
* worker can still be committed to a new job before being released.
*/
public void release(Worker worker)
{
// if worker can still proces some jobs assign the first
// backlog job to this worker, else reset job count and put
// it on the idle queue.
synchronized (worker)
{
if ((worker.getJobCount()<this.maxJobsPerWorker)&&(queue.size()>0))
{
RenderingJob job = (RenderingJob)queue.pop();
AccessControlContext context = (AccessControlContext)queue.pop();
worker.setJob(job, context);
return;
}
else
{
worker.setJob(null);
worker.resetJobCount();
}
}
synchronized (this.workers)
{
this.workers.push(worker);
}
}
}