PageRenderTime 47ms CodeModel.GetById 20ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 1ms

/hudson-core/src/main/java/hudson/Plugin.java

http://github.com/hudson/hudson
Java | 256 lines | 57 code | 20 blank | 179 comment | 5 complexity | ad283cbc4131e1bcd4da9e5ebdba4c3b 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;
 25
 26import hudson.model.Hudson;
 27import hudson.model.Descriptor;
 28import hudson.model.Saveable;
 29import hudson.model.listeners.ItemListener;
 30import hudson.model.listeners.SaveableListener;
 31import hudson.model.Descriptor.FormException;
 32import org.kohsuke.stapler.StaplerRequest;
 33import org.kohsuke.stapler.StaplerResponse;
 34
 35import javax.servlet.ServletContext;
 36import javax.servlet.ServletException;
 37import javax.servlet.http.HttpServletResponse;
 38import java.io.IOException;
 39import java.io.File;
 40import java.net.URL;
 41
 42import net.sf.json.JSONObject;
 43import com.thoughtworks.xstream.XStream;
 44
 45/**
 46 * Base class of Hudson plugin.
 47 *
 48 * <p>
 49 * A plugin needs to derive from this class.
 50 *
 51 * <p>
 52 * One instance of a plugin is created by Hudson, and used as the entry point
 53 * to plugin functionality.
 54 *
 55 * <p>
 56 * A plugin is bound to URL space of Hudson as <tt>${rootURL}/plugin/foo/</tt>,
 57 * where "foo" is taken from your plugin name "foo.hpi". All your web resources
 58 * in src/main/webapp are visible from this URL, and you can also define Jelly
 59 * views against your Plugin class, and those are visible in this URL, too.
 60 *
 61 * <p>
 62 * {@link Plugin} can have an optional <tt>config.jelly</tt> page. If present,
 63 * it will become a part of the system configuration page (http://server/hudson/configure).
 64 * This is convenient for exposing/maintaining configuration that doesn't
 65 * fit any {@link Descriptor}s.
 66 *
 67 * <p>
 68 * Up until Hudson 1.150 or something, subclasses of {@link Plugin} required
 69 * <tt>@plugin</tt> javadoc annotation, but that is no longer a requirement.
 70 *
 71 * @author Kohsuke Kawaguchi
 72 * @since 1.42
 73 */
 74public abstract class Plugin implements Saveable {
 75
 76    /**
 77     * Set by the {@link PluginManager}.
 78     * This points to the {@link PluginWrapper} that wraps
 79     * this {@link Plugin} object.
 80     */
 81    /*package*/ transient PluginWrapper wrapper;
 82
 83    /**
 84     * Called when a plugin is loaded to make the {@link ServletContext} object available to a plugin.
 85     * This object allows plugins to talk to the surrounding environment.
 86     *
 87     * <p>
 88     * The default implementation is no-op.
 89     *
 90     * @param context
 91     *      Always non-null.
 92     *
 93     * @since 1.42
 94     */
 95    public void setServletContext(ServletContext context) {
 96    }
 97
 98    /**
 99     * Called to allow plugins to initialize themselves.
100     *
101     * <p>
102     * This method is called after {@link #setServletContext(ServletContext)} is invoked.
103     * You can also use {@link Hudson#getInstance()} to access the singleton hudson instance,
104     * although the plugin start up happens relatively early in the initialization
105     * stage and not all the data are loaded in Hudson.
106     *
107     * <p>
108     * If a plugin wants to run an initialization step after all plugins and extension points
109     * are registered, a good place to do that is {@link #postInitialize()}.
110     * If a plugin wants to run an initialization step after all the jobs are loaded,
111     * {@link ItemListener#onLoaded()} is a good place.
112     *
113     * @throws Exception
114     *      any exception thrown by the plugin during the initialization will disable plugin.
115     *
116     * @since 1.42
117     * @see ExtensionPoint
118     * @see #postInitialize()
119     */
120    public void start() throws Exception {
121    }
122
123    /**
124     * Called after {@link #start()} is called for all the plugins.
125     *
126     * @throws Exception
127     *      any exception thrown by the plugin during the initialization will disable plugin.
128     */
129    public void postInitialize() throws Exception {}
130
131    /**
132     * Called to orderly shut down Hudson.
133     *
134     * <p>
135     * This is a good opportunity to clean up resources that plugin started.
136     * This method will not be invoked if the {@link #start()} failed abnormally.
137     *
138     * @throws Exception
139     *      if any exception is thrown, it is simply recorded and shut-down of other
140     *      plugins continue. This is primarily just a convenience feature, so that
141     *      each plugin author doesn't have to worry about catching an exception and
142     *      recording it.
143     *
144     * @since 1.42
145     */
146    public void stop() throws Exception {
147    }
148
149    /**
150     * @since 1.233
151     * @deprecated as of 1.305 override {@link #configure(StaplerRequest,JSONObject)} instead
152     */
153    public void configure(JSONObject formData) throws IOException, ServletException, FormException {
154    }
155
156    /**
157     * Handles the submission for the system configuration.
158     *
159     * <p>
160     * If this class defines <tt>config.jelly</tt> view, be sure to
161     * override this method and persists the submitted values accordingly.
162     *
163     * <p>
164     * The following is a sample <tt>config.jelly</tt> that you can start yours with:
165     * <pre><xmp>
166     * <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">
167     *   <f:section title="Locale">
168     *     <f:entry title="${%Default Language}" help="/plugin/locale/help/default-language.html">
169     *       <f:textbox name="systemLocale" value="${it.systemLocale}" />
170     *     </f:entry>
171     *   </f:section>
172     * </j:jelly>
173     * </xmp></pre>
174     *
175     * <p>
176     * This allows you to access data as {@code formData.getString("systemLocale")}
177     *
178     * <p>
179     * If you are using this method, you'll likely be interested in
180     * using {@link #save()} and {@link #load()}.
181     * @since 1.305
182     */
183    public void configure(StaplerRequest req, JSONObject formData) throws IOException, ServletException, FormException {
184        configure(formData);
185    }
186
187    /**
188     * This method serves static resources in the plugin under <tt>hudson/plugin/SHORTNAME</tt>.
189     */
190    public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
191        String path = req.getRestOfPath();
192
193        if(path.length()==0)
194            path = "/";
195
196        if(path.indexOf("..")!=-1 || path.length()<1) {
197            // don't serve anything other than files in the sub directory.
198            rsp.sendError(HttpServletResponse.SC_BAD_REQUEST);
199            return;
200        }
201
202        // use serveLocalizedFile to support automatic locale selection
203        rsp.serveLocalizedFile(req, new URL(wrapper.baseResourceURL,'.'+path));
204    }
205
206//
207// Convenience methods for those plugins that persist configuration
208//
209    /**
210     * Loads serializable fields of this instance from the persisted storage.
211     *
212     * <p>
213     * If there was no previously persisted state, this method is no-op.
214     *
215     * @since 1.245
216     */
217    protected void load() throws IOException {
218        XmlFile xml = getConfigXml();
219        if(xml.exists())
220            xml.unmarshal(this);
221    }
222
223    /**
224     * Saves serializable fields of this instance to the persisted storage.
225     *
226     * @since 1.245
227     */
228    public void save() throws IOException {
229        if(BulkChange.contains(this))   return;
230        getConfigXml().write(this);
231        SaveableListener.fireOnChange(this, getConfigXml());
232    }
233
234    /**
235     * Controls the file where {@link #load()} and {@link #save()}
236     * persists data.
237     *
238     * This method can be also overriden if the plugin wants to
239     * use a custom {@link XStream} instance to persist data.
240     *
241     * @since 1.245
242     */
243    protected XmlFile getConfigXml() {
244        return new XmlFile(Hudson.XSTREAM,
245                new File(Hudson.getInstance().getRootDir(),wrapper.getShortName()+".xml"));
246    }
247
248
249    /**
250     * Dummy instance of {@link Plugin} to be used when a plugin didn't
251     * supply one on its own.
252     *
253     * @since 1.321
254     */
255    public static final class DummyImpl extends Plugin {}
256}