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