Index: core/src/main/java/hudson/matrix/MatrixConfiguration.java
===================================================================
--- core/src/main/java/hudson/matrix/MatrixConfiguration.java	(revision 15166)
+++ core/src/main/java/hudson/matrix/MatrixConfiguration.java	(working copy)
@@ -36,7 +36,6 @@
 import hudson.model.Cause.LegacyCodeCause;
 import hudson.model.Node;
 import hudson.model.ParameterValue;
-import hudson.model.ParameterizedProjectTask;
 import hudson.model.ParametersAction;
 import hudson.model.Project;
 import hudson.model.SCMedItem;
@@ -293,8 +292,7 @@
     }
     
 	public boolean scheduleBuild(ParametersAction parameters, Cause c) {
-        return Hudson.getInstance().getQueue().add(
-                new ParameterizedProjectTask(this, parameters.getParameters(), c), getQuietPeriod());
+        return Hudson.getInstance().getQueue().add(this, getQuietPeriod(), parameters);
 	}
 	
 }
Index: core/src/main/java/hudson/model/AbstractProject.java
===================================================================
--- core/src/main/java/hudson/model/AbstractProject.java	(revision 15166)
+++ core/src/main/java/hudson/model/AbstractProject.java	(working copy)
@@ -64,6 +64,7 @@
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
@@ -450,10 +451,10 @@
             return false;
 
         if (isParameterized())
-            return Hudson.getInstance().getQueue().add(
-                    new ParameterizedProjectTask(this, getDefaultParametersValues(), c), quietPeriod);
+        	return Hudson.getInstance().getQueue().add(
+        			this, quietPeriod, new ParametersAction(getDefaultParametersValues()), new CauseAction(c));
         else
-            return Hudson.getInstance().getQueue().add(new ParameterizedProjectTask(this, c), quietPeriod);
+            return Hudson.getInstance().getQueue().add(this, quietPeriod, new CauseAction(c));
     }
 
     private List<ParameterValue> getDefaultParametersValues() {
@@ -1016,10 +1017,8 @@
                     // TODO: more unit handling
                     if(delay.endsWith("sec"))   delay=delay.substring(0,delay.length()-3);
                     if(delay.endsWith("secs"))  delay=delay.substring(0,delay.length()-4);
-                    Hudson.getInstance().getQueue().add(
-                    		new ParameterizedProjectTask(this, new UserCause()), 
-                    		Integer.parseInt(delay)
-                    );
+                    Hudson.getInstance().getQueue().add(this, Integer.parseInt(delay), 
+                    		new CauseAction(new UserCause()));
                 } catch (NumberFormatException e) {
                     throw new ServletException("Invalid delay parameter value: "+delay);
                 }
Index: core/src/main/java/hudson/model/Executor.java
===================================================================
--- core/src/main/java/hudson/model/Executor.java	(revision 15166)
+++ core/src/main/java/hudson/model/Executor.java	(working copy)
@@ -92,13 +92,14 @@
                     }
                 }
 
-                Queue.Task task;
+                Queue.Item queueItem;
                 try {
-                    task = queue.pop();
+                	queueItem = queue.pop();
                 } catch (InterruptedException e) {
                     continue;
                 }
 
