From 4568d16268a59c92be1d899c9e1457c54c2fe312 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 May 2021 17:21:01 -0400 Subject: [PATCH 1/6] [INFRA-2908] Prepare to publish Stapler via JEP-229 --- .github/release-drafter.yml | 1 - .github/workflows/cd.yaml | 59 +++ .mvn/extensions.xml | 2 +- .mvn/maven.config | 1 + Jenkinsfile | 7 +- README.md | 9 + core/pom.xml | 3 +- {src/site/resources => docs}/compression.html | 0 .../resources => docs}/getting-started.html | 0 {src/site/resources => docs}/i18n.html | 0 .../resources => docs}/jelly-taglib-ref.html | 0 {src/site/resources => docs}/reference.html | 0 {src/site/resources => docs}/stapler.css | 0 {src/site/resources => docs}/stapler.png | Bin {src/site/resources => docs}/stapler.vsd | Bin {src/site/resources => docs}/taglib-jelly.xsd | 0 {src/site/resources => docs}/taglib.xsd | 0 {src/site/resources => docs}/what-is.html | 0 groovy/pom.xml | 3 +- .../groovy/GroovyClassLoaderTearOff.java | 2 + jelly/pom.xml | 3 +- jrebel/pom.xml | 2 +- jruby/pom.xml | 3 +- jsp/pom.xml | 2 +- pom.xml | 391 ++---------------- src/site/markdown/index.md | 10 - src/site/site.xml | 20 - 27 files changed, 106 insertions(+), 412 deletions(-) create mode 100644 .github/workflows/cd.yaml create mode 100644 README.md rename {src/site/resources => docs}/compression.html (100%) rename {src/site/resources => docs}/getting-started.html (100%) rename {src/site/resources => docs}/i18n.html (100%) rename {src/site/resources => docs}/jelly-taglib-ref.html (100%) rename {src/site/resources => docs}/reference.html (100%) rename {src/site/resources => docs}/stapler.css (100%) rename {src/site/resources => docs}/stapler.png (100%) rename {src/site/resources => docs}/stapler.vsd (100%) rename {src/site/resources => docs}/taglib-jelly.xsd (100%) rename {src/site/resources => docs}/taglib.xsd (100%) rename {src/site/resources => docs}/what-is.html (100%) delete mode 100644 src/site/markdown/index.md delete mode 100644 src/site/site.xml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1cf7c58c56..bcae69878e 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,2 +1 @@ _extends: jenkinsci/.github -tag-template: stapler-parent-$NEXT_MINOR_VERSION diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 0000000000..d1dafdd095 --- /dev/null +++ b/.github/workflows/cd.yaml @@ -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 }} diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 94863e605b..43d6281619 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -2,6 +2,6 @@ io.jenkins.tools.incrementals git-changelist-maven-extension - 1.0-beta-7 + 1.2 diff --git a/.mvn/maven.config b/.mvn/maven.config index 2a0299c486..f7daf60d07 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1,2 +1,3 @@ -Pconsume-incrementals -Pmight-produce-incrementals +-Dchangelist.format=%d.v%s diff --git a/Jenkinsfile b/Jenkinsfile index 91c0e7b81b..09bd2e153d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -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() } diff --git a/README.md b/README.md new file mode 100644 index 0000000000..fa25d748cb --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ + +* [What is Stapler?](docs/what-is.html) +* [Getting Started](docs/getting-started.html) +* Developer Reference + * [URL binding reference](docs/reference.html) + * [HTTP compression](docs/compression.html) + * [Internationalization support](docs/i18n.html) + * [Javadoc](https://javadoc.jenkins.io/component/stapler/) + * [Stapler jelly tag library reference and XML Schema](docs/jelly-taglib-ref.html) diff --git a/core/pom.xml b/core/pom.xml index f5d23e5a01..23412334c6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ org.kohsuke.stapler stapler-parent - ${revision}${changelist} + ${changelist} stapler @@ -119,6 +119,7 @@ junit junit + test org.mockito diff --git a/src/site/resources/compression.html b/docs/compression.html similarity index 100% rename from src/site/resources/compression.html rename to docs/compression.html diff --git a/src/site/resources/getting-started.html b/docs/getting-started.html similarity index 100% rename from src/site/resources/getting-started.html rename to docs/getting-started.html diff --git a/src/site/resources/i18n.html b/docs/i18n.html similarity index 100% rename from src/site/resources/i18n.html rename to docs/i18n.html diff --git a/src/site/resources/jelly-taglib-ref.html b/docs/jelly-taglib-ref.html similarity index 100% rename from src/site/resources/jelly-taglib-ref.html rename to docs/jelly-taglib-ref.html diff --git a/src/site/resources/reference.html b/docs/reference.html similarity index 100% rename from src/site/resources/reference.html rename to docs/reference.html diff --git a/src/site/resources/stapler.css b/docs/stapler.css similarity index 100% rename from src/site/resources/stapler.css rename to docs/stapler.css diff --git a/src/site/resources/stapler.png b/docs/stapler.png similarity index 100% rename from src/site/resources/stapler.png rename to docs/stapler.png diff --git a/src/site/resources/stapler.vsd b/docs/stapler.vsd similarity index 100% rename from src/site/resources/stapler.vsd rename to docs/stapler.vsd diff --git a/src/site/resources/taglib-jelly.xsd b/docs/taglib-jelly.xsd similarity index 100% rename from src/site/resources/taglib-jelly.xsd rename to docs/taglib-jelly.xsd diff --git a/src/site/resources/taglib.xsd b/docs/taglib.xsd similarity index 100% rename from src/site/resources/taglib.xsd rename to docs/taglib.xsd diff --git a/src/site/resources/what-is.html b/docs/what-is.html similarity index 100% rename from src/site/resources/what-is.html rename to docs/what-is.html diff --git a/groovy/pom.xml b/groovy/pom.xml index df30de83b3..df56a8d0f3 100644 --- a/groovy/pom.xml +++ b/groovy/pom.xml @@ -3,7 +3,7 @@ org.kohsuke.stapler stapler-parent - ${revision}${changelist} + ${changelist} stapler-groovy @@ -38,6 +38,7 @@ junit junit + test org.kohsuke.metainf-services diff --git a/groovy/src/main/java/org/kohsuke/stapler/jelly/groovy/GroovyClassLoaderTearOff.java b/groovy/src/main/java/org/kohsuke/stapler/jelly/groovy/GroovyClassLoaderTearOff.java index 11eb2dd908..4639d4be94 100644 --- a/groovy/src/main/java/org/kohsuke/stapler/jelly/groovy/GroovyClassLoaderTearOff.java +++ b/groovy/src/main/java/org/kohsuke/stapler/jelly/groovy/GroovyClassLoaderTearOff.java @@ -23,6 +23,7 @@ package org.kohsuke.stapler.jelly.groovy; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyCodeSource; import org.apache.commons.jelly.XMLOutput; @@ -36,6 +37,7 @@ /** * @author Kohsuke Kawaguchi */ +@SuppressFBWarnings(value = "GROOVY_SHELL", justification = "from trusted sources") public class GroovyClassLoaderTearOff { private final MetaClassLoader owner; diff --git a/jelly/pom.xml b/jelly/pom.xml index 1fad9e7df6..acae5662bc 100644 --- a/jelly/pom.xml +++ b/jelly/pom.xml @@ -3,7 +3,7 @@ org.kohsuke.stapler stapler-parent - ${revision}${changelist} + ${changelist} stapler-jelly @@ -61,6 +61,7 @@ junit junit + test org.jvnet.hudson diff --git a/jrebel/pom.xml b/jrebel/pom.xml index 5fa5408314..0d19822554 100644 --- a/jrebel/pom.xml +++ b/jrebel/pom.xml @@ -3,7 +3,7 @@ org.kohsuke.stapler stapler-parent - ${revision}${changelist} + ${changelist} stapler-jrebel diff --git a/jruby/pom.xml b/jruby/pom.xml index 8309e3bea6..7e75bdc0e8 100644 --- a/jruby/pom.xml +++ b/jruby/pom.xml @@ -3,7 +3,7 @@ org.kohsuke.stapler stapler-parent - ${revision}${changelist} + ${changelist} stapler-jruby @@ -72,6 +72,7 @@ junit junit + test org.kohsuke.metainf-services diff --git a/jsp/pom.xml b/jsp/pom.xml index 5ce1d053c5..8fe039dba5 100644 --- a/jsp/pom.xml +++ b/jsp/pom.xml @@ -3,7 +3,7 @@ org.kohsuke.stapler stapler-parent - ${revision}${changelist} + ${changelist} stapler-jsp diff --git a/pom.xml b/pom.xml index 0b3182e3b6..170626e3f7 100644 --- a/pom.xml +++ b/pom.xml @@ -2,15 +2,15 @@ 4.0.0 - org.kohsuke - pom - 21 + org.jenkins-ci + jenkins + 1.63 org.kohsuke.stapler stapler-parent pom - ${revision}${changelist} + ${changelist} Stapler Stapler HTTP request handling engine @@ -24,7 +24,7 @@ jrebel - https://stapler.kohsuke.org/ + https://github.com/stapler/stapler @@ -41,29 +41,6 @@ ${scmTag} - - - maven.jenkins-ci.org - https://repo.jenkins-ci.org/releases/ - - - maven.jenkins-ci.org - https://repo.jenkins-ci.org/snapshots/ - - - github-pages - gitsite:git@github.com/stapler/stapler.git - - - - - - Kohsuke Kawaguchi - kohsuke - kk@kohsuke.org - - - repo.jenkins-ci.org @@ -79,218 +56,43 @@ - UTF-8 - 8 - 1.264 - -SNAPSHOT - https://repo.jenkins-ci.org/incrementals/ + UTF-8 + 999999-SNAPSHOT HEAD - - 3.1.12 - - - true - - Medium - - default - ${project.basedir}/../src/spotbugs/spotbugs-excludes.xml - - - - maven-compiler-plugin - 3.1 - - 1.${java.level} - 1.${java.level} - - - - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.15 - - - - check - - - - - - org.codehaus.mojo.signature - java1${java.level} - 1.0 - - - - java.lang.invoke.MethodHandle - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M1 - - - - enforce - - - - - [1.${java.level},) - - - 1.${java.level} - - - com.headius.invokebinder.* - - - - - - - - - - org.codehaus.mojo - extra-enforcer-rules - 1.0-beta-4 - - - - - maven-release-plugin - - - clean install - true - - - - org.apache.maven.plugins - maven-site-plugin - - - org.kohsuke - doxia-module-markdown - 1.0 - - - - - org.jvnet.maven-jellydoc-plugin - maven-jellydoc-plugin - 1.2 - - - com.github.spotbugs - spotbugs-maven-plugin - - ${spotbugs.failOnError} - true - false - ${spotbugs.effort} - ${spotbugs.threshold} - - ${spotbugs.excludeFilterFile} - - - com.h3xstream.findsecbugs - findsecbugs-plugin - 1.10.1 - - - - - - spotbugs - - check - - verify - - - - - maven-deploy-plugin - 2.8.2 - - - maven-jar-plugin - 2.5 - - - maven-source-plugin - 2.4 - - - maven-javadoc-plugin - 2.10.1 - - - maven-surefire-plugin - 2.18.1 + org.apache.maven.plugins + maven-enforcer-plugin - false - - -Djdk.net.URLClassPath.disableClassPathURLCheck=true + + + + + com.headius.invokebinder.* + + + - io.jenkins.tools.incrementals - incrementals-maven-plugin - 1.0-beta-7 + org.codehaus.mojo + animal-sniffer-maven-plugin - false - false + + + java.lang.invoke.MethodHandle + - - com.github.spotbugs - spotbugs-maven-plugin - ${spotbugs-maven-plugin.version} - - - - - maven-project-info-reports-plugin - 2.7 - - - - license - - - - - - maven-javadoc-plugin - 2.9.1 - - true - true - - - - - - @@ -325,159 +127,12 @@ - - junit - junit - 4.13.1 - test - commons-collections commons-collections 3.2.2 - - com.github.spotbugs - spotbugs-annotations - 3.1.12 - - - - - consume-incrementals - - - incrementals - ${incrementals.url} - - false - - - - - - incrementals - ${incrementals.url} - - false - - - - - - might-produce-incrementals - - - - org.codehaus.mojo - flatten-maven-plugin - 1.0.1 - - true - - - - flatten - process-resources - - flatten - - - oss - ${project.build.directory} - ${project.artifactId}-${project.version}.pom - - - - - - maven-enforcer-plugin - - - display-info - - - - [3.5.0,) - 3.5.0+ required to use Incrementals. - - - [1.0-beta-4,) - - - - - - - - io.jenkins.tools.incrementals - incrementals-enforcer-rules - 1.0-beta-5 - - - - - maven-release-plugin - - incrementals:reincrementalify - - - - - - - produce-incrementals - - - set.changelist - true - - - - - incrementals - ${incrementals.url} - - - - - - - maven-source-plugin - - - attach-sources - - jar-no-fork - - - - attach-test-sources - - test-jar-no-fork - - - ${no-test-jar} - - - - - - maven-javadoc-plugin - - - attach-javadocs - - jar - - - - - - - - diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md deleted file mode 100644 index b9d8a91e1b..0000000000 --- a/src/site/markdown/index.md +++ /dev/null @@ -1,10 +0,0 @@ - -* [What is Stapler?](what-is.html) -* [Download](http://repo.jenkins-ci.org/public/org/kohsuke/stapler/) -* [Getting Started](getting-started.html) -* Developer Reference - * [URL binding reference](reference.html) - * [HTTP compression](compression.html) - * [Internationalization support](i18n.html) - * [Javadoc](apidocs/) - * [Stapler jelly tag library reference and XML Schema](jelly-taglib-ref.html) diff --git a/src/site/site.xml b/src/site/site.xml deleted file mode 100644 index fb8f8b08a1..0000000000 --- a/src/site/site.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - org.kohsuke - maven-skin - 1.0 - - - - - - - - - - - - - ${reports} - - From ff4f5b4916fbfc8017d1b38298b103a19ed0a987 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 May 2021 17:29:31 -0400 Subject: [PATCH 2/6] pandoc --- README.md | 13 +- docs/compression.adoc | 38 ++++ docs/compression.html | 34 --- docs/getting-started.adoc | 216 ++++++++++++++++++ docs/getting-started.html | 172 --------------- docs/i18n.adoc | 29 +++ docs/i18n.html | 34 --- docs/jelly-taglib-ref.adoc | 380 ++++++++++++++++++++++++++++++++ docs/jelly-taglib-ref.html | 439 ------------------------------------- docs/reference.adoc | 300 +++++++++++++++++++++++++ docs/reference.html | 263 ---------------------- docs/stapler.css | 27 --- docs/what-is.adoc | 88 ++++++++ docs/what-is.html | 59 ----- 14 files changed, 1057 insertions(+), 1035 deletions(-) create mode 100644 docs/compression.adoc delete mode 100644 docs/compression.html create mode 100644 docs/getting-started.adoc delete mode 100644 docs/getting-started.html create mode 100644 docs/i18n.adoc delete mode 100644 docs/i18n.html create mode 100644 docs/jelly-taglib-ref.adoc delete mode 100644 docs/jelly-taglib-ref.html create mode 100644 docs/reference.adoc delete mode 100644 docs/reference.html delete mode 100644 docs/stapler.css create mode 100644 docs/what-is.adoc delete mode 100644 docs/what-is.html diff --git a/README.md b/README.md index fa25d748cb..5410cd49af 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ - -* [What is Stapler?](docs/what-is.html) -* [Getting Started](docs/getting-started.html) +* [What is Stapler?](docs/what-is.adoc) +* [Getting Started](docs/getting-started.adoc) * Developer Reference - * [URL binding reference](docs/reference.html) - * [HTTP compression](docs/compression.html) - * [Internationalization support](docs/i18n.html) + * [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.html) + * [Stapler jelly tag library reference and XML Schema](docs/jelly-taglib-ref.adoc) diff --git a/docs/compression.adoc b/docs/compression.adoc new file mode 100644 index 0000000000..4d72f2ef08 --- /dev/null +++ b/docs/compression.adoc @@ -0,0 +1,38 @@ +== 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 `` as the root +element of your view, like this: + +[source,code] +---- + + + ... +---- + +If the client doesn't support HTTP compression, stapler will silently +revert to the normal uncompressed stream. diff --git a/docs/compression.html b/docs/compression.html deleted file mode 100644 index 985a8da773..0000000000 --- a/docs/compression.html +++ /dev/null @@ -1,34 +0,0 @@ - - - HTTP compression support - - - -

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: -


-<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. - - - diff --git a/docs/getting-started.adoc b/docs/getting-started.adoc new file mode 100644 index 0000000000..c8f0f41178 --- /dev/null +++ b/docs/getting-started.adoc @@ -0,0 +1,216 @@ +== 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,code] +---- + Stapler + org.kohsuke.stapler.Stapler + + + + Stapler + / +---- + +=== 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,code] +---- +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,code] +---- + example.WebAppMain +---- + +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,code] +---- +... + <%-- side files can include static resources. --%> + + + Inventory + + ${i.value.title} + + + ... +---- + +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,code] +---- +public class BookStore { + public Map/**/ 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,code] +---- +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,code] +---- +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]. diff --git a/docs/getting-started.html b/docs/getting-started.html deleted file mode 100644 index 2258924836..0000000000 --- a/docs/getting-started.html +++ /dev/null @@ -1,172 +0,0 @@ - - - Getting Started - - - - - - -

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. -

