Hudson Dev Workshop - PowerPoint PPT Presentation

gomer
kohsuke kawaguchi kohsuke kawaguchi@cloudbees com cloudbees inc n.
Skip this Video
Loading SlideShow in 5 Seconds..
Hudson Dev Workshop PowerPoint Presentation
Download Presentation
Hudson Dev Workshop

play fullscreen
1 / 77
Download Presentation
Hudson Dev Workshop
169 Views
Download Presentation

Hudson Dev Workshop

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

  1. Kohsuke Kawaguchi kohsuke.kawaguchi@cloudbees.com CloudBees, Inc. Hudson Dev Workshop

  2. Why a Hudson plugin? • 300+ people have done it. Can’t be that hard • Integrate Hudson with your favorite tools, systems, and etc. • Make Hudson speak in domain specific terms • Instead of batch files or shell scripts

  3. Pre-requisite • Maven 2 • Terminal and/or command prompt • IDE of your choice • The following entry in your .m2/settings.xml • Windows user will find this in %USERPROFILE%\.m2\settings.xml <settings> <pluginGroups> <pluginGroup>org.jvnet.hudson.tools</pluginGroup> </pluginGroups> </settings>

  4. Create a skeleton • CUI wizard to create a skeleton • This will download a lot from the internet if this is the first time • Download seed.zip and unzip it in ~/.m2/repository for faster bootstrap • From my USB key $ mvn -cpuhpi:create

  5. And while we wait for Maven to download the internet…

  6. Hudson Plugin Community • https://github.com/hudson/ • Developer resources • IRC channel: #hudson • Mailing list: hudson-dev@googlegroups.com • Wiki: http://wiki.hudson-ci.org/display/HUDSON/Extend+Hudson

  7. Build your Hudson plugin • Update Hudson version to 1.387 • In reference to parent POM • Hudson plugin follows a Maven standard • And while you wait for Maven to build… $ mvn package $ ls target/*.hpi

  8. Let’s look at source files • POM • Source files • Jelly files • Help HTML files

  9. Debug a Hudson plugin • Start your plugin in an embedded Hudson • If port 8080 is occupied, use –Dport=12345 • Point your browser to http://localhost:8080/ $ mvnhpi:run

  10. Play with the hello world builder • Configure a new job • Add a “Say hello world” build step • Run the build and see hello world • Where is HUDSON_HOME? • Hint: system configuration

  11. Interactive Development • Edit help HTML file, save • Just reload the browser and see the change • Edit Jelly files, save • Just reload the browser and see the change

  12. Interactive Development • For Eclipse • Edit source code, save • Wait for Hudson to auto reload • For other IDEs • Edit source code, compile • Wait for Hudson to auto reload

  13. Sometimes auto-reload is annoying • Most often useful with “mvnDebug” • So that you can reload classes from debugger • Add –DscanIntervalSeconds=0 to Maven

  14. When your plugin is ready… • “Manage Hudson” > “Manage Plugins” > “Advancced”

  15. Let’s look at the source codemore carefully

  16. Key ingredient check list • @DataBoundConstructor • @Extension • DescriptorImpl • Package structure for Jelly files • Help files by convention

  17. Basic pattern of plugins • Pick an extension point • http://wiki.hudson-ci.org/display/HUDSON/Extension+points • Implement it • Create a descriptor with @Extension • Or in some cases there’s no descriptor and the implementation class itself gets @Extension

  18. UI / Data binding

  19. Exercise: add a new config field • Add another text field to accept another name, then say hello to both • Add a help file for this new text field

  20. UI Samples • Unfortunately, much of config.jelly editing is monkey-see-monkey-do • UI samples = our attempt to fix this

  21. Exercise: add a new config field • Add auto-completion to the name field • Note the method name has to match with @field • Extra Credits • Try other UI samples and see if you can copy them

  22. Form Validation • Write doCheckXyz method for xyz field • “value” parameter to receive the current value • Can also retrieve nearby sibling values • Return/throw FormValidation instance for status public FormValidationdoCheckPort(@QueryParameter String value) { if(looksOk(value)) return FormValidation.ok(); else return FormValidation.error("There's a problem here"); }

  23. Different datatypes available • URL • int • boolean • Enum • hudson.util.Secret • Persisted and sent to browser in encrypted form • Can be extended by adding converter

  24. Exercise: play with Secret • Add another configuration field whose type is hudson.util.Secret • Inspect the persisted config.xml • ./work/jobs/JOBNAME/config.xml • Inspect the value set in the config page • Trace the execution in the debugger

  25. Playing with Launcher • Abstraction for forking processes • Works even when a build is on a slave • Fluent API • launcher.launch().doThis().doThat().start() • or join()

  26. Playing with BuildListener • Used to write to build console • getLogger() : PrintStream • Used to report error • e.printStackTrace(listener.error(“Failed to do X”));

  27. Exercise: Launcher&BuildListener • Tweak HelloWorldBuilder to fork a process and send its output to build console • E.g., “uname –a” or “cmd.exe /?”

  28. FilePath: working with files • java.io.File on steroid • Represents a file or a directory • Lots of convenient file operations • Works transparently on files on slaves

  29. Exercise: FilePath • Tweak HelloWorldBuilder to play with FilePath • Use AbstractBuild.getWorkspace() • Create several text files and create a zip file from them • Compute md5 checksums of all the files • Hint: getDigest()

  30. Descriptor : Describable • Descriptor : Describable = Class : Object • Describable • Created from UI through Descriptor • Live as long as the configuration remains the same • Descriptor • Singletons • Capture behaviors and operations that are “static” • Global configuration, form validation • … is a static nested type of Describable by convention

  31. @Extension • Enumerated during compile time to generate index • Sometimes “mvn compile” is needed for this • Used at runtime to find extensions

  32. View Technology

  33. Side track: RootAction • Useful for experimenting with views • Extension point to add a menu item to the top page • Descriptor-less, because it’s not configured from anywhere

  34. Apache Commons Jelly • XML based template engine • Think of it as JSPX+JSTL+customtaglibs • Used to render HTML • And other XMLs, such as RSS, JNLP, … • Most people start by monkey-see-monkey-do • References • http://commons.apache.org/jelly/tags.html • http://hudson-ci.org/maven-site/hudson-core/jelly-taglib-ref.html

  35. Apache Commons JEXL • EL on steroid • ${foo.bar[5]+”abc”} • Allow method calls • XML friendly operators • “and”/”or”,”lt”,”ge” etc in addition to &&, ||, <, >= • a?b:c and a?:b (=a?a:b)

  36. Object-oriented views • Views are like methods • Co-located to classes via naming convention • foo.jelly for org.acme.Bar should be in org/acme/Bar/foo.jelly • Views are inherited to subtypes • In JEXL, “it” variable refers to the “this” object • “index.jelly” plays the special role

  37. Exercise: Jelly, JEXL, and RootAction • Create a new root action “MyRootAction” • See javadoc for what your methods need to return • Add index.jelly • Start from this: • Play with Jelly and JEXL • Add methods to your class and invoke it from view • Use <j:forEach> to say hello 10 times • Otherwise be creative! <j:jelly xmlns:j=“jelly:core”> <html><body><h1> Hello from ${it.getClass()} </h1></body></html> </j:jelly>

  38. Object-oriented Views • In Hudson, URLs are mapped to object graph • And this determines how request is eventually handled • IOW, controller layer is implicit • More precisely, this is in Stapler • See HTTP headers for evaluation trace • So if you add “bar.jelly” to MyRootAction,

  39. Exercise: Object-oriented views • Add “bar.jelly” to MyRootAction • Modify index.jelly to add a hyperlink to the new page • RefactorMyRootAction and introduce a base type • Push down bar.jelly to the new base class. See how the UI behaves

  40. URL → Object graph mapping

  41. URL → Object graph mapping • Typical traversal methods • getFoo() → …/foo/ • getFoo(“bar”) → …/foo/bar/ • getDynamic(“foo”) → …/foo/… • Exact rules are in https://stapler.dev.java.net/reference.html

  42. Exercise: URL → Object graph • Define immutable Employee class • And populate several of them from MyRootAction • Define index.jelly on Employee and create navigable UI public class Employee { public int number; public String name; public Employee boss; public List<Employee> reports; … } public class MyRootAction { List<Employee> employees; }

  43. Programmatic request handling • Instead of “foo.jelly”, define “doFoo” method • Parameters are injected • StaplerRequest (<: HttpServletRequest) • StaplerResponse (<: HttpServletResponse) • @QueryParameter • @AncestorInPath • Return value / exception becomes HTTP response (or void) • Or void and control it yourself • org.kohsuke.stapler.HttpResponse

  44. Demo • Employee.doTerminate()

  45. Exercise: doXyz method • Define a form that takes one text field and update the employee name

  46. Persistence

  47. XStream • This is how Hudson persists most data • Characteristics • Clean almost human-readable XML • No mapping required / semi-transparent to code • Supports arbitrary object graph • Customizable enough • Robust in the face of partial unmarshalling failure

  48. XStream: naïve example public class Employee { public int number; public String name; } <org.acme.Employee> <number>1</number> <name>Kohskue</name> </org.acme.Employee>

  49. Persistence and extension points • If/how your extensions are persisted is described in javadoc • E.g., Builder is persisted as a part of AbstractProject • SecurityRealm is persisted as a part of Hudson • etc.

  50. XmlFile • Used if you need to persist things on your own • Represents a XML file that contains persisted objects • Important methods • write(Object) • Object read() • void unmarshal(Object)