Stapler Web Framework

repository·master·Indexed 41 days ago

https://github.com/jenkinsci/stapler

A Java web framework used primarily by Jenkins to map URLs directly to Java objects and methods. It handles URL binding, request processing, and view rendering via Jelly, featuring annotations like @Header, @QueryParameter, @JsonBody, and @RequirePOST for request handling, as well as support for side files and i18n.

Tokens
6.3K
Snippets
14
Records
32
Agent score
51%

What's inside Stapler

  1. Benefits of Stapler over standard Servlets/JSPs

    master

    Stapler solves several common issues found in traditional Servlet/JSP development:

    1. Hierarchical URLs vs. Parameter-heavy URLs: Instead of using complex query parameters (e.g., /servlets/SearchList?listName=announce), Stapler uses clean, hierarchical paths (e.g., /list/announce/search). This makes generating relative links much easier; a link from a summary page to a search page can simply be search rather than a full path with parameters.
    2. Reduction of Boilerplate: You no longer need to write repetitive code to locate objects from request parameters. Stapler handles object lookup and provides the object via the it variable or as the target of method invocation.
    3. Natural Recursive Structures: Stapler handles recursive data structures (like a Folder class containing other Folder objects) naturally. The URL structure will follow the tree structure of your objects, whereas Servlets typically require flattening the structure or using unique IDs at a top-level servlet.
  2. Implement Polymorphic side files

    master

    When using class hierarchies, Stapler searches for side files by checking the entire implementation hierarchy. This allows derived classes to override or extend the presentation of base classes.

    If the target object is an instance of Book (which extends Item) and Stapler is looking for footer.jsp:

    1. It first looks for /WEB-INF/side-files/example/Book/footer.jsp.
    2. If not found, it falls back to /WEB-INF/side-files/example/Item/footer.jsp.

    This mechanism can also be used when one JSP includes another, allowing common components to be defined in a base class directory.

  3. Understand the Stapler URL mapping concept

    master

    Stapler is a library that automatically maps your application's object hierarchy to a URL hierarchy using reflection. Instead of manually routing URLs to servlets or JSPs using parameters (e.g., /servlets/SummarizeList?listName=announce), Stapler allows your object model to define the structure.

    How it works:

    • The root application object is mapped to /.
    • Nested objects are mapped to paths corresponding to their accessors. For example, an object accessible via root.getProject("stapler") is automatically assigned the URL /project/stapler.
    • This creates an intuitive, hierarchical URL structure that mirrors your Java class relationships.
  4. Manage scopes and variables with jelly:parentScope and jelly:findAncestor

    master

    These tags help manage variable visibility and scope within Jelly files:

    • jelly:parentScope: Executes the body of the tag in the parent scope. This is useful for creating a 'local' scope.
    • jelly:findAncestor[tag, var, namespaceContext]: Searches the call stack for the nearest tag matching the provided tag (QName) and assigns that tag object to the variable specified by var.
  5. Use jelly:customTagLibrary.StaplerDynamic for predictable variable resolution

    master

    In standard Jelly, using <d:invokeBody/> to call back into a script causes the body to be evaluated with the variable scope of the caller rather than where the body was created. This often prevents access to the attrs variable or local variables.

    jelly:customTagLibrary.StaplerDynamic counters this by temporarily restoring the original attrs when the body is evaluated, making name resolution behave like standard programming languages.

  6. Use Side Files to render objects

    master

    Stapler uses "side files" (typically JSPs or static resources) to display objects. Side files are organized in a directory structure that mirrors your Java class hierarchy under WEB-INF/side-files/.

    Key Concepts:

    • Implicit it variable: Inside a side JSP, the target object being served is available via the implicit variable it. You can access its properties using EL, e.g., ${it.items}.
    • Naming Convention: To serve a URL like /count, create a file named count.jsp. Note that the .jsp extension is omitted in the URL to work around servlet specification limitations.
    • Static Resources: Side files can also be static resources (like logo.png) placed in the class-specific directory, allowing them to be referenced via relative URLs in JSPs.
    <%-- Example: index.jsp for a BookStore object --%>
    <html...><body>
      <img src="logo.png">
      <h2 id="Inventory"></h2>
      <c:forEach var="i" items="${it.items}">
        <a href="items/${i.key}">${i.value.title}</a><br/>
      </c:forEach>
    </body></html>
  7. How Stapler maps URLs to Java objects

    master

    Stapler reflexively binds reachable child objects to URLs by inspecting public get methods.

    Example URL Resolution for /items/b1:

    1. Root: Stapler starts at the root object (e.g., BookStore).
    2. Method Matching: It sees items in the URL and looks for a method getItems() on BookStore. It evaluates x = bookStore.getItems().
    3. Map/Collection Navigation: It sees b1 and notices x is a Map. It evaluates y = x.get("b1").
    4. Side File Selection: Once the URL is exhausted, Stapler looks for the side file associated with the resulting object y (e.g., /WEB-INF/side-files/example/Book/index.jsp).
  8. How Stapler URL binding works

    master

    Stapler maps URLs to Java objects using a recursive evaluation process called evaluate(node, url). It takes a starting object (the node) and a URL, then evaluates the URL against that object. This process repeats recursively, traversing the object graph until it reaches a terminal state: a static resource, a view (like Jelly or Groovy), or an action method.

    Example Evaluation Flow: For a request POST /project/jaxb/docsAndFiles/upload:

    1. evaluate(root, "/project/jaxb/docsAndFiles/upload")
    2. evaluate(root.getProject("jaxb"), "/docsAndFiles/upload")
    3. evaluate(project, "/docsAndFiles/upload")
    4. evaluate(project.getDocsAndFiles(), "/upload")
    5. evaluate(docsAndFiles, "/upload")
    6. evaluate(docsAndFiles, "") -> calls .doUpload(...)
  9. Use the 'it' variable in JSPs

    master

    When Stapler dispatches a request to a JSP, it automatically sets up the environment so the JSP can access the target application object directly using the it variable. This eliminates the need to manually extract parameters from the request to locate the object you want to display.

    Usage: In your JSP file, you can access properties of the target object using the syntax ${it.propertyName}. For example, if the URL /project/stapler maps to a project object, you can display its name using ${it.name}.

  10. Define Views for rendering templates

    master

    Views are template files (e.g., .jelly, .groovy) associated with a Java class. They are organized in a folder structure mirroring the class name. For example, views for org.acme.foo.Bar must be located in /org/acme/foo/Bar/.

    Common View Types:

    • Index View: If the URL is empty, Stapler looks for index.* (e.g., index.jelly).
    • Named View: If the remaining URL segment is xxxx, Stapler looks for a file named xxxx.* (e.g., xxxx.jelly).
  11. Define actions by adding methods to application objects

    master

    To perform operations (like deleting a resource or redirecting a user) when a specific URL is requested, you define methods directly on your application objects. Stapler identifies these methods and invokes them when the corresponding URL path is hit.

    Example Workflow: If you want to handle a request to /project/stapler/delete, you would define a delete() method on the stapler project object. Stapler will then invoke that method when the URL is requested.

  12. Document Jelly tag files and attributes

    master

    You can use specific tags to generate documentation and schemas for your custom Jelly tag files. The maven-stapler-plugin uses these to automate documentation generation.

    Documenting a Tag File

    Use <jelly:documentation> immediately inside the root element of your tag file. The body can use Textile markup.

    Documenting Attributes

    Use <jelly:attribute> inside an org.kohsuke.stapler.jelly.DocumentationTag to describe specific attributes. The body uses natural language (Textile markup supported).

    Attribute Documentation Options:

    • name (required): The name of the attribute.
    • use: Set to "required" if the attribute is mandatory; otherwise defaults to "optional".
    • type: A description of the expected Java type.
    • deprecated: Set to true to mark an attribute as deprecated.
    • since: Tracks when the attribute was added.