+                Queue.Task task = queueItem.task;
                 Throwable problems = null;
                 owner.taskAccepted(this, task);
                 try {
@@ -110,6 +111,11 @@
 
                         startTime = System.currentTimeMillis();
                         executable = task.createExecutable();
+                        if (executable instanceof Actionable) {
+                        	for (Action action: queueItem.getActions()) {
+                        		((Actionable) executable).addAction(action);
+                        	}
+                        }
                         queue.execute(executable, task);
                     } catch (Throwable e) {
                         // for some reason the executor died. this is really
Index: core/src/main/java/hudson/model/ParameterizedProjectTask.java
===================================================================
--- core/src/main/java/hudson/model/ParameterizedProjectTask.java	(revision 15166)
+++ core/src/main/java/hudson/model/ParameterizedProjectTask.java	(working copy)
@@ -1,141 +0,0 @@
-/*
- * The MIT License
- * 
- * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package hudson.model;
-
-import hudson.model.Queue.Executable;
-import hudson.util.QueueTaskFilter;
-
-import java.io.IOException;
-import java.util.List;
-
-import javax.servlet.ServletException;
-
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-
-/**
- * A task representing a project that should be built with a certain set of
- * parameter values
- */
-public class ParameterizedProjectTask extends QueueTaskFilter {
-
-    private final AbstractProject<?,?> project;
-    private final List<ParameterValue> parameters;
-    private final Cause cause;
-    
-    private static long COUNTER = System.currentTimeMillis(); 
-
-    /**
-     * Used for identifying the task in the queue
-     */
-    private final String key = Long.toString(COUNTER++);
-
-	/**
-	 * @deprecated
-	 *    Use {@link #ParameterizedProjectTask(AbstractProject, List, Cause)}.  Since 1.283
-	 */
-    public ParameterizedProjectTask(AbstractProject<?,?> project, List<ParameterValue> parameters) {
-        this(project, parameters, new Cause.LegacyCodeCause());
-    }
-    
-    public ParameterizedProjectTask(AbstractProject<?,?> project, List<ParameterValue> parameters, Cause c) {
-        super(project);
-        this.project = project;
-        this.parameters = parameters;
-        this.cause = c;
-    }
-
-    public ParameterizedProjectTask(AbstractProject<?,?> project, Cause c) {
-        this(project, null, c);
-    }
-
-    public AbstractProject<?, ?> getProject() {
-		return project;
-	}
-
-	public List<ParameterValue> getParameters() {
-		return parameters;
-	}
-
-	@Override
-    public Executable createExecutable() throws IOException {
-        AbstractBuild<?, ?> build = project.createExecutable();
-        if(parameters != null) 
-        	build.addAction(new ParametersAction(parameters, build));
-        build.addAction(new CauseAction(cause));
-        return build;
-    }
-	
-    /**
-     * Cancels a scheduled build.
-     */
-    public void doCancelQueue( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
-        project.checkPermission(AbstractProject.BUILD);
-
-        Hudson.getInstance().getQueue().cancel(this);
-        rsp.forwardToPreviousPage(req);
-    }
-	
-
-    @Override
-    public int hashCode() {
-    	// cause is NOT included so distinct causes won't schedule duplicate builds
-        final int prime = 31;
-        int result = 1;
-        result = prime * result
-                + ((parameters == null) ? 0 : parameters.hashCode());
-        result = prime * result + ((project == null) ? 0 : project.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-    	// cause is NOT included so distinct causes won't schedule duplicate builds
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        ParameterizedProjectTask other = (ParameterizedProjectTask) obj;
-        if (parameters == null) {
-            if (other.parameters != null)
-                return false;
-        } else if (!parameters.equals(other.parameters)) {
-            return false;
-        }
-        if (project != other.project) {
-            return false;
-        }
-        return true;
-    }
-    
-    public String getUrl() {
-    	return getProject().getUrl() + "/parameters/queued/" + key + "/";
-    }
-
-	public String getQueueKey() {
-		return key;
-	}
-}
Index: core/src/main/java/hudson/model/ParametersAction.java
===================================================================
--- core/src/main/java/hudson/model/ParametersAction.java	(revision 15166)
+++ core/src/main/java/hudson/model/ParametersAction.java	(working copy)
@@ -24,6 +24,7 @@
 package hudson.model;
 
 import hudson.Util;
+import hudson.model.Queue.QueueAction;
 import hudson.tasks.BuildWrapper;
 import hudson.tasks.BuildStep;
 import hudson.util.VariableResolver;
@@ -40,14 +41,12 @@
  * This object is associated with the build record so that we remember what parameters
  * were used for what build.
  */
-public class ParametersAction implements Action, Iterable<ParameterValue> {
+public class ParametersAction implements Action, Iterable<ParameterValue>, QueueAction {
 
     private final List<ParameterValue> parameters;
-    private final AbstractBuild<?, ?> build;
 
-    public ParametersAction(List<ParameterValue> parameters, AbstractBuild<?, ?> build) {
+    public ParametersAction(List<ParameterValue> parameters) {
         this.parameters = parameters;
-        this.build = build;
     }
 
     public void createBuildWrappers(AbstractBuild<?,?> build, Collection<? super BuildWrapper> result) {
@@ -86,10 +85,6 @@
         return new VariableResolver.Union<String>(resolvers);
     }
 
-    public AbstractBuild<?, ?> getBuild() {
-        return build;
-    }
-
     public Iterator<ParameterValue> iterator() {
         return parameters.iterator();
     }
@@ -109,4 +104,12 @@
     public String getUrlName() {
         return "parameters";
     }
+
+    /**
+     * Allow any other build of the same project to be scheduled, regardless if it has no, the same, or other parameters.
+     */
+	public boolean shouldSchedule(List<Action> actions) {
+		return true;
+	}
+
 }
Index: core/src/main/java/hudson/model/ParametersDefinitionProperty.java
===================================================================
--- core/src/main/java/hudson/model/ParametersDefinitionProperty.java	(revision 15166)
+++ core/src/main/java/hudson/model/ParametersDefinitionProperty.java	(working copy)
@@ -26,7 +26,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 import javax.servlet.ServletException;
 
@@ -97,8 +96,8 @@
             values.add(d.createValue(req, jo));
         }
 
-        Hudson.getInstance().getQueue().add(
-                new ParameterizedProjectTask(owner, values, new Cause.UserCause()), 0);
+    	Hudson.getInstance().getQueue().add(
+    			owner, 0, new ParametersAction(values), new CauseAction(new Cause.UserCause()));
 
         // send the user back to the job top page.
         rsp.sendRedirect(".");
@@ -116,7 +115,7 @@
         }
 
     	Hudson.getInstance().getQueue().add(
-                new ParameterizedProjectTask(owner, values, new Cause.UserCause()), 0);
+    			owner, 0, new ParametersAction(values), new CauseAction(new Cause.UserCause()));
 
         // send the user back to the job top page.
         rsp.sendRedirect(".");
@@ -182,19 +181,6 @@
         return "parameters";
     }
     
