PageRenderTime 40ms CodeModel.GetById 14ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-core/src/main/java/hudson/model/ViewJob.java

http://github.com/hudson/hudson
Java | 211 lines | 105 code | 25 blank | 81 comment | 9 complexity | 0a81848f2a64bc4bbcc92e3617183c65 MD5 | raw file
  1/*
  2 * The MIT License
  3 * 
  4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
  5 * 
  6 * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 * of this software and associated documentation files (the "Software"), to deal
  8 * in the Software without restriction, including without limitation the rights
  9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 * copies of the Software, and to permit persons to whom the Software is
 11 * furnished to do so, subject to the following conditions:
 12 * 
 13 * The above copyright notice and this permission notice shall be included in
 14 * all copies or substantial portions of the Software.
 15 * 
 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22 * THE SOFTWARE.
 23 */
 24package hudson.model;
 25
 26import org.kohsuke.stapler.StaplerRequest;
 27import org.kohsuke.stapler.StaplerResponse;
 28
 29import javax.servlet.ServletException;
 30import java.io.IOException;
 31import java.util.LinkedHashSet;
 32import java.util.SortedMap;
 33
 34import hudson.model.Descriptor.FormException;
 35
 36/**
 37 * {@link Job} that monitors activities that happen outside Hudson,
 38 * which requires occasional batch reload activity to obtain the up-to-date information.
 39 *
 40 * <p>
 41 * This can be used as a base class to derive custom {@link Job} type.
 42 *
 43 * @author Kohsuke Kawaguchi
 44 */
 45public abstract class ViewJob<JobT extends ViewJob<JobT,RunT>, RunT extends Run<JobT,RunT>>
 46    extends Job<JobT,RunT> {
 47
 48    /**
 49     * We occasionally update the list of {@link Run}s from a file system.
 50     * The next scheduled update time.
 51     */
 52    private transient long nextUpdate = 0;
 53
 54    /**
 55     * All {@link Run}s. Copy-on-write semantics.
 56     */
 57    protected transient /*almost final*/ RunMap<RunT> runs = new RunMap<RunT>();
 58
 59    private transient boolean notLoaded = true;
 60
 61    /**
 62     * If the reloading of runs are in progress (in another thread,
 63     * set to true.)
 64     */
 65    private transient volatile boolean reloadingInProgress;
 66
 67    /**
 68     * {@link ExternalJob}s that need to be reloaded.
 69     *
 70     * This is a set, so no {@link ExternalJob}s are scheduled twice, yet
 71     * it's order is predictable, avoiding starvation.
 72     */
 73    private static final LinkedHashSet<ViewJob> reloadQueue = new LinkedHashSet<ViewJob>();
 74    /*package*/ static final Thread reloadThread = new ReloadThread();
 75    static {
 76        reloadThread.start();
 77    }
 78
 79    /**
 80     * @deprecated as of 1.390
 81     */
 82    protected ViewJob(Hudson parent, String name) {
 83        super(parent,name);
 84    }
 85
 86    protected ViewJob(ItemGroup parent, String name) {
 87        super(parent,name);
 88    }
 89
 90    public boolean isBuildable() {
 91        return false;
 92    }
 93
 94    @Override
 95    public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
 96        super.onLoad(parent, name);
 97        notLoaded = true;
 98    }
 99
100    protected SortedMap<Integer,RunT> _getRuns() {
101        if(notLoaded || runs==null) {
102            // if none is loaded yet, do so immediately.
103            synchronized(this) {
104                if(runs==null)
105                    runs = new RunMap<RunT>();
106                if(notLoaded) {
107                    notLoaded = false;
108                    _reload();   
109                }
110            }
111        }
112        if(nextUpdate<System.currentTimeMillis()) {
113            if(!reloadingInProgress) {
114                // schedule a new reloading operation.
115                // we don't want to block the current thread,
116                // so reloading is done asynchronously.
117                reloadingInProgress = true;
118                synchronized(reloadQueue) {
119                    reloadQueue.add(this);
120                    reloadQueue.notify();
121                }
122            }
123        }
124        return runs;
125    }
126
127    public void removeRun(RunT run) {
128        // reload the info next time
129        nextUpdate = 0;
130    }
131
132    private void _reload() {
133        try {
134            reload();
135        } finally {
136            reloadingInProgress = false;
137            nextUpdate = reloadPeriodically ? System.currentTimeMillis()+1000*60 : Long.MAX_VALUE;
138        }
139    }
140
141    /**
142     * Reloads the list of {@link Run}s. This operation can take a long time.
143     *
144     * <p>
145     * The loaded {@link Run}s should be set to {@link #runs}.
146     */
147    protected abstract void reload();
148
149    @Override
150    protected void submit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
151        super.submit(req,rsp);
152        // make sure to reload to reflect this config change.
153        nextUpdate = 0;
154    }
155
156
157    /**
158     * Thread that reloads the {@link Run}s.
159     */
160    private static final class ReloadThread extends Thread {
161        private ReloadThread() {
162            setName("ViewJob reload thread");
163        }
164
165        private ViewJob getNext() throws InterruptedException {
166            synchronized(reloadQueue) {
167                // reload operations might eat InterruptException,
168                // so check the status every so often
169                while(reloadQueue.isEmpty() && !terminating())
170                    reloadQueue.wait(60*1000);
171                if(terminating())
172                    throw new InterruptedException();   // terminate now
173                ViewJob job = reloadQueue.iterator().next();
174                reloadQueue.remove(job);
175                return job;
176            }
177        }
178
179        private boolean terminating() {
180            return Hudson.getInstance().isTerminating();
181        }
182
183        @Override
184        public void run() {
185            while (!terminating()) {
186                try {
187                    getNext()._reload();
188                } catch (InterruptedException e) {
189                    // treat this as a death signal
190                    return;
191                } catch (Throwable t) {
192                    // otherwise ignore any error
193                    t.printStackTrace();
194                }
195            }
196        }
197    }
198
199    // private static final Logger logger = Logger.getLogger(ViewJob.class.getName());
200
201    /**
202     * In the very old version of Hudson, an external job submission was just creating files on the file system,
203     * so we needed to periodically reload the jobs from a file system to pick up new records.
204     *
205     * <p>
206     * We then switched to submission via HTTP, so this reloading is no longer necessary, so only do this
207     * when explicitly requested.
208     * 
209     */
210    public static boolean reloadPeriodically = Boolean.getBoolean(ViewJob.class.getName()+".reloadPeriodically");
211}