<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 ServletContextListener and use the helper method from Stapler. -

- Specifically, you write the following class: -

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. -

<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: -

<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: -

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. -

    -
  1. Stapler knows '/' is mapped to the singleton instance of the BookStore class. -
  2. Stapler takes the next portion 'items' and notice that the BookStore class has a method getItems, so Stapler evaluates x=bookStore.getItems(). -
  3. Stapler takes the next portion 'b1' from the URL and notice that x is a Map, so Stapler evaluates y=x.get("b1"). -
  4. 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: -

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. -

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 the reference guide. -

- If you have any suggestions on how to improve this documentation, please let me know. - - \ No newline at end of file diff --git a/docs/i18n.adoc b/docs/i18n.adoc new file mode 100644 index 0000000000..7143362c16 --- /dev/null +++ b/docs/i18n.adoc @@ -0,0 +1,29 @@ +== i18n support + +Stapler depends on https://localizer.dev.java.net/[localizer] for the +i18n support. + +=== i18n from Jelly view scripts + +For Jelly view scripts, the convention is to place localized message +resource files by using the same base name. For example, if you are +writing `foo.jelly`, its property files will be placed in files like +`foo.properties` and `foo_ja.properties`. + +In this set up, Jelly view scripts can refer to message resources by +using a special expression of the form `${%KEY(arg1,arg2,...)}`. The +messages are formatted by using `MessageFormat`, and arguments are +individually parsed as JEXL scripts. If there's no arguments, +parenthesis can be omitted and thus it can be simply written as +`${%KEY}`. + +If you need to refer to message resources from within a JEXL expression, +you can use a string literal notation `"%KEY(arg1,arg2,...)"` or its +argument-less short form `"%KEY"`. For example, +`${obj.methodCall(1,true,"%Message")}` + +If the specified KEY is not available in the property files, the key +itself will be used as a formatting string. The key can contain any +character except '(' and '}'. This convenient defaulting allows you to +skip writing `foo.properties` most of the time (that is, you'd only need +property files for translations.) diff --git a/docs/i18n.html b/docs/i18n.html deleted file mode 100644 index d08fdf95da..0000000000 --- a/docs/i18n.html +++ /dev/null @@ -1,34 +0,0 @@ - - - i18n support - - - -

