Introduction

This document provides a complete reference for the plugin programming model.

Getting Started

For a quick introduction to developing plugins please consult the getting started guide.

Plugin API Goals

These goals are met by leveraging the Java EE Servlet API and the JSR-330 Dependency Injection Framework, along with a small set of custom annotations that enable HttpServlets to be plugged in without resorting to editing a web.xml deployment descriptor.

Extension Points

Plugins can plug in additional logic at the following extension points:

We call these extension points, services. A service is simply any type on which another type depends. The ExtensionPoints type enumerates the available extension points.

Plugin Provider

A provider is any Java type annotated with the @Provides annotation. A provider provides one or more services. See the @Provides documentation for more information about how to express what services a provider offers.

Provider Lifecycle

Providers can have one of two life-cycles:

By default all types annotated with @Provides are given a @RequestScoped life-cycle. This means that for each request a new instance of the type is instantiated, and that instance is destroyed once processing of the request has completed.

Since each request is processed in a single thread, this means that, @RequestScoped services:

Since @ApplicationScoped services endure for the lifetime of ORDS application, this means that:

About @ApplicationScoped

Avoid the use of @ApplicationScoped services, only use them when it is absolutely necessary to share state across requests. Some appropriate use-cases for @ApplicationScoped services:

Inappropriate use cases include:

Rather than sharing state across threads, prefer to store global state in the backend storage, i.e. the database, where it can be reliably accessed concurrently.

Service Provider Prioritization

In many instances there may be multiple providers of a service. To assist in choosing the most appropriate provider, or to determine the order in which providers should be invoked, the @Priority annotation is provided.

Dependency Injection

The runtime uses the common dependency injection pattern to communicate what services a provider depends on. The runtime includes a JSR-330 compatible dependency injection framework. A provider enumerates it’s dependencies via it’s constructor, which must be annotated with @Inject:

@Provides /* Advertise the Foo provider to the D.I. framework */
class Foo {
 @Inject Foo(Bar bar) { /* Express the dependency on the Bar service */
  this.bar = bar;
 }
 
 public void someMethod() {
  bar.use(); /* use the dependency */
 }
 private final Bar bar;
}

Available Dependencies

The set of dependencies made available to plugins is enumerated by the AvailableDependencies type.

Servlet Extensions

If you wish to create an extension that services HTTP Requests then you must create a HttpServlet extension. The Getting Started guide contains a tutorial demonstrating all the steps required to create a HttpServlet extension. The sections below provide further detail about creating servlet extensions, in particular how to use Java annotations to describe the servlet’s metadata.

Most of the metadata can be specific to a particular HTTP method, a particular URI pattern or an entire servlet. Specific metadata is defined by annotating the corresponding Java method or by setting a property of the @PathTemplate annotation.

Servlet Lifecycle

In contrast to the lifecycle of servlets in a regular JEE application server, servlets in ORDS only exist for the duration of a HTTP request. This means each servlet instance is used in a single thread, and does not have to worry about threading considerations.

Each HTTP request gets it’s own instance of a servlet. The instance is created once it has been identified as the target of the request, and the instance is destroyed once the request has been serviced.

About the @Dispatches annotation

The @Dispatches annotation informs ORDS about what URL patterns the servlet wishes to service. The Javadoc describes in detail how to use the annotation and provides several examples. These patterns are termed Route Patterns, and have a specific syntax.

Every servlet must have exactly one @Dispatches annotation.

A @Dispatches annotation must have at least one @PathTemplate annotation. Each @PathTemplate annotation describes a URL pattern that the servlet is willing to service.

About the @PathTemplate annotation

The [@PathTemplate][path-template] annotation describes a single Route Pattern that a servlet services. In addition the @PathTemplate annotation has a number of properties that can be used to provide metadata specific to the specified Route Pattern.

About PathTemplateMatch

When a servlet wishes to determine which @PathTemplate was matched, it should use the PathTemplates#matchedTemplate(HttpServletRequest) method to retrieve the PathTemplateMatch:

@Provides
@Dispatches({
 @PathTemplate(name="collection", value="/examples/collection/"),
 @PathTemplate(name="item", value="/examples/collection/:id")})
class ExampleServlet {

 protected void doGet(HttpServletRequest req,HttpServletResponse resp) {
  PathTemplateMatch matched = this.pathTemplates.matchedTemplate(req);
  switch(matched.name()) {
   case "collection":
    // process collection pattern
    break;
   case "item::
    String id = matched.parameters().get("id");
    // process item pattern
    break;
  }
 }
}

Once the PathTemplateMatch instance has been retrieved the servlet can branch to the correct logic for handling the request, typically by examining the PathTemplateMatch#name() method.

If the Route Pattern includes any parameters, the value bound to the parameter can be retrieved via the PathTemplateMatch#parameters() method.