-
Notifications
You must be signed in to change notification settings - Fork 83
Working Directly With The Native Java Classes
The Ruby Runtime strives to provide a simple, idiomatic Ruby API for extending Jenkins. You should use this method for extensions wherever you can
However, the native Jenkins API is vast, and the idiomatic API only covers a small percentage of it. This situation will surely improve, but in the meantime, you may find yourself wanting to extend Jenkins with an set of extension points that just aren't available to Ruby yet.
In these cases, you can extend the Jenkins Extension point directly and the Ruby runtime will help you do it as easily as possible.
Extensions in Jenkins are divided into two categories: singletons and describables. Singletons are objects instantiated on system startup and generally scoped to the life of the server. Describables on the other hand are many, created in response to some configuration event and are scoped to some other object in the system.
We'll go through implementing these two types of extensions by directly scripting the Java API starting with the singleton.
We'll be working with the Jenkins RunListener interface. This is a wonderful extension point that allows you to receive callbacks at each point during the actual running of a build. There's actually already a nice ruby API for it, but we won't let that stop our demonstrations
We'll start here with the most basic RunListener
class MyListener < Java.hudson.model.listeners.RunListener
def initialize()
super(Java.hudson.model.Run.java_class)
end
end
There's a couple key takeaways here. First, notice that we use
JRuby integration to extend the class
hudson.model.listeners.RunListener
directly. Second, and this is
a gotcha anytime you extend a Java class: you must invoke one of
the Java super constructors if it does not have a default
constructor. I can't tell you how many times I've been bitten by this.
Your constructor must also take no arguments
In our case, the RunListener
class filters which jobs
it will provide callbacks for by class. By providing a more specific
class to the constructor, you limit the scope of jobs you'll receive
to subclasses of that class. For our purposes, we cast a pretty wide
net by selecting all builds via the AbstractBuild
Java class.
Pro Tip: when you're implementing a native Java API, it really helps to have the javadoc open in one window so that you can view the documentation and crib from the source
Now that we've got our class defined, let's implement some methods! We'll add callbacks for when a build is started and when it's completed.
class MyListener < Java.hudson.model.listeners.RunListener
def initialize()
super(Java.hudson.model.AbstractBuild.java_class)
end
def onStarted(run, listener)
listener.getLogger().println("onStarted(#{run})")
end
def onCompleted(run, listener)
listener.getLogger().println("onCompleted(#{run})")
end
end
And finally, we need to tell Jenkins that this is a singleton that it
can pick up and use an an extension point. We do this by including the
Jenkins::Model::SingletonNative
module.
class MyListener < Java.hudson.model.listeners.RunListener
include Jenkins::Model::SingletonNative
def initialize()
super(Java.hudson.model.AbstractBuild.java_class)
end
def onStarted(run, listener)
listener.getLogger().println("onStarted(#{run})")
end
def onCompleted(run, listener)
listener.getLogger().println("onCompleted(#{run})")
end
end
That's it! You've got your native RunListener
Working with Describable extensions is just as easy. Let's make a native HelloWorld builder.
Again, there is a very pleasant Ruby API for Builder, you should always try to see if there is an idiomatic API before you go off using native apis
We'll make it print out "Hello World!" as the implementation of its build step.
class HelloWorldBuilder < Java.hudson.tasks.Builder
include Jenkins::Model::DescribableNative
def perform(build, launcher, listener)
listener.getLogger().println("Hello World!")
return true
end
end
Like the Singleton, we include a module to tell Jenkins that this builder is an
extension, but this time we include Jenkins::Model::DescribableNative
to indicate
that it is describable.
Like other describable extension, its views will be stored in views/hello_world_builder.
You can also give it a display name to show whenever it appears in the UI. Just remember that this is a class method, not an instance method e.g.
class HelloWorldBuilder < Java.hudson.tasks.Builder
include Jenkins::Model::DescribableNative
def self.getDisplayName()
"The Fabulous Hello World Builder"
end
def perform(build, launcher, listener)
listener.getLogger().println("Hello World!")
return true
end
end