i18n support

-

- Stapler depends on localizer for the i18n support. - -

i18n from Jelly view scripts

-

- For Jelly view scripts, the convention is to place localized message resource files by using - the same base name. For example, if you are writing foo.jelly, its property files will - be placed in files like foo.properties and foo_ja.properties. -

- In this set up, Jelly view scripts can refer to message resources by using a special expression of the form - ${%KEY(arg1,arg2,...)}. The messages are formatted by using MessageFormat, and - arguments are individually parsed as JEXL scripts. If there's no arguments, parenthesis can be - omitted and thus it can be simply written as ${%KEY}. - -

- If you need to refer to message resources from within a JEXL expression, you can use a string literal - notation "%KEY(arg1,arg2,...)" or its argument-less short form "%KEY". For example, - ${obj.methodCall(1,true,"%Message")} - -

- If the specified KEY is not available in the property files, - the key itself will be used as a formatting string. The key can contain any character except '(' and - '}'. This convenient defaulting allows you to - skip writing foo.properties most of the time (that is, you'd only need property files for translations.) - - - diff --git a/docs/jelly-taglib-ref.adoc b/docs/jelly-taglib-ref.adoc new file mode 100644 index 0000000000..818998d0c0 --- /dev/null +++ b/docs/jelly-taglib-ref.adoc @@ -0,0 +1,380 @@ +The following Jelly tag libraries are defined in this project. + +[#jelly]## + +== jelly:stapler + +Optional Jelly support, to write views in Jelly. + +This tag library is link:taglib-jelly.xsd[also available as an XML +Schema] + +[width="100%",cols="50%,50%",options="header",] +|=== +|Tag Name |Description +|#jelly:statusCode[statusCode] a| +Sets HTTP status code. + +This is generally useful for programatically creating the error page. + +|#jelly:redirect[redirect] |Sends HTTP redirect. + +|#jelly:parentScope[parentScope] |Executes the body in the parent scope. +This is useful for creating a 'local' scope. + +|#jelly:out[out] |Tag that outputs the specified value but with +escaping, so that you can escape a portion even if the +org.apache.commons.jelly.XMLOutput is not escaping. + +|#jelly:once[once] |Tag that only evaluates its body once during the +entire request processing. + +|#jelly:nbsp[nbsp] |Writes out ' '. + +|#jelly:isUserInRole[isUserInRole] | + +|#jelly:include[include] |Tag that includes views of the object. + +|#jelly:header[header] |Adds an HTTP header to the response. + +|#jelly:findAncestor[findAncestor] |Finds the nearest tag (in the call +stack) that has the given tag name, and sets that as a variable. + +|#jelly:documentation[documentation] a| +Documentation for a Jelly tag file. + +This tag should be placed right inside the root element once, to +describe the tag and its attributes. Maven-stapler-plugin picks up this +tag and generate schemas and documentations. + +The description text inside this tag can also use Textile markup + +|#jelly:doctype[doctype] |Writes out DOCTYPE declaration. + +|#jelly:customTagLibrary.StaplerDynamic[customTagLibrary.StaplerDynamic] +a| +When is used to call back into the calling script, the +Jelly name resolution rule is in such that the body is evaluated with +the variable scope of the caller. This is very different +from a typical closure name resolution mechanism, where the body is +evaluated with the variable scope of where the body was created. + +More concretely, in Jelly, this often shows up as a problem as inability +to access the "attrs" variable from inside a body, because every + +org.apache.commons.jelly.impl.DynamicTag invocation sets this variable +in a new scope. + +To counter this effect, this class temporarily restores the original +"attrs" when the body is evaluated. This makes the name resolution of +'attrs' work like what programmers normally expect. + +The same problem also shows up as a lack of local variables ? when a tag +calls into the body via , the invoked body will see all +the variables that are defined in the caller, which is again not what a +normal programming language does. But unfortunately, changing this is +too pervasive. + +|#jelly:copyStream[copyStream] |Copies a stream as text. + +|#jelly:contentType[contentType] |Set the HTTP Content-Type header of +the page. + +|#jelly:compress[compress] |Outer-most wrapper tag to indicate that the +gzip compression is desirable for this output. + +|#jelly:attribute[attribute] a| +Documentation for an attribute of a Jelly tag file. + +This tag should be placed right inside + +org.kohsuke.stapler.jelly.DocumentationTag to describe attributes of a +tag. The body would describe the meaning of an attribute in a natural +language. The description text can also use Textile markup + +|#jelly:adjunct[adjunct] |Writes out links to adjunct CSS and +JavaScript, if not done so already. +|=== + +[#jelly:statusCode]## + +=== statusCode + +Sets HTTP status code. + +This is generally useful for programatically creating the error page. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|value (required) |int |HTTP status code to send back. +|=== + +This tag does not accept any child elements/text. + +[#jelly:redirect]## + +=== redirect + +Sends HTTP redirect. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|url (required) |String |Sets the target URL to redirect to. This just +gets passed to +org.kohsuke.stapler.StaplerResponse.sendRedirect2(String). +|=== + +This tag does not accept any child elements/text. + +[#jelly:parentScope]## + +=== parentScope + +Executes the body in the parent scope. This is useful for creating a +'local' scope. + +[#jelly:out]## + +=== out + +Tag that outputs the specified value but with escaping, so that you can +escape a portion even if the org.apache.commons.jelly.XMLOutput is not +escaping. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|value (required) |Expression | +|=== + +This tag does not accept any child elements/text. + +[#jelly:once]## + +=== once + +Tag that only evaluates its body once during the entire request +processing. + +[#jelly:nbsp]## + +=== nbsp + +Writes out ' '. + +This tag does not accept any child elements/text. + +[#jelly:isUserInRole]## + +=== isUserInRole + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|role (required) |String |The name of the role against which the user is +checked. +|=== + +[#jelly:include]## + +=== include + +Tag that includes views of the object. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|page (required) |String |Specifies the name of the JSP to be included. + +|it |Object |Specifies the object for which JSP will be included. +Defaults to the "it" object in the current context. + +|from |Object |When loading the script, use the classloader from this +object to locate the script. Otherwise defaults to "it" object. + +|class |java.lang.Class |When loading script, load from this class. By +default this is "from.getClass()". This takes precedence over the +org.kohsuke.stapler.jelly.IncludeTag.setFrom(Object) method. + +|optional |boolean |If true, not finding the page is not an error. +|=== + +This tag does not accept any child elements/text. + +[#jelly:header]## + +=== header + +Adds an HTTP header to the response. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|name (required) |String |Header name. +|value (required) |String |Header value. +|=== + +This tag does not accept any child elements/text. + +[#jelly:findAncestor]## + +=== findAncestor + +Finds the nearest tag (in the call stack) that has the given tag name, +and sets that as a variable. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|var |String |Variable name to set the discovered +org.apache.commons.jelly.Tag object. + +|tag |String |QName of the tag to look for. + +|namespaceContext |java.util.Map | +|=== + +This tag does not accept any child elements/text. + +[#jelly:documentation]## + +=== documentation + +Documentation for a Jelly tag file. + +This tag should be placed right inside the root element once, to +describe the tag and its attributes. Maven-stapler-plugin picks up this +tag and generate schemas and documentations. + +The description text inside this tag can also use Textile markup + +[#jelly:doctype]## + +=== doctype + +Writes out DOCTYPE declaration. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|publicId (required) |String | +|systemId (required) |String | +|=== + +This tag does not accept any child elements/text. + +[#jelly:customTagLibrary.StaplerDynamic]## + +=== customTagLibrary.StaplerDynamic + +When is used to call back into the calling script, the +Jelly name resolution rule is in such that the body is evaluated with +the variable scope of the caller. This is very different +from a typical closure name resolution mechanism, where the body is +evaluated with the variable scope of where the body was created. + +More concretely, in Jelly, this often shows up as a problem as inability +to access the "attrs" variable from inside a body, because every + +org.apache.commons.jelly.impl.DynamicTag invocation sets this variable +in a new scope. + +To counter this effect, this class temporarily restores the original +"attrs" when the body is evaluated. This makes the name resolution of +'attrs' work like what programmers normally expect. + +The same problem also shows up as a lack of local variables ? when a tag +calls into the body via , the invoked body will see all +the variables that are defined in the caller, which is again not what a +normal programming language does. But unfortunately, changing this is +too pervasive. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|template |Script | +|=== + +[#jelly:copyStream]## + +=== copyStream + +Copies a stream as text. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|reader |Reader | +|inputStream |InputStream | +|file |File | +|url |URL | +|=== + +This tag does not accept any child elements/text. + +[#jelly:contentType]## + +=== contentType + +Set the HTTP Content-Type header of the page. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|value (required) |String |The content-type value, such as "text/html". +|=== + +This tag does not accept any child elements/text. + +[#jelly:compress]## + +=== compress + +Outer-most wrapper tag to indicate that the gzip compression is +desirable for this output. + +[#jelly:attribute]## + +=== attribute + +Documentation for an attribute of a Jelly tag file. + +This tag should be placed right inside + +org.kohsuke.stapler.jelly.DocumentationTag to describe attributes of a +tag. The body would describe the meaning of an attribute in a natural +language. The description text can also use Textile markup + +[width="100%",cols="34%,33%,33%",options="header",] +|=== +|Attribute Name |Type |Description +|name (required) |String |Name of the attribute. + +|use |String a| +If the attribute is required, specify use="required". (This is modeled +after XML Schema attribute declaration.) + +By default, use="optional" is assumed. + +|type |String |If it makes sense, describe the Java type that the +attribute expects as values. + +|deprecated |boolean |If the attribute is deprecated, set to true. Use +of the deprecated attribute will cause a warning. +|=== + +[#jelly:adjunct]## + +=== adjunct + +Writes out links to adjunct CSS and JavaScript, if not done so already. + +[cols=",,",options="header",] +|=== +|Attribute Name |Type |Description +|includes (required) |String |Comma-separated adjunct names. +|=== + +This tag does not accept any child elements/text. diff --git a/docs/jelly-taglib-ref.html b/docs/jelly-taglib-ref.html deleted file mode 100644 index bf2d649275..0000000000 --- a/docs/jelly-taglib-ref.html +++ /dev/null @@ -1,439 +0,0 @@ - - - Stapler - Jelly Taglib references - - - -

The following Jelly tag libraries are defined in this project.

- -

jelly:stapler

-Optional Jelly support, to write views in Jelly.

This tag library is also available as an XML Schema

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Tag NameDescription
statusCodeSets HTTP status code. - - -

- This is generally useful for programatically creating the error page.

-
redirectSends HTTP redirect.
parentScopeExecutes the body in the parent scope. - This is useful for creating a 'local' scope.
outTag that outputs the specified value but with escaping, - so that you can escape a portion even if the - org.apache.commons.jelly.XMLOutput is not escaping.
onceTag that only evaluates its body once during the entire request processing.
nbspWrites out '&nbsp;'.
isUserInRole
includeTag that includes views of the object.
headerAdds an HTTP header to the response.
findAncestorFinds the nearest tag (in the call stack) that has the given tag name, - and sets that as a variable.
documentationDocumentation for a Jelly tag file. - - -

- This tag should be placed right inside the root element once, - to describe the tag and its attributes. Maven-stapler-plugin - picks up this tag and generate schemas and documentations. - -

-

- The description text inside this tag can also use - - Textile markup -

-
doctypeWrites out DOCTYPE declaration.
customTagLibrary.StaplerDynamicWhen <d:invokeBody/> is used to call back into the calling script, - the Jelly name resolution rule is in such that the body is evaluated with - the variable scope of the <d:invokeBody/> caller. This is very different - from a typical closure name resolution mechanism, where the body is evaluated - with the variable scope of where the body was created. - - -

- More concretely, in Jelly, this often shows up as a problem as inability to - access the "attrs" variable from inside a body, because every

org.apache.commons.jelly.impl.DynamicTag - invocation sets this variable in a new scope. - - -

- To counter this effect, this class temporarily restores the original "attrs" - when the body is evaluated. This makes the name resolution of 'attrs' work - like what programmers normally expect. - -

-

- The same problem also shows up as a lack of local variables ? when a tag - calls into the body via <d:invokeBody/>, the invoked body will see all the - variables that are defined in the caller, which is again not what a normal programming language - does. But unfortunately, changing this is too pervasive.

-
copyStreamCopies a stream as text.
contentTypeSet the HTTP Content-Type header of the page.
compressOuter-most wrapper tag to indicate that the gzip compression is desirable - for this output.
attributeDocumentation for an attribute of a Jelly tag file. - - -

- This tag should be placed right inside

org.kohsuke.stapler.jelly.DocumentationTag - to describe attributes of a tag. The body would describe - the meaning of an attribute in a natural language. - The description text can also use - - Textile markup -
adjunctWrites out links to adjunct CSS and JavaScript, if not done so already.
- -

statusCode

-Sets HTTP status code. - - -

- This is generally useful for programatically creating the error page.

-
- - - - - - - -
Attribute NameTypeDescription
value (required)intHTTP status code to send back.
-

This tag does not accept any child elements/text.

-
- -

redirect

-Sends HTTP redirect. - - - - - - - -
Attribute NameTypeDescription
url (required)StringSets the target URL to redirect to. This just gets passed - to org.kohsuke.stapler.StaplerResponse.sendRedirect2(String).
-

This tag does not accept any child elements/text.

-
- -

parentScope

-Executes the body in the parent scope. - This is useful for creating a 'local' scope.
- -

out

-Tag that outputs the specified value but with escaping, - so that you can escape a portion even if the - org.apache.commons.jelly.XMLOutput is not escaping. - - - - - - - -
Attribute NameTypeDescription
value (required)Expression
-

This tag does not accept any child elements/text.

-
- -

once

-Tag that only evaluates its body once during the entire request processing.
- -

nbsp

-Writes out '&nbsp;'.

This tag does not accept any child elements/text.

-
- -

isUserInRole

- - - - - - - - -
Attribute NameTypeDescription
role (required)StringThe name of the role against which the user is checked.
-
- -

include

-Tag that includes views of the object. - - - - - - - - - - - - - - - - - - - - - - - -
Attribute NameTypeDescription
page (required)StringSpecifies the name of the JSP to be included.
itObjectSpecifies the object for which JSP will be included. - Defaults to the "it" object in the current context.
fromObjectWhen loading the script, use the classloader from this object - to locate the script. Otherwise defaults to "it" object.
classjava.lang.ClassWhen loading script, load from this class. - - By default this is "from.getClass()". This takes - precedence over the org.kohsuke.stapler.jelly.IncludeTag.setFrom(Object) method.
optionalbooleanIf true, not finding the page is not an error.
-

This tag does not accept any child elements/text.

-
- -

header

-Adds an HTTP header to the response. - - - - - - - - - - - -
Attribute NameTypeDescription
name (required)StringHeader name.
value (required)StringHeader value.
-

This tag does not accept any child elements/text.

-
- -

findAncestor

-Finds the nearest tag (in the call stack) that has the given tag name, - and sets that as a variable. - - - - - - - - - - - - - - - -
Attribute NameTypeDescription
varStringVariable name to set the discovered org.apache.commons.jelly.Tag object.
tagStringQName of the tag to look for.
namespaceContextjava.util.Map
-

This tag does not accept any child elements/text.

-
- -

documentation

-Documentation for a Jelly tag file. - - -

- This tag should be placed right inside the root element once, - to describe the tag and its attributes. Maven-stapler-plugin - picks up this tag and generate schemas and documentations. - -

-

- The description text inside this tag can also use - - Textile markup -

-
- -

doctype

-Writes out DOCTYPE declaration. - - - - - - - - - - - -
Attribute NameTypeDescription
publicId (required)String
systemId (required)String
-

This tag does not accept any child elements/text.

-
- -

customTagLibrary.StaplerDynamic

-When <d:invokeBody/> is used to call back into the calling script, - the Jelly name resolution rule is in such that the body is evaluated with - the variable scope of the <d:invokeBody/> caller. This is very different - from a typical closure name resolution mechanism, where the body is evaluated - with the variable scope of where the body was created. - - -

- More concretely, in Jelly, this often shows up as a problem as inability to - access the "attrs" variable from inside a body, because every

org.apache.commons.jelly.impl.DynamicTag - invocation sets this variable in a new scope. - - -

- To counter this effect, this class temporarily restores the original "attrs" - when the body is evaluated. This makes the name resolution of 'attrs' work - like what programmers normally expect. - -

-

- The same problem also shows up as a lack of local variables ? when a tag - calls into the body via <d:invokeBody/>, the invoked body will see all the - variables that are defined in the caller, which is again not what a normal programming language - does. But unfortunately, changing this is too pervasive.

-
- - - - - - - -
Attribute NameTypeDescription
templateScript
-
- -

copyStream

-Copies a stream as text. - - - - - - - - - - - - - - - - - - - -
Attribute NameTypeDescription
readerReader
inputStreamInputStream
fileFile
urlURL
-

This tag does not accept any child elements/text.

-
- -

contentType

-Set the HTTP Content-Type header of the page. - - - - - - - -
Attribute NameTypeDescription
value (required)StringThe content-type value, such as "text/html".
-

This tag does not accept any child elements/text.

-
- -

compress

-Outer-most wrapper tag to indicate that the gzip compression is desirable - for this output.
- -

attribute

-Documentation for an attribute of a Jelly tag file. - - -

- This tag should be placed right inside

org.kohsuke.stapler.jelly.DocumentationTag - to describe attributes of a tag. The body would describe - the meaning of an attribute in a natural language. - The description text can also use - - Textile markup -
- - - - - - - - - - - - - - - - - - - -
Attribute NameTypeDescription
name (required)StringName of the attribute.
useStringIf the attribute is required, specify use="required". - (This is modeled after XML Schema attribute declaration.) - - -

- By default, use="optional" is assumed.

-
typeStringIf it makes sense, describe the Java type that the attribute - expects as values.
deprecatedbooleanIf the attribute is deprecated, set to true. - Use of the deprecated attribute will cause a warning.
-
- -

adjunct

-Writes out links to adjunct CSS and JavaScript, if not done so already. - - - - - - - -
Attribute NameTypeDescription
includes (required)StringComma-separated adjunct names.
-

This tag does not accept any child elements/text.

-
-
- - diff --git a/docs/reference.adoc b/docs/reference.adoc new file mode 100644 index 0000000000..e815ff3d7c --- /dev/null +++ b/docs/reference.adoc @@ -0,0 +1,300 @@ +== Reference + +This document explains the details of how Stapler "staples" your objects +to URLs. + +The way Stapler works is some what like +http://www-106.ibm.com/developerworks/java/library/j-jstl0211.html[Expression +Language]; *it takes an object and URL, then evaluate the URL against +the object. It repeats this process until it hits either a static +resource, a view (such as JSP, Jelly, Groovy, etc.), or an action +method.* + +This process can be best understood as a recursively defined +mathematical function `evaluate(node,url)`. For example, +link:stapler.png[the hypothetical application] depicted in +link:what-is.html[the "getting started" document] could have an +evaluation process like the following: + +____ +.... +Scenario: browser sends "POST /project/jaxb/docsAndFiles/upload HTTP/1.1" + + evaluate(, "/project/jaxb/docsAndFiles/upload") +-> evaluate(.getProject("jaxb"), "/docsAndFiles/upload") +-> evaluate(, "/docsAndFiles/upload") +-> evaluate(.getDocsAndFiles(), "/upload") +-> evaluate(, "/upload") +-> .doUpload(...) +.... +____ + +The exact manner of recursion depends on the signature of the type of +the `node` parameter, and the following sections describe them in +detail. Also see +http://hudson.gotdns.com/wiki/display/HUDSON/Figuring+out+URL+binding+of+Stapler[a +document in Hudson] that explains how you can see the actual evaluation +process unfold in your own application by monitoring HTTP headers. + +=== Evaluation of URL: Reference + +This section defines the `evaluate(node,url)` function. Possible +branches are listed in the order of preference, so when the given `node` +and `url` matches to multiple branches, earlier ones take precedence. + +____ +==== Notation + +We often use the notation `url[0], url[1], ...` to indicate the tokens +of `url` separated by '/', and `url.size` to denote the number of such +tokens. For example, if `url="/abc/def/"`, then +`url[0]="abc", url[1]="def", url.size=2`. Similarly if `url="xyz"`, then +`url[0]="xyz", url.size=1` + +List notation `[a,b,c,...]` is also used to describe `url`. Lower-case +variables represent a single token, while upper-case variables represent +variable-length tokens. For example, if we say `url=[a,W]` and the +actual URL was "/abc/def/ghi", then `a="abc"` and `W="/def/ghi"`. If the +actual URL was "/abc", then `a="abc"` and `W=""`. +____ + +==== Stapler Proxy + +`node` can implement +https://stapler.dev.java.net/nonav/javadoc/index.html?org/kohsuke/stapler/StaplerProxy.html[the +`StaplerProxy` interface] to delegate the UI processing to another +object. There's also a provision for selectively doing this, so that +`node` can intelligently decide if it wants to delegate to another +object. + +Formally, + +____ +.... +evaluate(node,url) := evaluate(target,url) — if target instanceof StaplerProxy, target=node.getTarget(), and target!=null +.... +____ + +==== Index View + +If there's no remaining URL, then a welcome page for the current object +is served. A welcome page is a side-file of the `node` named `index.*` +(such as `index.jsp`, `index.jelly`, etc. + +Formally, + +____ +.... +evaluate(node,[]) := renderView(node,"index") +.... +____ + +==== Action Method + +If `url` is of the form "/fooBar/...." and `node` has a public "action" +method named `doFooBar(...)`, then this method is invoked. + +The action method is the final consumer of the request. This is is +convenient for form submission handling, and/or implementing URLs that +have side effects. Formally, + +____ +.... +evaluate(node,[x,W]) := node.doX(...) +.... +____ + +Stapler performs link:parameter-injection.html[parameter injections] on +calling action methods. + +==== View + +If the remaining URL is "/xxxx/...." and a side file of the node named +`xxxx` exists, then this view gets executed. Views normally have their +view-specific extensions, like `xxxx.jelly` or `xxxx.groovy`. + +Formally, + +____ +.... +evaluate(node,[x,W]) := renderView(node,x) +.... +____ + +==== Index Action Method + +This is a slight variation of above. If there's no remaining URL and +there's an action method called "doIndex", this method will be invoked. +Formally, + +____ +.... +evaluate(node,[]) := node.doIndex(...) +.... +____ + +==== Public Field + +If `url` is "/fooBar/..." and `node` has a public field named "fooBar", +then the object stored in `node.fooBar` will be evaluated against the +rest of the URL. Formally, + +____ +.... +evaluate(node,[x,W]) := evaluate(node.x,W) +.... +____ + +==== Public Getter Method + +If `url` is "/fooBar/..." and `node` has a public getter method named +"getFooBar()", then the object returned from `node.getFooBar()` will be +evaluated against the rest of the URL. + +Stapler also looks for the public getter of the form +"getXxxx(StaplerRequest)". If such a method exists, then this getter +method is invoked in a similar way. This version allows the get method +to take sophisticated action based on the current request (such as +returning the object specific to the current user, or returning null if +the user is not authenticated.) + +Formally, + +____ +.... +evaluate(node,[x,W]) := evaluate(node.getX(...),W) +.... +____ + +==== Public Getter Method with a String Argument + +If `url` is "/xxxx/yyyy/..." and `node` has a public method named +"getXxxx(String arg)", then the object returned from +`currentObject.getXxxx("yyyy")` will be evaluated against the rest of +the URL "/...." recursively. + +Formally, + +____ +.... +evaluate(node,[x,y,W]) := evaluate(node.getX(y),W) +.... +____ + +==== Public Getter Method with an int Argument + +Really the same as above, except it takes `int` instead of `String`. + +____ +.... +evaluate(node,[x,y,W]) := evaluate(node.getX(y),W) — if y is an integer +.... +____ + +==== Array + +If `node` is an array and `url` is "/nnnn/...." where nnnn is a number, +then the object returned from `node[nnnn]` will be evaluated against the +rest of the URL "/...." recursively. + +Formally, + +____ +.... +evaluate(node,[x,W]) := evaluate(node[x],W) — if node instanceof Object[] +.... +____ + +==== List + +If `node` implements `java.util.List` and the URL is "/nnnn/...." where +nnnn is a number, then the object returned from `node.get(nnnn)` will be +evaluated against the rest of the URL "/...." recursively. + +Formally, + +____ +.... +evaluate(node,[x,W]) := evaluate(node.get(x),W) — if node instanceof List +.... +____ + +==== Map + +If `node` implements `java.util.Map` and the URL is "/xxxx/....", then +the object returned from `node.get("xxxx")` will be evaluated against +the rest of the URL "/...." recursively. + +____ +.... +evaluate(node,[x,W]) := evaluate(node.get(x),W) — if node instanceof Map +.... +____ + +==== Dynamic Getter Method + +If the current object has a public method +`getDynamic(String,StaplerRequest,StaplerResponse)`, and the URL is +"/xxxx/..." and then this method is invoked with "xxxx" as the first +parameter. The object returned from this method will be evaluated +against the rest of the URL "/...." recursively. + +This is convenient for a reason similar to above, except that this +doesn't terminate the URL mapping. + +Formally, + +____ +.... +evaluate(node,[x,W]) := evaluate(node.getDynamic(x,request,response),W) +.... +____ + +==== Dynamic Action Method + +If the current object has a public "action" method +`doDynamic(StaplerRequest,StaplerResponse)`, then this method is +invoked. From within this method, the rest of the URL can be accessed by +`StaplerRequest.getRestOfPath()`. This is convenient for an object that +wants to control the URL mapping entirely on its own. + +The action method is the final consumer of the request. + +Formally, + +____ +.... +evaluate(node,url) := node.doDynamic(request,response) +.... +____ + +==== If None of the Above Works + +... then the client receives 404 NOT FOUND error. + +== Views + +A Java class can have associated "views", which are the inputs to +template engines mainly used to render HTML. Views are placed as +resources, organized by their class names. For example, views for the +class `org.acme.foo.Bar` would be in the `/org/acme/foo/Bar/` folder, +like `/org/acme/foo/Bar/index.jelly` or `/org/acme/foo/Bar/test.jelly`. +This structure emphasizes the close tie between model objects and views. + +Views are inherited from base classes to subclasses. + +=== Jelly + +Jelly script can be used as view files. When they are executed, the +variable "it" is set to the object for which the view is invoked. (The +idea is that "it" works like "this" in Java.) + +For example, if your Jelly looks like the following, then it prints the +name property of the current object. + +[source,code] +---- + + My name is ${it.name} + +---- diff --git a/docs/reference.html b/docs/reference.html deleted file mode 100644 index 3099c339c4..0000000000 --- a/docs/reference.html +++ /dev/null @@ -1,263 +0,0 @@ - - - Reference - - - - - - -

Reference

-

- This document explains the details of how Stapler "staples" your objects to URLs. -

- The way Stapler works is some what like Expression Language; it takes an object and URL, then evaluate the URL against the object. It repeats this process until it hits either a static resource, a view (such as JSP, Jelly, Groovy, etc.), or an action method. -

- This process can be best understood as a recursively defined mathematical function evaluate(node,url). For example, the hypothetical application depicted in the "getting started" document could have an evaluation process like the following: -


-Scenario: browser sends "POST /project/jaxb/docsAndFiles/upload HTTP/1.1"
-
-   evaluate(<root object>, "/project/jaxb/docsAndFiles/upload")
--> evaluate(<root object>.getProject("jaxb"), "/docsAndFiles/upload")
--> evaluate(<jaxb project object>, "/docsAndFiles/upload")
--> evaluate(<jaxb project object>.getDocsAndFiles(), "/upload")
--> evaluate(<docs-and-files-section-object for jaxb>, "/upload")
--> <docs-and-files-section-object for jaxb>.doUpload(...)
-
-

- The exact manner of recursion depends on the signature of the type of the node parameter, and the following sections describe them in detail. Also see a document in Hudson that explains how you can see the actual evaluation process unfold in your own application by monitoring HTTP headers. - - - -

Evaluation of URL: Reference

- -

-This section defines the evaluate(node,url) function. Possible branches are listed in the order of preference, so when the given node and url matches to multiple branches, earlier ones take precedence. - -

-

Notation

- -

-We often use the notation url[0], url[1], ... to indicate the tokens of url separated by '/', and url.size to denote the number of such tokens. For example, if url="/abc/def/", then url[0]="abc", url[1]="def", url.size=2. Similarly if url="xyz", then url[0]="xyz", url.size=1 - -

-List notation [a,b,c,...] is also used to describe url. Lower-case variables represent a single token, while upper-case variables represent variable-length tokens. For example, if we say url=[a,W] and the actual URL was "/abc/def/ghi", then a="abc" and W="/def/ghi". If the actual URL was "/abc", then a="abc" and W="". -

- - -

Stapler Proxy

-

- node can implement the StaplerProxy interface to delegate the UI processing to another object. There's also a provision for selectively doing this, so that node can intelligently decide if it wants to delegate to another object. - -

- Formally, - -

-evaluate(node,url) := evaluate(target,url)   — if target instanceof StaplerProxy, target=node.getTarget(), and target!=null
-
- - -

Index View

-

- If there's no remaining URL, then a welcome page for the current object is served. A welcome page is a side-file of the node named index.* (such as index.jsp, index.jelly, etc. - -

Formally, - -

-evaluate(node,[]) := renderView(node,"index")
-
- - - -

Action Method

-

- If url is of the form "/fooBar/...." and node has a public "action" method named doFooBar(...), then this method is invoked. -

- The action method is the final consumer of the request. This is is convenient for form submission handling, and/or implementing URLs that have side effects. Formally, - -

-evaluate(node,[x,W]) := node.doX(...)
-
- -

- Stapler performs parameter injections on calling action methods. - - -

View

-

- If the remaining URL is "/xxxx/...." and a side file of the node named xxxx exists, then this view gets executed. Views normally have their view-specific extensions, like xxxx.jelly or xxxx.groovy. - -

Formally, - -

-evaluate(node,[x,W]) := renderView(node,x)
-
- - -

Index Action Method

-

- This is a slight variation of above. If there's no remaining URL and there's an action method called "doIndex", this method will be invoked. Formally, - -

-evaluate(node,[]) := node.doIndex(...)
-
- - -

Public Field

-

- If url is "/fooBar/..." and node has a public field named "fooBar", then the object stored in node.fooBar will be evaluated against the rest of the URL. Formally, - -

-evaluate(node,[x,W]) := evaluate(node.x,W)
-
- - - -

Public Getter Method

-

- If url is "/fooBar/..." and node has a public getter method named "getFooBar()", then the object returned from node.getFooBar() will be evaluated against the rest of the URL. -

- Stapler also looks for the public getter of the form "getXxxx(StaplerRequest)". If such a method exists, then this getter method is invoked in a similar way. This version allows the get method to take sophisticated action based on the current request (such as returning the object specific to the current user, or returning null if the user is not authenticated.) - -

- Formally, - -

-evaluate(node,[x,W]) := evaluate(node.getX(...),W)
-
- - - - -

Public Getter Method with a String Argument

-

- If url is "/xxxx/yyyy/..." and node has a public method named "getXxxx(String arg)", then the object returned from currentObject.getXxxx("yyyy") will be evaluated against the rest of the URL "/...." recursively. - -

-Formally, - -

-evaluate(node,[x,y,W]) := evaluate(node.getX(y),W)
-
- - -

Public Getter Method with an int Argument

-

- Really the same as above, except it takes int instead of String. - -

-evaluate(node,[x,y,W]) := evaluate(node.getX(y),W)   — if y is an integer
-
- - - -

Array

-

- If node is an array and url is "/nnnn/...." where nnnn is a number, then the object returned from node[nnnn] will be evaluated against the rest of the URL "/...." recursively. - -

-Formally, - -

-evaluate(node,[x,W]) := evaluate(node[x],W)   — if node instanceof Object[]
-
- - -

List

-

- If node implements java.util.List and the URL is "/nnnn/...." where nnnn is a number, then the object returned from node.get(nnnn) will be evaluated against the rest of the URL "/...." recursively. - -

-Formally, - -

-evaluate(node,[x,W]) := evaluate(node.get(x),W)   — if node instanceof List
-
- - -

Map

-

- If node implements java.util.Map and the URL is "/xxxx/....", then the object returned from node.get("xxxx") will be evaluated against the rest of the URL "/...." recursively. - -

-evaluate(node,[x,W]) := evaluate(node.get(x),W)   — if node instanceof Map
-
- - - -

Dynamic Getter Method

-

- If the current object has a public method getDynamic(String,StaplerRequest,StaplerResponse), and the URL is "/xxxx/..." and then this method is invoked with "xxxx" as the first parameter. The object returned from this method will be evaluated against the rest of the URL "/...." recursively. -

- This is convenient for a reason similar to above, except that this doesn't terminate the URL mapping. - -

-Formally, - -

-evaluate(node,[x,W]) := evaluate(node.getDynamic(x,request,response),W)
-
- - - -

Dynamic Action Method

-

- If the current object has a public "action" method doDynamic(StaplerRequest,StaplerResponse), then this method is invoked. From within this method, the rest of the URL can be accessed by StaplerRequest.getRestOfPath(). This is convenient for an object that wants to control the URL mapping entirely on its own. -

- The action method is the final consumer of the request. - -

-Formally, - -

-evaluate(node,url) := node.doDynamic(request,response)
-
- - - -

If None of the Above Works

-

- ... then the client receives 404 NOT FOUND error. - - - - -

Views

- -

- A Java class can have associated "views", which are the inputs to template engines mainly used to render HTML. - Views are placed as resources, organized by their class names. - For example, views for the class org.acme.foo.Bar would be in the - /org/acme/foo/Bar/ folder, like /org/acme/foo/Bar/index.jelly or /org/acme/foo/Bar/test.jelly. - This structure emphasizes the close tie between model objects and views. -

- Views are inherited from base classes to subclasses. - -

Jelly

-

- Jelly script can be used as view files. When they are executed, the variable "it" is set to the object for which the view is invoked. (The idea is that "it" works like "this" in Java.) -

- For example, if your Jelly looks like the following, then it prints the name property of the current object. -

<html><body>
-  My name is ${it.name}
-</body></html>
- - - diff --git a/docs/stapler.css b/docs/stapler.css deleted file mode 100644 index 82b56c56f5..0000000000 --- a/docs/stapler.css +++ /dev/null @@ -1,27 +0,0 @@ -pre.cmd { - background-color: black; - color: white; - font-weight: bold; - margin-left: 2em; - padding-left: 0.5em; - padding-right: 0.5em; -} - -pre.code { - border: black solid 1px; - background-color: rgb(240,240,255); - color: navy; - font-weight: bold; - margin-left: 2em; - padding-left: 0.5em; - padding-right: 0.5em; -} -pre.code .annotation { - color: gray; -} - -.note { - font-size: smaller; - margin-left: 5em; - color: gray; -} \ No newline at end of file diff --git a/docs/what-is.adoc b/docs/what-is.adoc new file mode 100644 index 0000000000..eed4b04246 --- /dev/null +++ b/docs/what-is.adoc @@ -0,0 +1,88 @@ +Stapler is a library that "staples" your application objects to URLs, +making it easier to write web applications. The core idea of Stapler is +to automatically assign URLs for your objects, creating an intuitive URL +hierarchy. + +Normally, most of your JSP and servlet goes like this; first, you use +parameters and extra path info to locate the object to work with, then +use some more parameters and such to perform some operations against it +(or perhaps render HTML). Stapler takes care of this first portion by +dispatching requests to that object you'll be working with. + +Suppose you are building a web application like the system of java.net, +where you got projects, mailing lists, documents and files section, and +etc. The following figure shows how Stapler works. + +image:stapler.png[image] + +The left half shows your code. OOP lets you model those concepts +straight-forwardly into classes that refer to each other. Through +reflection, Stapler assigns URLs to your application objects. The root +of your application object gets the root URL `/`. The object you can +access with `root.getProject("stapler")` would be assigned to the URL +`/project/stapler`. In this way, your object model directly turns into +the URL hierarchy, as partially shown in purple arrows. + +== JSPs and servlets + +Most of the times JSPs are used to show an object in your application. +Stapler allows you to associate JSPs to your application classes, as +shown in green circles in the above figure. Stapler weaves those JSPs +into the URL hierarchy, and when they get requested, Stapler sets up the +environment such that they can access the corresponding application +object very easily, by the "it" variable (like $\{it.name}). + +Similarly, often you want to run some Java code when a certain URL is +requested, as shown in red circles in the above figure. For example, +when `/project/stapler/delete` is requested, you'd like to delete the +project and redirect the user to `/`. With Stapler, this is as easy as +defining a method on your application objects. Stapler invokes the +method on the right object. + +== Problems That Stapler Solves + +Servlet (and technologies like JSP/JSF that builds on top of it) can +only bind request URLs into your static resources, JSPs, and/or servlets +in a very limited way. You can even tell that from the java.net system. +For example, the URL to view the archive of a mailing list is +`/servlets/SummarizeList?listName=announce`, the URL to search the +archive if `/servlets/SearchList?listName=announce`, and the URL to +manage it is `/servlets/MailingListEdit?list=announce`. + +This has some problems. + +=== Relative URLs are useless + +Ordinary servlet/JSP prevents you from using the URL hierarchy to +organize your application. In the java.net system, if you want to +generate a link from the archive to the search page, you have to put the +whole URL including all the parameters. You'll be writing something like +`/servlets/SearchList?list=${listName}`. As your application gets more +complicated, the number of parameters will increase, making your +application harder and harder to maintain. + +Stapler allows you to use URLs like `list/announce/summarize`, +`list/announce/search`, and `list/announce/edit`. Thus a link from the +summary page to the search is simply `search`. + +=== A lot of boiler-plate code + +Most of the times, the first thing your JSPs and servlets need to do is +to locate the object that you'll be working with. This is tedious. Even +worse, it needs to be repeated in many places. + +Stapler takes care of this for you. When your JSP is invoked, you can +access the target object by the variable "it". Or when your action +method (the equivalent of the servlet) is invoked, it is invoked on the +target object. This cuts down a lot of boring boiler-plate code in your +application. + +=== Recursive Structure + +Servlets can't handle recursive directory structure very well. In the +above figure, I have recursive `Folder` class that has two JSPs each. +The only way to make this work in servlet is to assign unique IDs to +each folder and define those JSPs at the top-level. The actual URLs will +be very different from the natural tree structure. + +Stapler maps this very naturally to URLs. diff --git a/docs/what-is.html b/docs/what-is.html deleted file mode 100644 index d36d6e7a1d..0000000000 --- a/docs/what-is.html +++ /dev/null @@ -1,59 +0,0 @@ - - - What is Stapler? - - -

- Stapler is a library that "staples" your application objects to URLs, making it easier to write web applications. The core idea of Stapler is to automatically assign URLs for your objects, creating an intuitive URL hierarchy. -

- Normally, most of your JSP and servlet goes like this; first, you use parameters and extra path info to locate the object to work with, then use some more parameters and such to perform some operations against it (or perhaps render HTML). Stapler takes care of this first portion by dispatching requests to that object you'll be working with. -

- Suppose you are building a web application like the system of java.net, where you got projects, mailing lists, documents and files section, and etc. The following figure shows how Stapler works. - -

- -
- -

- The left half shows your code. OOP lets you model those concepts straight-forwardly into classes that refer to each other. Through reflection, Stapler assigns URLs to your application objects. The root of your application object gets the root URL /. The object you can access with root.getProject("stapler") would be assigned to the URL /project/stapler. In this way, your object model directly turns into the URL hierarchy, as partially shown in purple arrows. - -

JSPs and servlets

-

- Most of the times JSPs are used to show an object in your application. Stapler allows you to associate JSPs to your application classes, as shown in green circles in the above figure. Stapler weaves those JSPs into the URL hierarchy, and when they get requested, Stapler sets up the environment such that they can access the corresponding application object very easily, by the "it" variable (like ${it.name}). -

- Similarly, often you want to run some Java code when a certain URL is requested, as shown in red circles in the above figure. For example, when /project/stapler/delete is requested, you'd like to delete the project and redirect the user to /. With Stapler, this is as easy as defining a method on your application objects. Stapler invokes the method on the right object. - - - -

Problems That Stapler Solves

- -

- Servlet (and technologies like JSP/JSF that builds on top of it) can only bind request URLs into your static resources, JSPs, and/or servlets in a very limited way. You can even tell that from the java.net system. For example, the URL to view the archive of a mailing list is /servlets/SummarizeList?listName=announce, the URL to search the archive if /servlets/SearchList?listName=announce, and the URL to manage it is /servlets/MailingListEdit?list=announce. -

- This has some problems. - - -

Relative URLs are useless

-

- Ordinary servlet/JSP prevents you from using the URL hierarchy to organize your application. In the java.net system, if you want to generate a link from the archive to the search page, you have to put the whole URL including all the parameters. You'll be writing something like /servlets/SearchList?list=${listName}. As your application gets more complicated, the number of parameters will increase, making your application harder and harder to maintain. -

- Stapler allows you to use URLs like list/announce/summarize, list/announce/search, and list/announce/edit. Thus a link from the summary page to the search is simply search. - - -

A lot of boiler-plate code

-

- Most of the times, the first thing your JSPs and servlets need to do is to locate the object that you'll be working with. This is tedious. Even worse, it needs to be repeated in many places. -

- Stapler takes care of this for you. When your JSP is invoked, you can access the target object by the variable "it". Or when your action method (the equivalent of the servlet) is invoked, it is invoked on the target object. This cuts down a lot of boring boiler-plate code in your application. - - -

Recursive Structure

-

- Servlets can't handle recursive directory structure very well. In the above figure, I have recursive Folder class that has two JSPs each. The only way to make this work in servlet is to assign unique IDs to each folder and define those JSPs at the top-level. The actual URLs will be very different from the natural tree structure. -

- Stapler maps this very naturally to URLs. - - - - - From 2be9db493b21c2feadbe75f5a4a30eeaf8a12366 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 May 2021 17:30:16 -0400 Subject: [PATCH 3/6] `compression.html` was actually malformed to begin with, but somehow `maven-site-plugin` tolerated it? --- docs/compression.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/compression.adoc b/docs/compression.adoc index 4d72f2ef08..0f102299f1 100644 --- a/docs/compression.adoc +++ b/docs/compression.adoc @@ -29,8 +29,9 @@ element of your view, like this: [source,code] ---- - - + + + ... ---- From b8ccb0f7998e789ac85746d29e273f2dc584901e Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 May 2021 17:31:37 -0400 Subject: [PATCH 4/6] more bad Markdown --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5410cd49af..6f90872871 100644 --- a/README.md +++ b/README.md @@ -1,8 +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) + * [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) From 907875c234a10099f1f796e1987b0c1f72c3d0ec Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 May 2021 17:37:02 -0400 Subject: [PATCH 5/6] More code sample fixups --- docs/compression.adoc | 2 +- docs/getting-started.adoc | 49 +++++++++++++++++++++------------------ docs/reference.adoc | 2 +- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/docs/compression.adoc b/docs/compression.adoc index 0f102299f1..f3f3ebb591 100644 --- a/docs/compression.adoc +++ b/docs/compression.adoc @@ -27,7 +27,7 @@ If your view script produces text content, and if you'd like to compress the stream, put `` as the root element of your view, like this: -[source,code] +[source,xml] ---- diff --git a/docs/getting-started.adoc b/docs/getting-started.adoc index c8f0f41178..881e36e107 100644 --- a/docs/getting-started.adoc +++ b/docs/getting-started.adoc @@ -10,15 +10,17 @@ 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,code] +[source,xml] ---- - Stapler - org.kohsuke.stapler.Stapler - - - - Stapler - / + + Stapler + org.kohsuke.stapler.Stapler + + + + Stapler + / + ---- === Registering the root object @@ -32,7 +34,7 @@ and use the helper method from `Stapler`. Specifically, you write the following class: -[source,code] +[source,java] ---- package example; import org.kohsuke.stapler.Stapler; @@ -51,9 +53,11 @@ public class WebAppMain implements ServletContextListener { You also have to put the following XML in `WEB-INF/web.xml` so that the container recognizes this class. -[source,code] +[source,xml] ---- - example.WebAppMain + + example.WebAppMain + ---- In this way, the `WebAppMain.contextInitialized` method is executed as @@ -79,17 +83,16 @@ organized according to the class they are associated with. Here is a part of this index.jsp: -[source,code] +[source,xml] ---- -... +... <%-- side files can include static resources. --%> - - - Inventory - - ${i.value.title} + - +

