001    /**
002     * ========================================
003     * JFreeReport : a free Java report library
004     * ========================================
005     *
006     * Project Info:  http://reporting.pentaho.org/
007     *
008     * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009     *
010     * This library is free software; you can redistribute it and/or modify it under the terms
011     * of the GNU Lesser General Public License as published by the Free Software Foundation;
012     * either version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016     * See the GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public License along with this
019     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020     * Boston, MA 02111-1307, USA.
021     *
022     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023     * in the United States and other countries.]
024     *
025     * ------------
026     * $Id: WorkerPool.java 3525 2007-10-16 11:43:48Z tmorgner $
027     * ------------
028     * (C) Copyright 2000-2005, by Object Refinery Limited.
029     * (C) Copyright 2005-2007, by Pentaho Corporation.
030     */
031    
032    package org.jfree.report.util;
033    
034    /**
035     * A simple static workpool. Worker threads are created when necessary.
036     *
037     * @author Thomas Morgner
038     */
039    public class WorkerPool
040    {
041      /**
042       * The worker array.
043       */
044      private Worker[] workers;
045      /**
046       * A flag indicating whether idle workers are available.
047       */
048      private boolean workersAvailable;
049      /**
050       * the name prefix for all workers of this pool.
051       */
052      private String namePrefix;
053    
054      /**
055       * Creates a new worker pool with the default size of 10 workers and the default name.
056       */
057      public WorkerPool ()
058      {
059        this(10);
060      }
061    
062      /**
063       * Creates a new workerpool with the given number of workers and the default name.
064       *
065       * @param size the maximum number of workers available.
066       */
067      public WorkerPool (final int size)
068      {
069        this(size, "WorkerPool-worker");
070      }
071    
072      /**
073       * Creates a new worker pool for the given number of workers and with the given name
074       * prefix.
075       *
076       * @param size       the size of the worker pool.
077       * @param namePrefix the name prefix for all created workers.
078       */
079      public WorkerPool (final int size, final String namePrefix)
080      {
081        if (size <= 0)
082        {
083          throw new IllegalArgumentException("Size must be > 0");
084        }
085        workers = new Worker[size];
086        workersAvailable = true;
087        this.namePrefix = namePrefix;
088      }
089    
090      /**
091       * Checks, whether workers are available.
092       *
093       * @return true, if at least one worker is idle, false otherwise.
094       */
095      public synchronized boolean isWorkerAvailable ()
096      {
097        return workersAvailable;
098      }
099    
100      /**
101       * Updates the workersAvailable flag after a worker was assigned.
102       */
103      private void updateWorkersAvailable ()
104      {
105        for (int i = 0; i < workers.length; i++)
106        {
107          if (workers[i] == null)
108          {
109            workersAvailable = true;
110            return;
111          }
112          if (workers[i].isAvailable() == true)
113          {
114            workersAvailable = true;
115            return;
116          }
117        }
118        workersAvailable = false;
119      }
120    
121      /**
122       * Waits until a worker will be available.
123       */
124      private synchronized void waitForWorkerAvailable ()
125      {
126        while (isWorkerAvailable() == false)
127        {
128          try
129          {
130            // remove lock
131            this.wait(5000);
132          }
133          catch (InterruptedException ie)
134          {
135            // ignored
136          }
137        }
138      }
139    
140      /**
141       * Returns a workerhandle for the given workload. This method will wait until an idle
142       * worker is found.
143       *
144       * @param r the workload for the worker
145       * @return a handle to the worker.
146       */
147      public synchronized WorkerHandle getWorkerForWorkload (final Runnable r)
148      {
149        waitForWorkerAvailable();
150    
151        int emptySlot = -1;
152        for (int i = 0; i < workers.length; i++)
153        {
154          if (workers[i] == null)
155          {
156            // in the first run, try to avoid to create new threads...
157            // reuse the already available threads
158            if (emptySlot == -1)
159            {
160              emptySlot = i;
161            }
162            continue;
163          }
164          if (workers[i].isAvailable() == true)
165          {
166            workers[i].setWorkload(r);
167            updateWorkersAvailable();
168            return new WorkerHandle(workers[i]);
169          }
170        }
171        if (emptySlot != -1)
172        {
173          workers[emptySlot] = new Worker();
174          workers[emptySlot].setName(namePrefix + "-" + emptySlot);
175          workers[emptySlot].setWorkerPool(this);
176          workers[emptySlot].setWorkload(r);
177          updateWorkersAvailable();
178          return new WorkerHandle(workers[emptySlot]);
179        }
180        throw new IllegalStateException
181                ("At this point, a worker should already have been assigned.");
182      }
183    
184      /**
185       * Marks the given worker as finished. The worker will be removed from the list of the
186       * available workers.
187       *
188       * @param worker the worker which was finished.
189       */
190      public void workerFinished (final Worker worker)
191      {
192        if (worker.isFinish() == false)
193        {
194          throw new IllegalArgumentException("This worker is not in the finish state.");
195        }
196        for (int i = 0; i < workers.length; i++)
197        {
198          if (workers[i] == worker)
199          {
200            synchronized (this)
201            {
202              workers[i] = null;
203              workersAvailable = true;
204              this.notifyAll();
205            }
206            return;
207          }
208        }
209      }
210    
211      /**
212       * Marks the given worker as available.
213       *
214       * @param worker the worker which was available.
215       */
216      public synchronized void workerAvailable (final Worker worker)
217      {
218        for (int i = 0; i < workers.length; i++)
219        {
220          if (workers[i] == worker)
221          {
222            synchronized (this)
223            {
224              workersAvailable = true;
225              this.notifyAll();
226            }
227            return;
228          }
229        }
230      }
231    
232      /**
233       * Finishes all worker of this pool.
234       */
235      public void finishAll ()
236      {
237        for (int i = 0; i < workers.length; i++)
238        {
239          if (workers[i] != null)
240          {
241            workers[i].finish();
242          }
243        }
244      }
245    }