Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[INFRA-2908] Prepare to publish Stapler via JEP-229 #218

Merged
merged 7 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
_extends: jenkinsci/.github
tag-template: stapler-parent-$NEXT_MINOR_VERSION
59 changes: 59 additions & 0 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins

name: cd
on:
workflow_dispatch:
check_run:
types:
- completed

jobs:
validate:
runs-on: ubuntu-latest
outputs:
should_release: ${{ steps.verify-ci-status.outputs.result == 'success' && steps.interesting-categories.outputs.interesting == 'true' }}
steps:
- name: Verify CI status
uses: jenkins-infra/verify-ci-status-action@v1.2.0
id: verify-ci-status
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
output_result: true

- name: Release Drafter
uses: release-drafter/release-drafter@v5
if: steps.verify-ci-status.outputs.result == 'success'
with:
name: next
tag: next
version: next
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Check interesting categories
uses: jenkins-infra/interesting-category-action@v1.0.0
id: interesting-categories
if: steps.verify-ci-status.outputs.result == 'success'
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release:
runs-on: ubuntu-latest
needs: [validate]
if: needs.validate.outputs.should_release == 'true'
steps:
- name: Check out
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 8
- name: Release
uses: jenkins-infra/jenkins-maven-cd-action@v1.1.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}
2 changes: 1 addition & 1 deletion .mvn/extensions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
<extension>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>git-changelist-maven-extension</artifactId>
<version>1.0-beta-7</version>
<version>1.2</version>
</extension>
</extensions>
1 change: 1 addition & 0 deletions .mvn/maven.config
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
-Pconsume-incrementals
-Pmight-produce-incrementals
-Dchangelist.format=%d.v%s
7 changes: 1 addition & 6 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ node('maven') {
checkout scm
}
stage('Build / Test') {
sh '''
mvn -ntp -Dset.changelist -Dmaven.test.failure.ignore install
# Without -Dset.changelist (see 8edc206):
mvn -ntp -DskipTests install
mvn -ntp -DskipTests site
'''
sh 'mvn -ntp -Dset.changelist -Dmaven.test.failure.ignore install'
junit '**/target/surefire-reports/TEST-*.xml'
infra.prepareToPublishIncrementals()
}
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
* [What is Stapler?](docs/what-is.adoc)
* [Getting Started](docs/getting-started.adoc)
* Developer Reference
* [URL binding reference](docs/reference.adoc)
* [HTTP compression](docs/compression.adoc)
* [Internationalization support](docs/i18n.adoc)
* [Javadoc](https://javadoc.jenkins.io/component/stapler/)
* [Stapler jelly tag library reference and XML Schema](docs/jelly-taglib-ref.adoc)
3 changes: 2 additions & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-parent</artifactId>
<version>${revision}${changelist}</version>
<version>${changelist}</version>
</parent>

<artifactId>stapler</artifactId>
Expand Down Expand Up @@ -119,6 +119,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
Expand Down
39 changes: 39 additions & 0 deletions docs/compression.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
== Static resources are sent with gzip

When clients request static resources, stapler will send it with gzip as
the content encoding whenever it can, to minimize the size of the data
to be transferred. This only happens when the MIME type of the resource
is "text/*", or the file extension is one of the well-known text file
type.

This happens all transparently, so there's nothing you nor the browser
have to do.

== Sending compressed stream from the `doXXX` methods

If you are writing action methods that serve text content, and if you'd
like to compress the stream, you can call
`StaplerResponse.getCompressedOutputStream` (instead of
`StaplerResponse.getOutputStream`) or
`StaplerResponse.getCompressedWriter` (instead of
`StaplerResponse.getWriter`) and then write to them normally.

If the client doesn't support HTTP compression, these methods will
silently revert to the normal uncompressed streams.

== Using compression from Jelly view

If your view script produces text content, and if you'd like to compress
the stream, put `<st:compress xmlns:st="jelly:stapler">` as the root
element of your view, like this:

[source,xml]
----
<st:compress xmlns:st="jelly:stapler" ... other ns decls ...>
<html>
<head>
...
----

If the client doesn't support HTTP compression, stapler will silently
revert to the normal uncompressed stream.
219 changes: 219 additions & 0 deletions docs/getting-started.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
== Getting Started

Distribution packages of Stapler comes with a sample application that
illustrates how to use Stapler. This document explains the basic usage
of Stapler by using this sample application.

=== Setting up your web app

First, put `stapler.jar` into your application's `WEB-INF/lib`
directory. Then put the following XML into your `WEB-INF/web.xml` so
that the stapler will be recognized by the container.

[source,xml]
----
<servlet>
<servlet-name>Stapler</servlet-name>
<servlet-class>org.kohsuke.stapler.Stapler</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Stapler</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
----

=== Registering the root object

Stapler needs to know the root object of your application. It does that
by `ServletContext.getAttribute("app")`, so your application needs to
set the root object into a ServletContext. The easiest to do that is to
write a
http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/ServletContextListener.html[`ServletContextListener`]
and use the helper method from `Stapler`.

Specifically, you write the following class:

[source,java]
----
package example;
import org.kohsuke.stapler.Stapler;

public class WebAppMain implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// BookStore.theStore is the singleton instance of the application
Stapler.setRoot(event,BookStore.theStore);
}

