{"action":"created","comment":{"url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/comments/38190960","id":38190960,"diff_hunk":"@@ -0,0 +1,147 @@\n+---\n+layout: post\n+title: \"Jenkins Post-build Plugin Part 2\"\n+date: 2015-08-28 14:00\n+comments: true\n+author: Priti Changlani\n+published: true\n+categories:\n+    - jenkins\n+    - plugin\n+---\n+Welcome to part 2 of Jenkins Post-build Plugin development tutorial. This post will talk in detail about the plugin \n+project entities and their interactions with each other. To brush up on the previous post, please visit [Jenkins \n+Post-build Plugin Part 1](https://developer.rackspace.com/blog/jenkins-post-build-plugin-part-1/).\n+\n+<!-- more -->\n+\n+In part 1 of the tutorial, we created a plugin project which generates an example `HelloWorld` builder plugin. We \n+will continue working on the same project and turn it into a post-build plugin. The main difference between the two \n+is best shown as:\n+\n+                            \t| Build Plugin                       \t| Post-build Plugin              \t\n+:------------------------------:|:-------------------------------------:|:---------------------------------:\n+ Builder Class Extends       \t| `hudson.tasks.Builder`            \t| N/A                            \t\n+ Publisher Class Extends    \t| N/A                               \t| `hudson.tasks.Recorder`        \t\n+ Descriptor Class Extends   \t| `BuildStepDescriptor<Builder>`    \t| `BuildStepDescriptor<Publisher>` \t\n+\n+ \n+<img class=\"blog-post\" src=\"{% asset_path 2015-08-28-jenkins-post-build-plugin-part-2/info.png%}\"/>\n+**Tip:** I would highly recommend going through the comments in `HelloWorldBuilder.java` before delving further for \n+those comments are extremely detailed and give the reader a good insight into working of Jenkins core.\n+\n+Before we start, let's rename the `HelloWorldBuilder` class, its references and its `resources` to \n+`TestExamplePublisher`. Next, make the class `TestExamplePublisher` extend `hudson.tasks.Recorder`. This extension \n+also requires overriding of the abstract method `getRequiredMonitorService()`. Make the method return \n+`BuildStepMonitor.NONE`. More can be read on this method [here]\n+(http://javadoc.jenkins-ci.org/hudson/tasks/BuildStep.html#getRequiredMonitorService()).\n+\n+Next, make the inner class `DescriptorImpl` extend `BuildStepDescriptor<Publisher>`. Notice the `@Extension` \n+annotation before this class. This annotation lets Jenkins know that this is a plugin extension of the Jenkins core. \n+The `DescriptorImpl` class acts as a configuration object for the outer plugin class. When the outer class \n+`TestExamplePublisher` calls its `getDescriptor()` method, a `DescriptorImpl` object is returned. \n+\n+1. [Understanding the DescriptorImpl class](#descriptorimpl)\n+1. [Know thy Publisher class](#publisher-class)\n+1. [Build Action Class](#build-action)\n+1. [Project Action Class](#project-action)\n+1. [Conclusion](#conclusion)\n+\n+## <a name=\"descriptorimpl\"></a>Understanding the DescriptorImpl class\n+\n+<img class=\"blog-post right\" src=\"{% asset_path 2015-08-28-jenkins-post-build-plugin-part-2/sayHello2.png%}\"/>\n+\n+The `DescriptorImpl` class implements the abstract method `getDisplayName()` declared in the `Descriptor` class . This \n+method returns an alias-string for the user to click on when he first decides to use this plugin \n+in his Jenkins job. Because this is a post-build \n+plugin, this option is shown when the 'Add post-build action' drop-down is clicked on the job's configuration page.  \n+\n+The naming convention of this class can be confusing for beginners so the image on the right shows where this is \n+displayed.\n+\n+This class is also the place for any global configurations to reside. Global configurations are the ones configured \n+at the plugin level rather than each job/build/project level. You can have parameters of this class to act as \n+globally configurable fields, e.g. checkboxes, text-fields, radio-buttons, etc.\n+ \n+<img class=\"blog-post right\" src=\"{% asset_path 2015-08-28-jenkins-post-build-plugin-part-2/useFrenchCheckBox.png%}\"/>\n+ \n+Look at the boolean `useFrench` class parameter. The method `public boolean configure(...)` in the `Descriptor` class \n+is overridden to set this field to `true` if the user checks in the checkbox on the Manage Jenkins>Configure System \n+page.  The resources to render any such global configurations should be placed in `global.jelly`.\n+\n+The configurations local to each job/build/project are specified as class parameters in the outer plugin class. Look \n+at the field `name` in `TestExamplePublisher`. This field is set with any value that the user fills in the text-field \n+on the job's configuration page. As a good practice, you can restrict the user from entering any values that are \n+clearly invalid. The `doCheckName()` method restricts the user from leaving this field blank. It would \n+allow the user to set a value less than four characters but not without a warning message - \"Isn't the name too \n+short?\" In the exact same way, a developer can write any `doCheck<field-name>()` methods to add checks on the \n+corresponding fields in the class.\n+\n+<img class=\"blog-post right\" src=\"{% asset_path 2015-08-28-jenkins-post-build-plugin-part-2/warning.gif %}\"/>\n+\n+## <a name=\"publisher-class\"></a>Know thy Publisher class\n+\n+The `TestExamplePublisher` class is where the essence of the plugin lies. The parameters of this class are set by \n+the `config.jelly` file. When the user saves any value for these fields on the job's UI (text-fields, checkboxes \n+radio-boxes, etc.), the value is assigned to the corresponding parameters via the `DataBoundConstructor`. It is, \n+therefore, necessary to have the same names for the parameters in the `config.jelly` file as in the \n+`DataBoundConstructor`. To get these parameters, the getter methods are implemented. \n+\n+You need to override the `public boolean perform(...)` method to write your plugin code. This method is the point \n+where the plugin execution begins. As an example, instead of saying hello in the job's console output, let's display \n+the same on the job's build page. In addition, let's display greetings of all the builds within the project on a \n+project page.\n+\n+## <a name=\"build-action\"></a>Build Action Class\n+\n+To make a new page entry in Jenkins side-panel, all one has to do is create a class that implements \n+`hudson.model.Action` interface. Let's create a new `TestExampleBuildAction` class that implements this. To \n+create a new page, one has to provide three entities to Jenkins through this interface. These are a [URL name]\n+(http://javadoc.jenkins-ci.org/hudson/model/Action.html#getUrlName()) to access this page, a [display name]\n+(http://javadoc.jenkins-ci.org/hudson/model/Action.html#getDisplayName()) and an [icon]\n+(http://javadoc.jenkins-ci.org/hudson/model/Action.html#getIconFileName()) for the side-panel entry. Let's have the \n+page located at 'testExampleBuild' (url name), with the display name 'Test Example Build Page' and provide an icon ","path":"_posts/2015-08-28-jenkins-post-build-plugin-part-2.md","position":103,"original_position":103,"commit_id":"4ab8db69ec5fdbc0a3647d39c1be3288628b50f7","original_commit_id":"4ab8db69ec5fdbc0a3647d39c1be3288628b50f7","user":{"login":"catlook","id":5709104,"avatar_url":"https://avatars.githubusercontent.com/u/5709104?v=3","gravatar_id":"","url":"https://api.github.com/users/catlook","html_url":"https://github.com/catlook","followers_url":"https://api.github.com/users/catlook/followers","following_url":"https://api.github.com/users/catlook/following{/other_user}","gists_url":"https://api.github.com/users/catlook/gists{/gist_id}","starred_url":"https://api.github.com/users/catlook/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/catlook/subscriptions","organizations_url":"https://api.github.com/users/catlook/orgs","repos_url":"https://api.github.com/users/catlook/repos","events_url":"https://api.github.com/users/catlook/events{/privacy}","received_events_url":"https://api.github.com/users/catlook/received_events","type":"User","site_admin":false},"body":"... name 'Test Example Build Page', and provide  ...","created_at":"2015-08-28T11:27:24Z","updated_at":"2015-08-28T11:27:24Z","html_url":"https://github.com/rackerlabs/docs-developer-blog/pull/79#discussion_r38190960","pull_request_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79","_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/comments/38190960"},"html":{"href":"https://github.com/rackerlabs/docs-developer-blog/pull/79#discussion_r38190960"},"pull_request":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79"}}},"pull_request":{"url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79","id":43598212,"html_url":"https://github.com/rackerlabs/docs-developer-blog/pull/79","diff_url":"https://github.com/rackerlabs/docs-developer-blog/pull/79.diff","patch_url":"https://github.com/rackerlabs/docs-developer-blog/pull/79.patch","issue_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues/79","number":79,"state":"open","locked":false,"title":"Jenkins postbuild plugin","user":{"login":"pritic","id":9042084,"avatar_url":"https://avatars.githubusercontent.com/u/9042084?v=3","gravatar_id":"","url":"https://api.github.com/users/pritic","html_url":"https://github.com/pritic","followers_url":"https://api.github.com/users/pritic/followers","following_url":"https://api.github.com/users/pritic/following{/other_user}","gists_url":"https://api.github.com/users/pritic/gists{/gist_id}","starred_url":"https://api.github.com/users/pritic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pritic/subscriptions","organizations_url":"https://api.github.com/users/pritic/orgs","repos_url":"https://api.github.com/users/pritic/repos","events_url":"https://api.github.com/users/pritic/events{/privacy}","received_events_url":"https://api.github.com/users/pritic/received_events","type":"User","site_admin":false},"body":"Bio: I am Priti Changlani, a summer intern at Rackspace US, Inc. and a Computer Science graduate student at University of Florida. \r\n\r\nAuthor(s) social media contact info: https://www.linkedin.com/in/pritichanglani\r\n\r\nAccept or decline to allow redaction, editing or structure changes by technical writer staff: Accept\r\n\r\nDesired date of publishing: 08/28/2015","created_at":"2015-08-28T06:46:10Z","updated_at":"2015-08-28T11:27:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"140b95a4b54e10a9f59f94e265850d64f5d332e2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79/commits","review_comments_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79/comments","review_comment_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues/79/comments","statuses_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/statuses/4ab8db69ec5fdbc0a3647d39c1be3288628b50f7","head":{"label":"pritic:jenkins-postbuild-plugin","ref":"jenkins-postbuild-plugin","sha":"4ab8db69ec5fdbc0a3647d39c1be3288628b50f7","user":{"login":"pritic","id":9042084,"avatar_url":"https://avatars.githubusercontent.com/u/9042084?v=3","gravatar_id":"","url":"https://api.github.com/users/pritic","html_url":"https://github.com/pritic","followers_url":"https://api.github.com/users/pritic/followers","following_url":"https://api.github.com/users/pritic/following{/other_user}","gists_url":"https://api.github.com/users/pritic/gists{/gist_id}","starred_url":"https://api.github.com/users/pritic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pritic/subscriptions","organizations_url":"https://api.github.com/users/pritic/orgs","repos_url":"https://api.github.com/users/pritic/repos","events_url":"https://api.github.com/users/pritic/events{/privacy}","received_events_url":"https://api.github.com/users/pritic/received_events","type":"User","site_admin":false},"repo":{"id":40723299,"name":"docs-developer-blog","full_name":"pritic/docs-developer-blog","owner":{"login":"pritic","id":9042084,"avatar_url":"https://avatars.githubusercontent.com/u/9042084?v=3","gravatar_id":"","url":"https://api.github.com/users/pritic","html_url":"https://github.com/pritic","followers_url":"https://api.github.com/users/pritic/followers","following_url":"https://api.github.com/users/pritic/following{/other_user}","gists_url":"https://api.github.com/users/pritic/gists{/gist_id}","starred_url":"https://api.github.com/users/pritic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pritic/subscriptions","organizations_url":"https://api.github.com/users/pritic/orgs","repos_url":"https://api.github.com/users/pritic/repos","events_url":"https://api.github.com/users/pritic/events{/privacy}","received_events_url":"https://api.github.com/users/pritic/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pritic/docs-developer-blog","description":"The developer.rackspace.com blog.","fork":true,"url":"https://api.github.com/repos/pritic/docs-developer-blog","forks_url":"https://api.github.com/repos/pritic/docs-developer-blog/forks","keys_url":"https://api.github.com/repos/pritic/docs-developer-blog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pritic/docs-developer-blog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pritic/docs-developer-blog/teams","hooks_url":"https://api.github.com/repos/pritic/docs-developer-blog/hooks","issue_events_url":"https://api.github.com/repos/pritic/docs-developer-blog/issues/events{/number}","events_url":"https://api.github.com/repos/pritic/docs-developer-blog/events","assignees_url":"https://api.github.com/repos/pritic/docs-developer-blog/assignees{/user}","branches_url":"https://api.github.com/repos/pritic/docs-developer-blog/branches{/branch}","tags_url":"https://api.github.com/repos/pritic/docs-developer-blog/tags","blobs_url":"https://api.github.com/repos/pritic/docs-developer-blog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pritic/docs-developer-blog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pritic/docs-developer-blog/git/refs{/sha}","trees_url":"https://api.github.com/repos/pritic/docs-developer-blog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pritic/docs-developer-blog/statuses/{sha}","languages_url":"https://api.github.com/repos/pritic/docs-developer-blog/languages","stargazers_url":"https://api.github.com/repos/pritic/docs-developer-blog/stargazers","contributors_url":"https://api.github.com/repos/pritic/docs-developer-blog/contributors","subscribers_url":"https://api.github.com/repos/pritic/docs-developer-blog/subscribers","subscription_url":"https://api.github.com/repos/pritic/docs-developer-blog/subscription","commits_url":"https://api.github.com/repos/pritic/docs-developer-blog/commits{/sha}","git_commits_url":"https://api.github.com/repos/pritic/docs-developer-blog/git/commits{/sha}","comments_url":"https://api.github.com/repos/pritic/docs-developer-blog/comments{/number}","issue_comment_url":"https://api.github.com/repos/pritic/docs-developer-blog/issues/comments{/number}","contents_url":"https://api.github.com/repos/pritic/docs-developer-blog/contents/{+path}","compare_url":"https://api.github.com/repos/pritic/docs-developer-blog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pritic/docs-developer-blog/merges","archive_url":"https://api.github.com/repos/pritic/docs-developer-blog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pritic/docs-developer-blog/downloads","issues_url":"https://api.github.com/repos/pritic/docs-developer-blog/issues{/number}","pulls_url":"https://api.github.com/repos/pritic/docs-developer-blog/pulls{/number}","milestones_url":"https://api.github.com/repos/pritic/docs-developer-blog/milestones{/number}","notifications_url":"https://api.github.com/repos/pritic/docs-developer-blog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pritic/docs-developer-blog/labels{/name}","releases_url":"https://api.github.com/repos/pritic/docs-developer-blog/releases{/id}","created_at":"2015-08-14T15:52:16Z","updated_at":"2015-08-14T15:52:18Z","pushed_at":"2015-08-28T06:41:07Z","git_url":"git://github.com/pritic/docs-developer-blog.git","ssh_url":"git@github.com:pritic/docs-developer-blog.git","clone_url":"https://github.com/pritic/docs-developer-blog.git","svn_url":"https://github.com/pritic/docs-developer-blog","homepage":null,"size":59007,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"rackerlabs:master","ref":"master","sha":"af3dcc817096dfb5cf87e94d0a1273761a19ce6b","user":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":36315867,"name":"docs-developer-blog","full_name":"rackerlabs/docs-developer-blog","owner":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rackerlabs/docs-developer-blog","description":"The developer.rackspace.com blog.","fork":false,"url":"https://api.github.com/repos/rackerlabs/docs-developer-blog","forks_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/forks","keys_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/teams","hooks_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/hooks","issue_events_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues/events{/number}","events_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/events","assignees_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/assignees{/user}","branches_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/branches{/branch}","tags_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/tags","blobs_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/git/refs{/sha}","trees_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/statuses/{sha}","languages_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/languages","stargazers_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/stargazers","contributors_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/contributors","subscribers_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/subscribers","subscription_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/subscription","commits_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/commits{/sha}","git_commits_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/git/commits{/sha}","comments_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/comments{/number}","issue_comment_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues/comments{/number}","contents_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/contents/{+path}","compare_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/merges","archive_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/downloads","issues_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues{/number}","pulls_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls{/number}","milestones_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/milestones{/number}","notifications_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/labels{/name}","releases_url":"https://api.github.com/repos/rackerlabs/docs-developer-blog/releases{/id}","created_at":"2015-05-26T18:34:01Z","updated_at":"2015-07-14T20:57:56Z","pushed_at":"2015-08-28T06:46:10Z","git_url":"git://github.com/rackerlabs/docs-developer-blog.git","ssh_url":"git@github.com:rackerlabs/docs-developer-blog.git","clone_url":"https://github.com/rackerlabs/docs-developer-blog.git","svn_url":"https://github.com/rackerlabs/docs-developer-blog","homepage":null,"size":59715,"stargazers_count":6,"watchers_count":6,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":19,"mirror_url":null,"open_issues_count":7,"forks":19,"open_issues":7,"watchers":6,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79"},"html":{"href":"https://github.com/rackerlabs/docs-developer-blog/pull/79"},"issue":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues/79"},"comments":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/issues/79/comments"},"review_comments":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79/comments"},"review_comment":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/pulls/79/commits"},"statuses":{"href":"https://api.github.com/repos/rackerlabs/docs-developer-blog/statuses/4ab8db69ec5fdbc0a3647d39c1be3288628b50f7"}}}}