KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > triggers > SCMTrigger


1 package hudson.triggers;
2
3 import antlr.ANTLRException;
4 import hudson.Util;
5 import hudson.model.AbstractBuild;
6 import hudson.model.AbstractProject;
7 import hudson.model.Action;
8 import hudson.model.Item;
9 import hudson.model.Project;
10 import hudson.model.SCMedItem;
11 import hudson.model.TaskListener;
12 import hudson.util.StreamTaskListener;
13 import org.kohsuke.stapler.StaplerRequest;
14
15 import java.io.File JavaDoc;
16 import java.io.FileOutputStream JavaDoc;
17 import java.io.IOException JavaDoc;
18 import java.io.OutputStream JavaDoc;
19 import java.io.PrintStream JavaDoc;
20 import java.util.Date JavaDoc;
21 import java.util.concurrent.CancellationException JavaDoc;
22 import java.util.concurrent.ExecutionException JavaDoc;
23 import java.util.concurrent.ExecutorService JavaDoc;
24 import java.util.concurrent.Executors JavaDoc;
25 import java.util.concurrent.Future JavaDoc;
26 import java.util.logging.Level JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28
29 /**
30  * {@link Trigger} that checks for SCM updates periodically.
31  *
32  * @author Kohsuke Kawaguchi
33  */

