Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-31203

Stapler facet based on typesafe Java plus static HTML


      Today you have two options for writing Stapler views: Jelly and Groovy. Neither are comfortable to use during any kind of serious refactoring.

      The Jelly views get some very rudimentary syntax checks during injected tests; AFAIK the Groovy views get none. So the only automated tests run on views are what you write with JenkinsRule.WebClient or the acceptance test harness.

      At runtime, the JEXL component of Jelly allows all sorts of typos and mistyped expressions to be evaluated silently (usually just returning null), making it very hard to tell what is going on when things do not behave. Indeed Jenkins core makes heavy use of duck typing in standard form controls, widgets, etc., so finding what the expected Java type is for a given Jelly snippet is often very difficult. This makes refactoring and API migration tedious and stressful.

      Groovy views do a bit better at runtime, since at least missing properties will throw exceptions, and you can declare static types, but typically typing information is incomplete. Working with the code in these views also requires specialized tooling, and even with static types declared in the view there is no compile-time check done during the Maven build. So you are still left with a lot of opportunities for errors.

      A related but distinct problem is that standard form controls typically tend to produce either block content or rowsets, and accept nested elements with either block content or rowsets, but the distinction is generally not documented, much less captured in typing metadata that would allow category mistakes to be caught during a build. If you get it backwards, you will typically find out because of some meaningless-looking JavaScript error, or mangled display.

      There are also any number of contextual variables (it, instance, descriptor, job, ...) which some block-scoped controls define and others expect to consume, but the exact list is not always clearly documented or defined, it is not enforced meaningfully, and the types of the variables are often fuzzy. ("Usually a DescriptorList, but could be a Map<Descriptor,Describable> or anything else with a get method.")

      st:include (or the Groovy equivalent) is also rather error-prone. There are several different attributes which are confusingly similar-looking and indeed interrelated. There is no static verification that the included page actually exists. It is common to try to do basic OO modeling using views (abstract sections, overridable sections, calling super), but this is very awkward and there is no way to clearly document what is defined where. In fact if you look carefully at where various views are defined in Jenkins core, it is riddled with mistakes: things defined too high (on AbstractItem when it is clearly Job-specific) or too low (on AbstractBuild when the corresponding Java code is in Run), etc.

      Assuming that Stapler views are going to be with us for a long time to come, we can at least try to provide a more developer-friendly infrastructure for writing them. I suggest that there be no template engine; all logic must be in regular src/main/java/**/*.java plugin code. It may be OK to emit a handful of HTML tags directly from the Java code (with Messages for localizable pieces); for more complex HTML, use a Wicket-like static (X)HTML page associated with the Java code (with an annotation processor to enforce the right filename and perform validation!), with special IDs or other attributes to indicate places where conditional blocks or loops or nested views would be placed, and possibly with some special notation for localizable text spans. Body content and rowset content should ideally be differentiated by static type. (TBD if block content needs to be differentiated from inline content; HTML is generally more forgiving about that.)

      To associate views with Java type hierarchies, there should be some way of actually using abstract methods and super calls to render content from particular parts of the code modularly.

      If done properly, any type-related mistakes would be caught by the Java compiler long before you try to run your plugin, and plain old Javadoc would suffice to document form controls and other reusable components.

            Unassigned Unassigned
            jglick Jesse Glick
            3 Vote for this issue
            5 Start watching this issue