Inventory

+ + ${i.value.title}
+
... ---- @@ -119,10 +122,10 @@ 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,code] +[source,java] ---- public class BookStore { - public Map/**/ getItems() { + public Map/**/ getItems() { return items; } ... @@ -179,7 +182,7 @@ call these methods "action methods". Action methods take the following signature: -[source,code] +[source,java] ---- public void do[Name]( StaplerRequest request, StaplerResponse response ) { ... @@ -198,7 +201,7 @@ 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,code] +[source,java] ---- public void doHello( StaplerRequest request, StaplerResponse response ) { ... diff --git a/docs/reference.adoc b/docs/reference.adoc index e815ff3d7c..81aa6a200c 100644 --- a/docs/reference.adoc +++ b/docs/reference.adoc @@ -292,7 +292,7 @@ idea is that "it" works like "this" in Java.) For example, if your Jelly looks like the following, then it prints the name property of the current object. -[source,code] +[source,xml] ---- My name is ${it.name} From 9e91f1a24d008e532bec1a72dd268f763f4f01cb Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 6 May 2021 18:27:06 -0400 Subject: [PATCH 6/6] Docs fixes pointed out by @timja --- docs/reference.adoc | 6 +++--- docs/what-is.adoc | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/reference.adoc b/docs/reference.adoc index 81aa6a200c..a4f9676667 100644 --- a/docs/reference.adoc +++ b/docs/reference.adoc @@ -32,8 +32,8 @@ ____ The exact manner of recursion depends on the signature of the type of the `node` parameter, and the following sections describe them in detail. Also see -http://hudson.gotdns.com/wiki/display/HUDSON/Figuring+out+URL+binding+of+Stapler[a -document in Hudson] that explains how you can see the actual evaluation +https://wiki.jenkins.io/display/JENKINS/Figuring+out+URL+binding+of+Stapler[Figuring out URL binding of Stapler] +that explains how you can see the actual evaluation process unfold in your own application by monitoring HTTP headers. === Evaluation of URL: Reference @@ -61,7 +61,7 @@ ____ ==== Stapler Proxy `node` can implement -https://stapler.dev.java.net/nonav/javadoc/index.html?org/kohsuke/stapler/StaplerProxy.html[the +https://javadoc.jenkins.io/component/stapler/org/kohsuke/stapler/StaplerProxy.html[the `StaplerProxy` interface] to delegate the UI processing to another object. There's also a provision for selectively doing this, so that `node` can intelligently decide if it wants to delegate to another diff --git a/docs/what-is.adoc b/docs/what-is.adoc index eed4b04246..35f6a38dcd 100644 --- a/docs/what-is.adoc +++ b/docs/what-is.adoc @@ -1,3 +1,5 @@ += What is Stapler? + Stapler is a library that "staples" your application objects to URLs, making it easier to write web applications. The core idea of Stapler is to automatically assign URLs for your objects, creating an intuitive URL