34 public class SCMTrigger extends Trigger<SCMedItem> {
35     /**
36      * If we'd like to run another polling run, this is set to true.
37      *
38      * <p>
39      * To avoid submitting more than one polling jobs (which could flood the queue),
40      * we first use the boolean flag.
41      *
42      * @guardedBy this
43      */

44     private transient boolean pollingScheduled;
45
46     /**
47      * Signal to the polling thread to abort now.
48      */

49     private transient boolean abortNow;
50
51     /**
52      * Pending polling activity in progress.
53      * There's at most one polling activity per project at any given point.
54      *
55      * @guardedBy this
56      */

57     private transient Future JavaDoc<?> polling;
58
59     public SCMTrigger(String JavaDoc cronTabSpec) throws ANTLRException {
60         super(cronTabSpec);
61     }
62
63     protected synchronized void run() {
64         if(pollingScheduled)
65             return; // noop
66
pollingScheduled = true;
67
68         // otherwise do it now
69
startPolling();
70     }
71
72     public Action getProjectAction() {
73         return new SCMAction();
74     }
75
76     /**
77      * Makes sure that the polling is aborted.
78      */

79     public synchronized void abort() throws InterruptedException JavaDoc {
80         if(polling!=null && !polling.isDone()) {
81             System.out.println("killing polling");
82
83             abortNow = true;
84             polling.cancel(true);
85             try {
86                 polling.get();
87             } catch (ExecutionException JavaDoc e) {
88                 LOGGER.log(Level.WARNING, "Failed to poll",e);
89             } catch (CancellationException JavaDoc e) {
90                 // this is fine
91
}
92             abortNow = false;
93         }
94     }
95
96     /**
97      * Start polling if it's scheduled.
98      */

99     public synchronized void startPolling() {
100         AbstractBuild b = job.asProject().getLastBuild();
101
102         if(b!=null && b.isBuilding())
103             return; // build in progress
104

105         if(polling!=null && !polling.isDone())
106             return; // polling already in progress
107

108         if(!pollingScheduled)
109             return; // not scheduled
110

111         pollingScheduled = false;
112         polling = DESCRIPTOR.getExecutor().submit(new Runner());
113     }
114
115     /**
116      * Returns the file that records the last/current polling activity.
117      */

118     public File JavaDoc getLogFile() {
119         return new File JavaDoc(job.getRootDir(),"scm-polling.log");
120     }
121
122     public TriggerDescriptor getDescriptor() {
123         return DESCRIPTOR;
124     }
125
126     public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
127
128     public static final class DescriptorImpl extends TriggerDescriptor {
129         /**
130          * Used to control the execution of the polling tasks.
131          */

132         transient volatile ExecutorService JavaDoc executor;
133
134         /**
135          * Max number of threads for SCM polling.
136          * 0 for unbounded.
137          */

138         private int maximumThreads;
139
140         DescriptorImpl() {
141             super(SCMTrigger.class);
142             load();
143             // create an executor
144
update();
145         }
146
147         public boolean isApplicable(Item item) {
148             return item instanceof SCMedItem;
149         }
150
151         public ExecutorService JavaDoc getExecutor() {
152             return executor;
153         }
154
155         public String JavaDoc getDisplayName() {
156             return "Poll SCM";
157         }
158
159         public String JavaDoc getHelpFile() {
160             return "/help/project-config/poll-scm.html";
161         }
162
163         public Trigger newInstance(StaplerRequest req) throws FormException {
164             try {
165                 return new SCMTrigger(req.getParameter("scmpoll_spec"));
166             } catch (ANTLRException e) {
167                 throw new FormException(e.toString(),e,"scmpoll_spec");
168             }
169         }
170
171         /**
172          * Gets the number of concurrent threads used for polling.
173          *
174          * @return
175          * 0 if unlimited.
176          */

177         public int getPollingThreadCount() {
178             return maximumThreads;
179         }
180
181         public void setPollingThreadCount(int n) {
182             // fool proof
183
if(n<0) n=0;
184             if(n>100) n=100;
185
186             maximumThreads = n;
187             save();
188         }
189
190         /**
191          * Update the {@link ExecutorService} instance.
192          */

193         /*package*/ synchronized void update() {
194             // swap to a new one, and shut down the old one gradually
195
ExecutorService JavaDoc newExec = maximumThreads==0 ? Executors.newCachedThreadPool() : Executors.newFixedThreadPool(maximumThreads);
196             ExecutorService JavaDoc old = executor;
197             executor = newExec;
198             if(old!=null)
199                 old.shutdown();
200         }
201
202         public boolean configure(StaplerRequest req) throws FormException {
203             String JavaDoc t = req.getParameter("poll_scm_threads");
204             if(t==null || t.length()==0)
205                 setPollingThreadCount(0);
206             else
207                 setPollingThreadCount(Integer.parseInt(t));
208             return super.configure(req);
209         }
210     }
211
212     /**
213      * Action object for {@link Project}. Used to display the polling log.
214      */

215     public final class SCMAction implements Action {
216         public AbstractProject<?,?> getOwner() {
217             return job.asProject();
218         }
219
220         public String JavaDoc getIconFileName() {
221             return "clipboard.gif";
222         }
223
224         public String JavaDoc getDisplayName() {
225             return job.getScm().getDescriptor().getDisplayName()+" Polling Log";
226         }
227
228         public String JavaDoc getUrlName() {
229             return "scmPollLog";
230         }
231
232         public String JavaDoc getLog() throws IOException JavaDoc {
233             return Util.loadFile(getLogFile());
234         }
235     }
236
237     private static final Logger JavaDoc LOGGER = Logger.getLogger(SCMTrigger.class.getName());
238
239     /**
240      * {@link Runnable} that actually performs polling.
241      */

242     private class Runner implements Runnable JavaDoc {
243         private boolean runPolling() {
244             try {
245                 // to make sure that the log file contains up-to-date text,
246
// don't do buffering.
247
OutputStream JavaDoc fos = new FileOutputStream JavaDoc(getLogFile());
248                 TaskListener listener = new StreamTaskListener(fos);
249
250                 try {
251                     LOGGER.info("Polling SCM changes of "+ job.getName());
252
253                     PrintStream JavaDoc logger = listener.getLogger();
254                     long start = System.currentTimeMillis();
255                     logger.println("Started on "+new Date JavaDoc().toLocaleString());
256                     boolean result = job.pollSCMChanges(listener);
257                     logger.println("Done. Took "+ Util.getTimeSpanString(System.currentTimeMillis()-start));
258                     if(result)
259                         logger.println("Changes found");
260                     else
261                         logger.println("No changes");
262                     return result;
263                 } finally {
264                     fos.close();
265                 }
266             } catch (IOException JavaDoc e) {
267                 LOGGER.log(Level.SEVERE,"Failed to record SCM polling",e);
268                 return false;
269             }
270         }
271
272         public void run() {
273             if(runPolling()) {
274                 LOGGER.info("SCM changes detected in "+ job.getName());
275                 job.scheduleBuild();
276             }
277
278             synchronized(SCMTrigger.this) {
279                 if(abortNow)
280                     return; // terminate now without queueing the next one.
281

282                 if(pollingScheduled) {
283                     // schedule a next run
284
polling = DESCRIPTOR.getExecutor().submit(new Runner());
285                 }
286                 pollingScheduled = false;
287             }
288         }
289     }
290 }
291
Popular Tags