001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001-2003, ThoughtWorks, Inc.
004: * 200 E. Randolph, 25th Floor
005: * Chicago, IL 60601 USA
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * + Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * + Redistributions in binary form must reproduce the above
016: * copyright notice, this list of conditions and the following
017: * disclaimer in the documentation and/or other materials provided
018: * with the distribution.
019: *
020: * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021: * names of its contributors may be used to endorse or promote
022: * products derived from this software without specific prior
023: * written permission.
024: *
025: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036: ********************************************************************************/package net.sourceforge.cruisecontrol;
037:
038: import java.util.ArrayList;
039: import java.util.EventListener;
040: import java.util.Iterator;
041: import java.util.LinkedList;
042: import java.util.List;
043:
044: import net.sourceforge.cruisecontrol.util.threadpool.TaskAlreadyAddedException;
045: import net.sourceforge.cruisecontrol.util.threadpool.ThreadQueue;
046:
047: import org.apache.log4j.Logger;
048:
049: /**
050: * Provides an independent thread of execution that knows how to
051: * build Projects. Passes {@link ProjectInterface} objects to a thread
052: * queue. The number of worker threads is defined in config.xml
053: *
054: * @author Peter Mei <pmei@users.sourceforge.net>
055: * @author jfredrick
056: * @author Jared Richardson <jared.richardson@sas.com>
057: */
058: public class BuildQueue implements Runnable {
059: private static final Logger LOG = Logger
060: .getLogger(BuildQueue.class);
061:
062: private final LinkedList queue = new LinkedList();
063:
064: private boolean waiting = false;
065:
066: private Thread buildQueueThread;
067:
068: private List listeners = new ArrayList();
069:
070: /**
071: * @param project
072: */
073: public void requestBuild(ProjectInterface project) {
074: LOG.debug("BuildQueue.requestBuild Thread = "
075: + Thread.currentThread().getName());
076:
077: notifyListeners();
078: synchronized (queue) {
079: queue.add(project);
080: queue.notifyAll();
081: }
082: }
083:
084: /**
085: * @param project The project to find in the queues
086: * @return String representing this project's position in the various queues, e.g. IDLE[ 5 / 24 ]
087: */
088: public String findPosition(ProjectInterface project) {
089: int position;
090: int length;
091: synchronized (queue) {
092: position = queue.indexOf(project);
093: length = queue.size();
094: }
095: if (position < 0) {
096: return ThreadQueue.findPosition(project.getName());
097: }
098: // position is 0-based, make it 1-based for human reporting
099: return "BUILD_REQUESTED[ " + (position + 1) + " / " + length
100: + " ]";
101: }
102:
103: private void serviceQueue() {
104: synchronized (queue) {
105: while (!queue.isEmpty()) {
106: ProjectInterface nextProject = (ProjectInterface) queue
107: .remove(0);
108: if (nextProject != null) {
109: LOG.info("now adding to the thread queue: "
110: + nextProject.getName());
111: ProjectWrapper pw = new ProjectWrapper(nextProject);
112: try {
113: ThreadQueue.addTask(pw);
114: } catch (TaskAlreadyAddedException e) {
115: // it's already there... don't re-add it.
116: // later, we'll need to add it to a queued up list
117: // so we don't 'forget' about the new build request
118: }
119: }
120: }
121: }
122: }
123:
124: public void run() {
125: try {
126: LOG.info("BuildQueue started");
127: while (true) {
128: synchronized (queue) {
129: while (queue.isEmpty()) {
130: waiting = true;
131: queue.wait();
132: }
133: waiting = false;
134: }
135: serviceQueue();
136: }
137: } catch (InterruptedException e) {
138: LOG.debug("BuildQueue.run() interrupted. Stopping?", e);
139: } catch (Throwable e) {
140: LOG.error("BuildQueue.run()", e);
141: } finally {
142: waiting = false;
143: LOG.info("BuildQueue thread is no longer alive");
144: }
145: }
146:
147: void start() {
148: buildQueueThread = new Thread(this , "BuildQueueThread");
149: buildQueueThread.setDaemon(false);
150: buildQueueThread.start();
151: while (!buildQueueThread.isAlive()) {
152: try {
153: Thread.sleep(500);
154: } catch (InterruptedException e) {
155: String message = "BuildQueue.start() interrupted";
156: LOG.error(message, e);
157: throw new RuntimeException(message);
158: }
159: }
160: }
161:
162: void stop() {
163: LOG.info("Stopping BuildQueue");
164: if (buildQueueThread != null) {
165: buildQueueThread.interrupt();
166: }
167: synchronized (queue) {
168: queue.notifyAll();
169: }
170: }
171:
172: public boolean isAlive() {
173: return true;
174: }
175:
176: public boolean isWaiting() {
177: return waiting;
178: }
179:
180: public void addListener(Listener listener) {
181: synchronized (listeners) {
182: listeners.add(listener);
183: }
184: }
185:
186: private void notifyListeners() {
187: synchronized (listeners) {
188: Iterator toNotify = listeners.iterator();
189: while (toNotify.hasNext()) {
190: try {
191: ((Listener) toNotify.next()).buildRequested();
192: } catch (Exception e) {
193: LOG
194: .error(
195: "exception notifying listener before project queued",
196: e);
197: }
198: }
199: }
200: }
201:
202: public static interface Listener extends EventListener {
203: void buildRequested();
204: }
205: }
|