public void contextDestroyed(ServletContextEvent event) {
}
}
----

You also have to put the following XML in `WEB-INF/web.xml` so that the
container recognizes this class.

[source,xml]
----
<listener>
<listener-class>example.WebAppMain</listener-class>
</listener>
----

In this way, the `WebAppMain.contextInitialized` method is executed as
soon as the web app starts, and you get a chance to initialize your web
application.

=== Root BookStore class

In this tutorial, we are going to write a very simple book store that
sells books and CDs.

We first introduce the root `BookStore` class. Because this web
application is about a book store, an object that represents the whole
book store would be suitable as the root object.

By setting an object of `BookStore` as the root object, the root URL '/'
and its immediate subordinates will be served by this object. First, to
serve the root URL '/' by using JSP, let's put `index.jsp` into
`WEB-INF/side-files/example/BookStore/index.jsp`. These JSP files are
called side files. They are somewhat like methods, in the sense that
they are used to display objects of a particular class. Side files are
organized according to the class they are associated with.

Here is a part of this index.jsp:

[source,xml]
----
<html>...<body>
<%-- side files can include static resources. --%>
<img src="logo.png">

<h2>Inventory</h2>
<c:forEach var="i" items="${it.items}">
<a href="items/${i.key}">${i.value.title}</a><br>
</c:forEach>
...
----

Side files can contain resources other than JSPs. In the example,
`logo.png` is also placed as a side file for `BookStore`, allowing
`index.jsp` to refer to `logo.png` by using this simple relative URL.

Side JSP files can access their target object by the implicit "it"
variable. For example, the EL expression `${it.items}` refers to the
`getItems` method defined on the `BookStore` class. The "it" variable
makes it easy for side JSPs to access the target object.

==== More on side files

`index.jsp` is a special JSP used to serve the '.../' address, but you
can have more JSP files. For example, you can write `count.jsp` and
refer to it by '.../count'. Note that it doesn't have the .jsp extension
--- this is necessary to work around the short-coming of the servlet
specification.

=== Delegating to descendants

Stapler uses public get methods of application objects reflexively to
bind reachable child objects to URLs.

In our example, a BookStore has a map from the stock keeping unit (SKU)
number to the corresponding Item object. The corresponding Java class
looks like this:

[source,java]
----
public class BookStore {
public Map/*<String,Item>*/ getItems() {
return items;
}
...
}
----

When a client requests an URL "/items/b1", Stapler takes the following
steps to decide how to serve this request.

. Stapler knows '/' is mapped to the singleton instance of the
`BookStore` class.
. Stapler takes the next portion 'items' and notice that the `BookStore`
class has a method `getItems`, so Stapler evaluates
`x=bookStore.getItems()`.
. Stapler takes the next portion 'b1' from the URL and notice that `x`
is a `Map`, so Stapler evaluates `y=x.get("b1")`.
. Finally, because the whole URL is processed, Stapler looks for the
`index.jsp` for `y`.

In the example, `bookStore.getItems().get("b1")` returns a `Book`
object, so the side file `/WEB-INF/side-files/example/Book/index.jsp` is
used to serve this request, with the "it" object being set to this
`Book` instance.

As you see, in this way, you can define the URL hierarchy just by
defining get methods.

=== Polymorphism

When you got two classes `Book` and `CD` that both derives from `Item`,
you often want to have some commonality between the ways those two
classes are served. There are ways to do this.

When a side file is searched for a particular object, all of its
implementation hierarchy are checked. Fore example, if the "it" object
is an instance of the `Book` class and Stapler is looking for
`footer.jsp`, then first it looks for
`/WEB-INF/side-files/example/Book/footer.jsp`, then
`/WEB-INF/side-files/example/Item/footer.jsp`.

In a sense, it is as if the side files of the derived class would
override those of the base class.

You can also use this semantics when you include one JSP from another
JSP. `Book/index.jsp` and `CD/index.jsp` uses this mechanism to refer to
the common footer defined in `Item/footer.jsp`.

=== Action methods

Sometimes you want to perform some operations when a particular URL is
requested. Normally, you'd use servlets to do so, but with Stapler, you
can define a whole servlet as a method on your application object. We
call these methods "action methods".

Action methods take the following signature:

[source,java]
----
public void do[Name]( StaplerRequest request, StaplerResponse response ) {
...
}
----

Action methods can throw any exception. Unlike servlets where you are
discouraged to use instance variables, action methods are invoked on the
"it" object, allowing you to access the "it" object quickly.

In our example, we define an action method called "hello" in
`BookStore`. To invoke this action method, access `/hello`. Again, just
like servlets, you can serve the request from this action method
(perhaps by sending the contents out or redirecting clients) in exactly
the same way as you'd do in servlets. In the following example, we use a
method on `StaplerResponse` to forward to another side JSP file to
generate the response.

[source,java]
----
public void doHello( StaplerRequest request, StaplerResponse response ) {
...
response.forward(this,"helloJSP",request);
}
----

=== Conclusion

Hopefully, the basic concept of Stapler is easy to grasp. For the exact
rules of how URLs are evaluated against your application objects, see
link:reference.html[the reference guide].

If you have any suggestions on how to improve this documentation, please
mailto:kk@kohsuke.org[let me know].
Loading