-    public ParameterizedProjectTask getQueued(String key) {
-    	Queue.Item[] items = Hudson.getInstance().getQueue().getItems();
-    	for (Queue.Item item: items) {
-    		if (item.task instanceof ParameterizedProjectTask) {
-    			ParameterizedProjectTask ppt = (ParameterizedProjectTask) item.task;
-				if (ppt.getProject() == owner && ppt.getQueueKey().equals(key)) {
-    				return ppt;
-    			}
-    		}
-    	}
-    	return null;
-    }
-
     static {
         ParameterDefinition.LIST.add(StringParameterDefinition.DESCRIPTOR);
         ParameterDefinition.LIST.add(FileParameterDefinition.DESCRIPTOR);
Index: core/src/main/java/hudson/model/Queue.java
===================================================================
--- core/src/main/java/hudson/model/Queue.java	(revision 15166)
+++ core/src/main/java/hudson/model/Queue.java	(working copy)
@@ -40,17 +40,18 @@
 import java.io.InputStreamReader;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -102,27 +103,18 @@
     private final Set<WaitingItem> waitingList = new TreeSet<WaitingItem>();
 
     /**
-     * {@link Project}s that can be built immediately
+     * {@link Task}s that can be built immediately
      * but blocked because another build is in progress,
      * required {@link Resource}s are not available, or otherwise blocked
      * by {@link Task#isBuildBlocked()}.
-     *
-     * <p>
-     * Conceptually a set of {@link BlockedItem}, but we often need to look up
-     * {@link BlockedItem} from {@link Task}, so organized as a map.
      */
-    private final Map<Task,BlockedItem> blockedProjects = new HashMap<Task,BlockedItem>();
+    private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>();
 
     /**
-     * {@link Project}s that can be built immediately
+     * {@link Task}s that can be built immediately
      * that are waiting for available {@link Executor}.
-     *
-     * <p>
-     * Conceptually, this is a list of {@link BuildableItem} (FIFO list, not a set, so that
-     * the item doesn't starve in the queue), but we often need to look up
-     * {@link BuildableItem} from {@link Task}, so organized as a {@link LinkedHashMap}.
      */
-    private final LinkedHashMap<Task,BuildableItem> buildables = new LinkedHashMap<Task,BuildableItem>();
+    private final ItemList<BuildableItem> buildables = new ItemList<BuildableItem>();
 
     /**
      * Data structure created for each idle {@link Executor}.
@@ -200,9 +192,29 @@
             } else {
                 queueFile = getXMLQueueFile();
                 if (queueFile.exists()) {
-                    List<Task> tasks = (List<Task>) new XmlFile(XSTREAM, queueFile).read();
-                    for (Task task : tasks) {
-                        add(task, 0);
+                    List list = (List) new XmlFile(XSTREAM, queueFile).read();
+                    if (!list.isEmpty()) {
+                    	if (list.get(0) instanceof Queue.Task) {
+                    		// backward compatiblity
+                    		for (Task task : (List<Task>) list) {
+                    			add(task, 0);
+                    		}
+                    	} else if (list.get(0) instanceof Item) {
+                    		int maxId = 0;
+                    		for (Item item: (List<Item>) list) {
+                    			maxId = Math.max(maxId, item.id);
+                    			if (item instanceof WaitingItem) {
+                    				waitingList.add((WaitingItem) item);
+                    			} else if (item instanceof BlockedItem) {
+                    				blockedProjects.put(item.task, (BlockedItem) item);
+                    			} else if (item instanceof BuildableItem) {
+                    				buildables.add((BuildableItem) item);
+                    			} else {
+                    				throw new IllegalStateException("Unknown item type! " + item);
+                    			}
+                    		}
+                    		WaitingItem.COUNTER.set(maxId);
+                    	}
                     }
 
                     // I just had an incident where all the executors are dead at AbstractProject._getRuns()
@@ -228,13 +240,13 @@
         if(BulkChange.contains(this))  return;
         
         // write out the tasks on the queue
-    	ArrayList<Task> tasks = new ArrayList<Task>();
+    	ArrayList<Queue.Item> items = new ArrayList<Queue.Item>();
     	for (Item item: getItems()) {
-    	    tasks.add(item.task);
+    	    items.add(item);
     	}
     	
         try {
-            new XmlFile(XSTREAM, getXMLQueueFile()).write(tasks);
+            new XmlFile(XSTREAM, getXMLQueueFile()).write(items);
         } catch (IOException e) {
             LOGGER.log(Level.WARNING, "Failed to write out the queue file " + getQueueFile(), e);
         }
@@ -289,47 +301,69 @@
      *                    times.
      * @since 1.114
      */
-    public synchronized boolean add(Task p, int quietPeriod) {
+    public synchronized boolean add(Task p, int quietPeriod, List<Action> actions) {
         Item item = getItem(p);
         Calendar due = new GregorianCalendar();
         due.add(Calendar.SECOND, quietPeriod);
         if (item != null) {
-            if (!(item instanceof WaitingItem))
-                // already in the blocked or buildable stage
-                // no need to requeue
-                return false;
+        	
+        	boolean shouldSchedule = false;
+        	for (Action action: item.getActions()) {
+        		if (action instanceof QueueAction)
+        			shouldSchedule |= ((QueueAction) action).shouldSchedule(actions);
+        	}
+        	if (shouldSchedule) {
+        		//TODO rework the if-s to only have this once
+                LOGGER.fine(p.getFullDisplayName() + " added to queue");
 
-            WaitingItem wi = (WaitingItem) item;
-
-            if(quietPeriod<=0) {
-                // the user really wants to build now, and they mean NOW.
-                // so let's pull in the timestamp if we can.
-                if (wi.timestamp.before(due))
-                    return false;
-            } else {
-                // otherwise we do the normal quiet period implementation
-                if (wi.timestamp.after(due))
-                    return false;
-                // quiet period timer reset. start the period over again
-            }
-
-            // waitingList is sorted, so when we change a timestamp we need to maintain order
-            waitingList.remove(wi);
-            wi.timestamp = due;
-            waitingList.add(wi);
+                // put the item in the queue
+                waitingList.add(new WaitingItem(due,p,actions));
+        	} else {
+	            if (!(item instanceof WaitingItem))
+	                // already in the blocked or buildable stage
+	                // no need to requeue
+	                return false;
+	
+	            WaitingItem wi = (WaitingItem) item;
+	
+	            if(quietPeriod<=0) {
+	                // the user really wants to build now, and they mean NOW.
+	                // so let's pull in the timestamp if we can.
+	                if (wi.timestamp.before(due))
+	                    return false;
+	            } else {
+	                // otherwise we do the normal quiet period implementation
+	                if (wi.timestamp.after(due))
+	                    return false;
+	                // quiet period timer reset. start the period over again
+	            }
+	        	
+	            // waitingList is sorted, so when we change a timestamp we need to maintain order
+	            waitingList.remove(wi);
+	            wi.timestamp = due;
+	            waitingList.add(wi);
+        	}
         } else {
             LOGGER.fine(p.getFullDisplayName() + " added to queue");
 
             // put the item in the queue
-            waitingList.add(new WaitingItem(due,p));
+            waitingList.add(new WaitingItem(due,p,actions));
 
         }
         scheduleMaintenance();   // let an executor know that a new item is in the queue.
         return true;
     }
+    
+    public synchronized boolean add(Task p, int quietPeriod) {
+    	return add(p, quietPeriod, new Action[0]);
+    }
 
+    public synchronized boolean add(Task p, int quietPeriod, Action... actions) {
+    	return add(p, quietPeriod, Arrays.asList(actions));
+    }
+
     /**
-     * Cancels the item in the queue.
+     * Cancels the item in the queue. If the item is scheduled more than once, cancels the first occurrence.
      *
      * @return true if the project was indeed in the queue and was removed.
      *         false if this was no-op.
@@ -346,6 +380,12 @@
         // use bitwise-OR to make sure that both branches get evaluated all the time
         return blockedProjects.remove(p)!=null | buildables.remove(p)!=null;
     }
+    
+    public synchronized boolean cancel(Item item) {
+        LOGGER.fine("Cancelling " + item.task.getFullDisplayName() + " item#" + item.id);
+        // use bitwise-OR to make sure that both branches get evaluated all the time
+        return (item instanceof WaitingItem && waitingList.remove(item)) | blockedProjects.remove(item) | buildables.remove(item);
+    }
 
     public synchronized boolean isEmpty() {
         return waitingList.isEmpty() && blockedProjects.isEmpty() && buildables.isEmpty();
@@ -369,6 +409,13 @@
             r[idx++] = p;
         return r;
     }
+    
+    public synchronized Item getItem(int id) {
+    	for (Item item: waitingList) if (item.id == id) return item;
+    	for (Item item: blockedProjects) if (item.id == id) return item;
+    	for (Item item: buildables) if (item.id == id) return item;
+    	return null;
+    }
 
     /**
      * Gets all the {@link BuildableItem}s that are waiting for an executor in the given {@link Computer}.
@@ -426,6 +473,22 @@
     }
 
     /**
+     * Gets the information about the queue item for the given project.
+     *
+     * @return null if the project is not in the queue.
+     */
+    public synchronized List<Item> getItems(Task t) {
+    	List<Item> result =new ArrayList<Item>();
+    	result.addAll(blockedProjects.getAll(t));
+    	result.addAll(buildables.getAll(t));
+        for (Item item : waitingList) {
+            if (item.task == t)
+                result.add(item);
+        }
+        return result;
+    }
+
+    /**
      * Left for backward compatibility.
      *
      * @see #getItem(Task)
@@ -452,7 +515,7 @@
      * <p>
      * This method blocks until a next project becomes buildable.
      */
-    public Task pop() throws InterruptedException {
+    public Queue.Item pop() throws InterruptedException {
         final Executor exec = Executor.currentExecutor();
 
         try {
@@ -471,7 +534,7 @@
                     maintain();
 
                     // allocate buildable jobs to executors
-                    Iterator<BuildableItem> itr = buildables.values().iterator();
+                    Iterator<BuildableItem> itr = buildables.iterator();
                     while (itr.hasNext()) {
                         BuildableItem p = itr.next();
 
@@ -522,7 +585,7 @@
                     if (offer.item != null) {
                         LOGGER.fine("Pop returning " + offer.item + " for " + exec.getName());
                         // if so, just build it
-                        return offer.item.task;
+                        return offer.item;
                     }
                     // otherwise run a queue maintenance
                 }
@@ -784,10 +847,6 @@
          */
         String getUrl();
         
-        /**
-         * Called from queue.jelly.
-         */
-        void doCancelQueue( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException;
     }
 
     public interface Executable extends Runnable {
@@ -807,13 +866,16 @@
      * Item in a queue.
      */
     @ExportedBean(defaultVisibility = 999)
-    public abstract class Item {
-        /**
+    public static abstract class Item extends Actionable {
+    	
+    	public final int id;
+    	
+		/**
          * Project to be built.
          */
         @Exported
         public final Task task;
-
+        
         /**
          * Build is blocked because another build is in progress,
          * required {@link Resource}s are not available, or otherwise blocked
@@ -836,12 +898,18 @@
         @Exported
         public boolean isStuck() { return false; }
 
-        protected Item(Task project) {
-            this.task = project;
+        protected Item(Task task, List<Action> actions, int id) {
+            this.task = task;
+            this.id = id;
+            for (Action action: actions) addAction(action);
         }
+        
+        protected Item(Item item) {
+        	this(item.task, item.getActions(), item.id);
+        }
 
         /**
-         * Gets a human-readable status message describing why it's in the queu.
+         * Gets a human-readable status message describing why it's in the queue.
          */
         @Exported
         public abstract String getWhy();
@@ -849,32 +917,55 @@
         public boolean hasCancelPermission() {
             return task.hasAbortPermission();
         }
+        
+        public String getDisplayName() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+
+		public String getSearchUrl() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+
+        /**
+         * Called from queue.jelly.
+         */
+        public void doCancelQueue( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
+        	Hudson.getInstance().getQueue().cancel(this);
+            rsp.forwardToPreviousPage(req);
+        }
     }
+    
+    /**
+     * An optional interface for actions on Queue.Item.
+     * Lets the action cooperate in queue management.
+     */
+    public interface QueueAction extends Action {
+    	/**
+    	 * Returns whether the new item should be scheduled. 
+    	 * An action should return true if the associated task is 'different enough' to warrant a separate execution.
+    	 */
+    	public boolean shouldSchedule(List<Action> actions);
+    }
 
     /**
      * {@link Item} in the {@link Queue#waitingList} stage.
      */
-    public final class WaitingItem extends Item implements Comparable<WaitingItem> {
+    public static final class WaitingItem extends Item implements Comparable<WaitingItem> {
+    	private static final AtomicInteger COUNTER = new AtomicInteger(0);
+    	
         /**
          * This item can be run after this time.
          */
         @Exported
         public Calendar timestamp;
 
-        /**
-         * Unique number of this {@link WaitingItem}.
-         * Used to differentiate {@link WaitingItem}s with the same due date, to make it sortable.
-         */
-        public final int id;
-
-        WaitingItem(Calendar timestamp, Task project) {
-            super(project);
+        WaitingItem(Calendar timestamp, Task project, List<Action> actions) {
+            super(project, actions, COUNTER.incrementAndGet());
             this.timestamp = timestamp;
-            synchronized (Queue.this) {
-                this.id = iota++;
-            }
         }
-
+        
         public int compareTo(WaitingItem that) {
             int r = this.timestamp.getTime().compareTo(that.timestamp.getTime());
             if (r != 0) return r;
@@ -895,7 +986,7 @@
     /**
      * Common part between {@link BlockedItem} and {@link BuildableItem}.
      */
-    public abstract class NotWaitingItem extends Item {
+    public static abstract class NotWaitingItem extends Item {
         /**
          * When did this job exit the {@link Queue#waitingList} phase?
          */
@@ -903,12 +994,12 @@
         public final long buildableStartMilliseconds;
 
         protected NotWaitingItem(WaitingItem wi) {
-            super(wi.task);
+            super(wi);
             buildableStartMilliseconds = System.currentTimeMillis();
         }
 
         protected NotWaitingItem(NotWaitingItem ni) {
-            super(ni.task);
+            super(ni);
             buildableStartMilliseconds = ni.buildableStartMilliseconds;
         }
     }
@@ -940,7 +1031,7 @@
     /**
      * {@link Item} in the {@link Queue#buildables} stage.
      */
-    public final class BuildableItem extends NotWaitingItem {
+    public final static class BuildableItem extends NotWaitingItem {
         public BuildableItem(WaitingItem wi) {
             super(wi);
         }
@@ -1075,4 +1166,54 @@
                 cancel();
         }
     }
+    
+    /**
+     * A MultiMap -  LinkedMap crossover as a drop-in replacement for the previously used LinkedHashMap
+     * And no, I don't care about performance ;)
+     */
+    private static class ItemList<T extends Item> extends ArrayList<T> {
+    	public T get(Task task) {
+    		for (T item: this) {
+    			if (item.task == task) {
+    				return item;
+    			}
+    		}
+    		return null;
+    	}
+    	
+    	public List<T> getAll(Task task) {
+    		List<T> result = new ArrayList<T>();
+    		for (T item: this) {
+    			if (item.task == task) {
+    				result.add(item);
+    			}
+    		}
+    		return result;
+    	}
+    	
+    	public boolean containsKey(Task task) {
+    		return get(task) != null;
+    	}
+    	
+    	public T remove(Task task) {
+    		Iterator<T> it = iterator();
+    		while (it.hasNext()) {
+    			T t = it.next();
+    			if (t.task == task) {
+    				it.remove();
+    				return t;
+    			}
+    		}
+    		return null;
+    	}
+    	
+    	public void put(Task task, T item) {
+    		assert item.task == task;
+    		add(item);
+    	}
+    	
+    	public ItemList<T> values() {
+    		return this;
+    	}
+    }
 }
Index: core/src/main/java/hudson/widgets/BuildHistoryWidget.java
===================================================================
--- core/src/main/java/hudson/widgets/BuildHistoryWidget.java	(revision 15166)
+++ core/src/main/java/hudson/widgets/BuildHistoryWidget.java	(working copy)
@@ -27,6 +27,10 @@
 import hudson.model.Queue.Item;
 import hudson.model.Queue.Task;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Displays the build history on the side panel.
  *
@@ -46,9 +50,18 @@
     }
 
     /**
-     * Returns the queue item if the owner is scheduled for execution in the queue.
+     * Returns the first queue item if the owner is scheduled for execution in the queue.
      */
     public Item getQueuedItem() {
         return Hudson.getInstance().getQueue().getItem(owner);
     }
+
+    /**
+     * Returns the queue item if the owner is scheduled for execution in the queue, in REVERSE ORDER
+     */
+    public List<Item> getQueuedItems() {
+    	List<Item> list = new ArrayList<Item>(Hudson.getInstance().getQueue().getItems(owner));
+    	Collections.reverse(list);
+    	return list;
+    }
 }
Index: core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly
===================================================================
--- core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly	(revision 15166)
+++ core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly	(working copy)
@@ -27,23 +27,25 @@
 -->
 <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
   <!-- pending build -->
-  <j:set var="pending" value="${it.queuedItem}" />
-  <j:if test="${pending!=null}">
+  <j:set var="queuedItems" value="${it.queuedItems}" />
+  <j:if test="${!pending.isEmpty()}">
+	  <j:forEach var="item" items="${queuedItems}" indexVar="i">
     <j:set var="id" value="${h.generateId()}"/>
     <tr class="build-row transitive" id="${id}">
       <td nowrap="nowrap">
         <img width="16" height="16" src="${imagesURL}/16x16/grey.gif" alt="${%pending}" /><st:nbsp/>
-        #${it.owner.nextBuildNumber}
+        #${it.owner.nextBuildNumber+queuedItems.size()-i-1}
       </td>
-      <td nowrap="nowrap" tooltip="${pending.why}">
+      <td nowrap="nowrap" tooltip="${item.task.why}">
         <div style="float:right">
-          <j:if test="${pending.hasCancelPermission()}">
-            <a href="${rootURL}/${pending.task.url}cancelQueue"><img src="${imagesURL}/16x16/stop.gif" alt="${%cancel this build}" /></a>
+          <j:if test="${item.hasCancelPermission()}">
+            <a href="${rootURL}/queue/item/${item.id}/cancelQueue"><img src="${imagesURL}/16x16/stop.gif" alt="cancel this build" /></a>
           </j:if>
         </div>
         (${%pending})
       </td>
     </tr>
+	  </j:forEach>
   </j:if>
 
   <st:include page="/hudson/widgets/HistoryWidget/entries.jelly" />
Index: core/src/main/resources/lib/hudson/queue.jelly
===================================================================
--- core/src/main/resources/lib/hudson/queue.jelly	(revision 15166)
+++ core/src/main/resources/lib/hudson/queue.jelly	(working copy)
@@ -28,7 +28,7 @@
 
     <st:attribute name="items" use="required">
       Queue items to be displayed. Normally you should specify ${app.queue.items},
-      but for example yuo can specify a sublist after some filtering to narrow down
+      but for example you can specify a sublist after some filtering to narrow down
       the list.
     </st:attribute>
   </st:documentation>
@@ -65,7 +65,7 @@
             </td>
             <td class="pane" width="16" align="center" valign="middle">
               <j:if test="${item.hasCancelPermission()}">
-                <a href="${rootURL}/${item.task.url}cancelQueue"><img src="${imagesURL}/16x16/stop.gif" alt="cancel this build" /></a>
+                <a href="${rootURL}/queue/item/${item.id}/cancelQueue"><img src="${imagesURL}/16x16/stop.gif" alt="cancel this build" /></a>
               </j:if>
              </td>
           </tr>
Index: test/src/test/java/hudson/model/QueueTest.java
===================================================================
--- test/src/test/java/hudson/model/QueueTest.java	(revision 15166)
+++ test/src/test/java/hudson/model/QueueTest.java	(working copy)
@@ -43,6 +43,7 @@
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -155,5 +156,36 @@
             server.stop();
         }
     }
+    
+    public void testParallelSchedulingNotAllowedForPlainProjects() throws Exception {
+        // prevent execution to push stuff into the queue
+        hudson.setNumExecutors(0);
+        hudson.setNodes(hudson.getNodes());
 
+        FreeStyleProject testProject = createFreeStyleProject("test");
+        testProject.scheduleBuild();
+        
+        assert hudson.getQueue().getItems().length == 1;
+    	
+        testProject.scheduleBuild();
+
+        assert hudson.getQueue().getItems().length == 1;
+    }
+
+    public void testParallelSchedulingAllowedForParameterizedProjects() throws Exception {
+        // prevent execution to push stuff into the queue
+        hudson.setNumExecutors(0);
+        hudson.setNodes(hudson.getNodes());
+
+        FreeStyleProject testProject = createFreeStyleProject("test");
+    	Hudson.getInstance().getQueue().add(
+    			testProject, 0, new ParametersAction(Collections.EMPTY_LIST));
+        
+        assert hudson.getQueue().getItems().length == 1;
+    	
+    	Hudson.getInstance().getQueue().add(
+    			testProject, 0, new ParametersAction(Collections.EMPTY_LIST));
+
+    	assert hudson.getQueue().getItems().length == 2;
+    }
 }
