16 Extending ORDS Functionality with Plugins
This chapter explains and provides examples on using ORDS plugin framework.
ORDS has a plugin framework that allows you to add your own custom functionality
into the ORDS web application. Plugins can be added to the ORDS runtime by placing the
jar files in the lib/ext directory. The ORDS distribution contains the
source for example plugins. The plugin examples can be built using Apache
ant, a software tool used for automating the build processes.
- Plugin Programming Model
This section explains how ORDS plugin framework is designed to enable developers to extend ORDS functionality. - Servlet Extensions
This section explains how to create an extension to handle the HTTP requests. - Plugin Examples
This section walks you through building and deploying the plugin-demo plugin. - Route Patterns
A Route Pattern defines a format used to match specific HTTP request paths. The pattern is matched against the path component of the request URI. - Route Pattern Syntax Rules
This section describes the route pattern syntax rules. - Pattern Matching Rules
This section describes the matching rules of the route pattern. - Route Pattern Sets
A collection of route patterns is termed a route pattern set.
16.1 Plugin Programming Model
This section explains how ORDS plugin framework is designed to enable developers to extend ORDS functionality.
Developers can customize ORDS to meet specific business needs and requirements using the plugin framework. To leverage the ORDS plugin framework, developers must have experience with Java development and familiar with Java SE and Java EE APIs. When you choose to embed one of the GraalVM-supported polyglot languages, the required lnguage components must be installed and configured. This section briefly demonstrates a JavaScript plugin example.
- Plugin API Objectives
This section lists the ORDS plugin framework objectives. - Extension Points
This section describes how the plugins can implement additional logic. - Plugin Provider
This section describes how plugins declare and provide new extension points. - Provider Lifecycle
This section describes the provider lifecycles. - Service Provider Prioritization
This section describes the prioritization of service provider. - Dependency Injection
- AvailableDependencies
The set of dependencies made available to plugins is enumerated by the AvailableDependencies type.
Parent topic: Extending ORDS Functionality with Plugins
16.1.1 Plugin API Objectives
This section lists the ORDS plugin framework objectives.
The ORDS plugin framework was designed to meet the following objectives:
- Minimal learning curve: Build on existing Java SE, Java EE APIs and programming models
- Low Coupling: Minimizes external code dependencies, requiring
only the following dependencies:
- Java SE
- Java EE Servlet and
javax.inject - Minimal ORDS glue code
The ORDS plugin framework follows these principles by leveraging the Java EE
Servlet API, the JSR-330 Dependency Injection Framework, and a limited set of custom
annotations that enable HttpServlets to be seamlessly integrated as plugins. These
capabilities enable developers to extend ORDS without the need to edit
web.xml deployment descriptor, while also providing greater
flexibility and customization options.
Parent topic: Plugin Programming Model
16.1.2 Extension Points
This section describes how the plugins can implement additional logic.
Plugins can extend application functionality through the following extension point objects:
- Extend the
HttpServletclass to process the incoming HTTP requests - Implement the servlet
Filterinterface to intercept and modify requests or responses. - Implement the
Lifecycleinterface to respond to application startup and shutdown events. - Instantiate
ConfigurationSettingto introduce new settings to alter the behavior of the plugins - Implement the
CommandProviderinterface to add custom commands accessible from the command line.
We refer to these extension points as services. A service is simply any type
that another type depends on. The ExtensionPoints type lists the
available extension points.
Parent topic: Plugin Programming Model
16.1.3 Plugin Provider
This section describes how plugins declare and provide new extension points.
A provider refers to any Java type annotated with the @Provides annotation. A provider supplies one or more services. For detailed information about the services offered by a provider, refer to the @Provides documentation.
Parent topic: Plugin Programming Model
16.1.4 Provider Lifecycle
This section describes the provider lifecycles.
- @RequestScoped: instantiated at the start of an HTTP request and destroyed after the request is processed.
- @ApplicationScoped: Instantiated at the startup of ORDS when the application server starts and terminated upon stopping or restarting ORDS when the server stops or restarts ORDS.
By default all Java types annotated with @Provides are assigned a @RequestScoped life-cycle.
| Request Type | @RequestScoped Services | @ApplicationScoped Services |
|---|---|---|
| Threading | Exist in a single thread | Can be accessed from multiple threads, needs to be fully thread safe. |
| Instantiation | Are instantiated and destroyed very frequently thus instantiation/destruction must be efficient | Are instantiated and destroyed very occasionally. Endures ORDS application lifecycle |
| Can depend on | Other @RequestScoped services and @ApplicationScoped services | Other @ApplicationScoped services but not on @RequestScoped services |
| Additional considerations | Can safely hold per request state in member variables. | Lifecycle providers always have an @ApplicationScoped lifecycle. |
- Best Practices
This section lists the best practices.
Parent topic: Plugin Programming Model
16.1.4.1 Best Practices
This section lists the best practices.
Following are some best practices:
Avoid using @ApplicationScoped services unless it is absolutely necessary to share state across multiple requests.
- Caching data that must be shared among multiple requests. Caching can reduce the cost of certain transactions and operations, and in some cases, the performance benefits outweigh the complexity of thread synchronization and the threat of invalid cache states.
- Tracking global metrics such as the total number of requests served or the total number of bytes served.
- Caching data simply because you believe recomputing it is expensive.
- Preferring a single instance of a service because creating one per request is inefficient or wasteful.
Instead of sharing state across threads, store global state in backend storage systems such as databases, which offer reliable concurrent access. This approach avoids the risks associated with shared state across threads.
Parent topic: Provider Lifecycle
16.1.5 Service Provider Prioritization
This section describes the prioritization of service provider.
When there are multiple providers for a service, the @Priority annotation is provided to assist in choosing the most appropriate provider, or to determine the order in which the providers should be invoked.
Parent topic: Plugin Programming Model
16.1.6 Dependency Injection
The runtime leverages the standard dependency injection pattern to specify the services
that a provider depends on. It includes a JSR-330 compatible dependency injection
framework. A provider declares its dependencies in its constructor, which must be
annotated with @Inject.
Example 16-1 Dependency Injection Example
@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;
}
See Also:
Parent topic: Plugin Programming Model
16.1.7 AvailableDependencies
The set of dependencies made available to plugins is enumerated by the AvailableDependencies type.
Parent topic: Plugin Programming Model
16.2 Servlet Extensions
This section explains how to create an extension to handle the HTTP requests.
To create an extension that handles the HTTP requests, you must create a class that extends HttpServlet.
The following sections provide more details on building servlet extensions, highlighting how Java annotations can be used to define the metadata of the servlet.
Major portion of the metadata can be specific to a particular HTTP method, a particular URI pattern or an entire servlet. You can define the specific metadata by annotating the corresponding Java method or setting a property of the @PathTemplate annotation.
- Servlet Lifecycle
This section describes servlet lifecycle in ORDS.
Parent topic: Extending ORDS Functionality with Plugins
16.2.1 Servlet Lifecycle
This section describes servlet lifecycle in ORDS.
Servlets in ORDS exist only for the duration of an HTTP request, unlike the servlet lifecycle in a standard JEE application server. As a result, each servlet instance is used by a single thread and does not need to manage threading concerns.
For each HTTP request, a separate 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
This section explains about @Dispatches annotation. - About the @PathTemplate Annotation
This section describes the @PathTemplate annotation. - About PathTemplateMatch
This section describes PathTemplateMatch object.
Parent topic: Servlet Extensions
16.2.1.1 About the @Dispatches Annotation
This section explains about @Dispatches annotation.
The @Dispatches annotation informs ORDS on which URL patterns the servlet is intended to handle. These patterns are termed as Route Patterns, and they have a specific syntax.
Every servlet must have exactly one @Dispatches annotation. A @Dispatches annotation must have at least one @PathTemplate annotation and each @PathTemplate annotation describes an URL pattern that the servlet is willing to service.
The Javadoc describes in detail how to use the annotation and provides several examples.
Parent topic: Servlet Lifecycle
16.2.1.2 About the @PathTemplate Annotation
This section describes 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 the metadata specific to the specified route pattern.
Parent topic: Servlet Lifecycle
16.2.1.3 About PathTemplateMatch
This section describes PathTemplateMatch object.
To identify which @PathTemplate was matched, a servlet should call the
PathTemplates.matchedTemplate(HttpServletRequest) method to obtain the
corresponding 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, then the value bound to the parameter can be retrieved through the
PathTemplateMatch#parameters()method.
Given the
route:/examples/collection/:id
/examples/collection/101/examples/collection?id=101
Parent topic: Servlet Lifecycle
16.3 Plugin Examples
This section walks you through building and deploying the plugin-demo plugin.
The plugin-demo plugin queries the database to determine the current database user and echo that information in the response. Examples using both Java and Javascript are provided as they are the only two languages currently supported for plug-in creation.
Prerequisites
- JDK 1.8 or later
- Apache Ant 1.8.2 or later
- Installed and configured ORDS with a REST enabled database schema
- Java Plugin Demonstration
This section provides the details of the Java plugin-demonstraion example. - Analyzing the Request URLs
This section explains how ORDS analyzes and maps the request URL to the database pool and schema. - Trying the Request URL
- Java Examples
The following sections lists some Java examples. - Javascript Plugin Demonstration
Parent topic: Extending ORDS Functionality with Plugins
16.3.1 Java Plugin Demonstration
This section provides the details of the Java plugin-demonstraion example.
The plugin-demonstraion example is located at
examples/plugins/plugin-demo and contains the source for a
HttpServlet that gets a database connection injected at runtime. The servlet uses that
JDBC database connection to run a query in the database and return a response at
runtime. All the files referenced are included in the product distribution under the
examples/plugins folder:
${ORDS_HOME}
|-- examples
|-- plugins
|-- lib
|-- plugin-demo
|-- srcwhere ${ORDS_HOME} is the location from where the product distribution
was unzipped.
- About the plugin-demo Folder Structure
This section describes the folder structure of the plugin-demo files. - About PluginDemo.java
This section shows the sample code snippet ofPluginDemo.javaplugin along with an explaination for the sample code. - Building the Plugin
This section explains how to build the plugin. - Packaging the Plugin
This section explains how to package the plugin. - Testing the Plugin
This section explains how to test the plugin.
Parent topic: Plugin Examples
16.3.1.1 About the plugin-demo Folder Structure
This section describes the folder structure of the plugin-demo files.
${ORDS_HOME} folder at the
following location:${ORDS_HOME}/examples/plugins/plugin-demoThe ${ORDS_HOME}/examples/plugins/plugin-demo folder contains the
following:
srcfolder contains:PluginDemo.java: the Java source code of the plugin
build.xml:ANTbuild script that compiles and packages the source codebuilt: folder generated frombuild.xmlwhich contains the packaged plugin (plugin-demo.jar)
${ORDS_HOME}/examples/plugins/libfolder contains the.jarfiles required to compile the plugins.
- Required Libraries
This section describes the required libraries.
Parent topic: Java Plugin Demonstration
16.3.1.1.1 Required Libraries
This section describes the required libraries.
The required jar files are included in the
${ORDS_HOME}/examples/plugins/lib product distribution folder.
To compile a plugin the following libraries must be in the classpath:
plugin-api.jarplugin-apt.jarjavax.inject.jarservlet-api-3.1.0.jarojdbc11.jar
About plugin-api.jar
This library provides the glue code such as @Dispatches
to weave the plugin into the runtime.
About plugin-apt.jar
This library provides the annotation processor that makes the classes
annotated discoverable with @Provides.
About javax.inject.jar
This library provides the JSR-330 API types such as
@Inject.
About servlet-api-3.1.0.jar
This library provides the Java Servlet 3.1.0 API types, such as
HttpServlet.
About ojdbc11.jar
This library is optional and is only required if the plugin needs to
access the Oracle JDBC Extension APIs such as OracleConnection.
Parent topic: About the plugin-demo Folder Structure
16.3.1.2 About PluginDemo.java
This section shows the sample code snippet of
PluginDemo.java plugin along with an explaination for the sample
code.
PluginDemo.java
plugin:package example;
import java.io.IOException;
import java.sql.*;
import jakarta.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import oracle.dbtools.plugin.api.di.annotations.Provides;
import oracle.dbtools.plugin.api.http.annotations.*;
import oracle.dbtools.plugin.api.routes.*;
/**
* This example plugin {@link HttpServlet} demonstrates:
* <ul>
* <li>Using the injected {@link Connection} to query the database.</li>
* <li>Using the injected {@link PathTemplates} service to decode the parameters
* of the servlet's {@link PathTemplateMatch}.</li>
* </ul>
*
* <h4>Testing the Servlet</h4> Invoke the servlet with the following URL:
*
* <pre>
* http://<i>server</i>/ords/<i>schema</i>/demos/plugin?who=<i>somebody</i>
* </pre>
*
* where:
* <ul>
* <li><i>server</i> is the hostname and port of the server.</li>
* <li><i>schema</i> is the name of the REST enabled database schema.</li>
* <li><i>somebody</i> is any value you wish, e.g. a person's name.</li>
* <ul>
* For example:
*
* <pre>
* http://localhost:8080/ords/test_schema/demos/plugin?who=Scott
* </pre>
*
* @author cdivilly
*
*/
@Provides
@Dispatches(@PathTemplate("/demos/plugin"))
class PluginDemo extends HttpServlet {
@Inject
PluginDemo(Connection conn, PathTemplates pathTemplates) {
this.conn = conn;
this.pathTemplates = pathTemplates;
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PathTemplateMatch match = pathTemplates
.matchedTemplate(request);
try {
/* retrieve 'who' query parameter */
String who = match.parameters().get("who");
who = null == who ? "anonymous" : who;
/* execute database query */
PreparedStatement ps = conn
.prepareStatement("select sys_context('USERENV','CURRENT_USER') from dual");
ResultSet rs = ps.executeQuery();
rs.next();
/* determine the database user */
String user = rs.getString(1);
/* Print the greeting */
response.getWriter().println(user + " says hello to: " + who);
rs.close();
ps.close();
} catch (SQLException e) {
throw new ServletException(e);
}
}
private final Connection conn;
private final PathTemplates pathTemplates;
}Following is an explaination for the preceding code snippet:
-
The
PluginDemoclass is annotated with:-
@Providesannotation, signifying that offers a service, in this scenario, theHttpServletservice. -
@Dispatchesand@PathTemplateannotations, which defines the URL pattern that the servlet responds to.
-
-
The PluginDemo constructor is annotated with
@Inject, indicating that it should be used by the dependency injection framework that this constructor should be used to create instances of the class. - The parameters of the constructor specify the following
dependencies:
- The first parameter indicates that a database connection is required, ORDS assigns this connection to a specific database schema, according to the mapping rules of the request URL.
- The second parameter is the PathTemplates service, which enables the servlet to retrieve the specific PathTemplate associated with the current request.
- The
PluginDemo:-
Overrides the
doGet()method to indicate that it supports theGETHTTP method. - Uses the PathTemplates service to determine the
PathTemplateMatchbound to the request. - Then decodes the template parameters, extracting the value of
the
whoparameter. - Connection instance is queried to determine the identity of the
current database user.
In response a message is displayed to indicate that the database user and the value of the who parameter is printed in the response.
-
Parent topic: Java Plugin Demonstration
16.3.1.3 Building the Plugin
This section explains how to build the plugin.
plugin-demo folder type the following
command:$ antThe source code is compiled and packaged
into an archive named built/plugin-demo.jar.
Parent topic: Java Plugin Demonstration
16.3.1.4 Packaging the Plugin
This section explains how to package the plugin.
plugin-demo.jar to the extension
library:$ cp built/plugin-demo.jar ${ORDS_HOME}/lib/ext/Parent topic: Java Plugin Demonstration
16.3.1.5 Testing the Plugin
This section explains how to test the plugin.
To test the plugin:
.$ cd ${ORDS_HOME}/bin
.$ cd ${ORDS_HOME}/bin
$ ords --config config_dir serveParent topic: Java Plugin Demonstration
16.3.2 Analyzing the Request URLs
This section explains how ORDS analyzes and maps the request URL to the database pool and schema.
The plugin we have developed requires a database connection to operate. Therefore, ORDS must determine the correct database and schema to connect to. ORDS accomplishes this, by analyzing the request URL and then mapping it to the corresponding database pool and schema.
If ORDS cannot determine a mapping, then it returns a 404 Not
Found status, for the request URL.
Parent topic: Plugin Examples
16.3.3 Trying the Request URL
To try the request URL:
http://localhost:8080/ords/hr/demos/plugin?who=ScottThe
browser should display the following
text:hr says hello to: Scott- The
/hrportion of the request URL maps the request to thehrdatabase schema. - A Connection instance connected to the
hrschema is injected into the PluginDemo instance. - PluginDemo queries the connection to determine the current user and
decodes the
whoparameter bound to the request URL, and uses this information to construct the message displayed.
Parent topic: Plugin Examples
16.3.4 Java Examples
The following sections lists some Java examples.
- Hello World Example
This section shows the Hello World example. - Injecting Dependencies
This section provides a sample code snippet showing how plugin can specify its dependencies on external APIs.
Parent topic: Plugin Examples
16.3.4.1 Hello World Example
This section shows the Hello World example.
The following Hello World example demonstrates the basics of creating a request handler plugin:
@Dispatches(@PathTemplate("/hello"))
@Provides
public class HelloWorld extends HttpServlet {
public doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
response.getWriter().println("Hello World");
}
}- Create a class that sub-classes HttpServlet.
- Advertise the class to the Dependency Injection framework through
the
@Providesannotation - Advertise the class to the request dispatching framework through
the
@Dispatchesannotation - Advertise the request path that the class responds to through the
@PathTemplateannotation - Override the
HttpServletdoGet ()method to provide the logic of the handler.
Parent topic: Java Examples
16.3.4.2 Injecting Dependencies
This section provides a sample code snippet showing how plugin can specify its dependencies on external APIs.
A plugin can specify its dependencies on external APIs using the
@Inject annotation on its constructor.
UsesLogging.java
@Dispatches(@PathTemplate("/uses-logging"))
@Provides
public class UsesLogging extends HttpServlet {
@Inject
public UsesLogging(Logger log) {
this.log = log;
}
public doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
log.fine("received request:\n" + request.toString());
response.setContentType("text/plain");
response.getWriter().println("Hello World");
log.fine("processed request");
}
private final Logger log;- Before instantiating the UsesLogging type, the DI framework looks
for a constructor annotated with
@Inject. It then examines the arguments of the constructor and attempts to resolve an implementation of the specified type. Once all dependencies have been resolved, the DI framework invokes the constructor with the required arguments. - In this case, the servlet uses the
Loggerservice to log some debugging information. - The set of services that can be injected is documented in the
AvailableDependenciesenum.
Parent topic: Java Examples
16.3.5 Javascript Plugin Demonstration
This section describes how to create a plugin using Javascript.
Parent topic: Plugin Examples
16.3.5.1 Plugin Javascript
ORDS provides a JavaScript as a service framework for customers to define
a JavaScript that can be executed in the ORDS instance on request. This is similar
to the conventional RESTful services concept used to develop the applications. The
framework is based on the module, template, and handler architecture. See Developing Oracle REST Data Services Applications. Rather than defining the modules, templates, and handlers in the
database, they are specified in an XML representation that is read from
lib/ext/ directory as a plugin.
plugin-javascript
example and the source can be found in the
examples/plugins/plugin-javascript directory. This section
describes the key elements of the plugin.
Note:
GraalVM with JS component is required for JavaScript plugin ORDS feature to work.The example contains a number of inline and external definitions for JavaScript source. References to external JavaScript source are to the files that are found in the classpath.
| File | Description |
|---|---|
build.xml |
The ant build project.
|
src/js/example.js |
An example external JavaScript file. External here means, not defined in, but referred to from, the XML Resource Module file. |
src/META-INF/manifest.json |
A plugin configuration metadata file that ORDS reads at startup to register XML Resource Modules. |
src/META-ING/modules/javascript.xml |
An XML Resource Module file that defines an example module with a number of templates and handlers. |
- Change the directory to
examples/plugins/plugin-javascript. - Run
antto buildexamples/plugins/plugin-javascript/built/plugin-javascript.jarfile. - Copy the
plugin-javascript.jarfile to the ORDS distributionlib/extdirectory and start the ORDS instance using a supported GraalVM with JS component. - Invoke the defined handlers using the URL pattern:
http://server/ords/javascript-examples/{template pattern}.- For example:
http://localhost:8080/ords/javascript-examples/nowwhere the current time is returned.Note:
Unlike the ORDS REST Services, the JavaScript as a service implementation does not require or use a database connection.
- For example:
- Example Services Purpose and Use
This section provides the information on the purpose and use of the example services. - Embedding Graal JavaScript Component
Parent topic: Javascript Plugin Demonstration
16.3.5.1.1 Example Services Purpose and Use
This section provides the information on the purpose and use of the example services.
| Purpose | Request | Action | Response |
|---|---|---|---|
An example of inline Javascript that returns the current UTC time
as application/json.
|
/ords/javascript-examples/now |
GET |
{ "now":"2023-08-31T16:08:55.471Z" } |
| An example of inline Javascript that accepts a parameter. | /ords/javascript-examples/future?days=7 |
GET |
{ "now":"2023-08-31T16:08:55.471Z",
"future":"2023-09-07T16:08:55.471Z", "days":7 } |
| An example of inline Javascript that accepts various parameters from different sources. |
|
GET |
|
| An example of external Javascript file that accepts a parameter. | /ords/javascript-examples/fibonacci?length=50 |
GET | {fib: 12586269025} |
An example of inline Javascript that uses implicit parameters
content_type and body_text for
getting the request values as well as using
ords_response to invoke
setStatus and setContentType
on HttpServletResponse.
|
|
POST |
|
Parent topic: Plugin Javascript
16.3.5.1.2 Embedding Graal JavaScript Component
The JavaScript component must be embedded as a plugin to be able to run JavaScript as a guest language in ORDS that is running in GraalVM for JDK version 21.
- GraalVM Polyglot API
- JavaScript language
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
<version>${graalvm.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<!-- Language: js -->
<artifactId>js</artifactId>
<version>${graalvm.version}</version>
<type>pom</type>
</dependency>Refer to section, Embedding Languages in the GraalVM reference manual for more
information about dependency setup to embed languages. Once the required artifacts have
be dowloaded, place them in lib/ext/ directory to be included in the
classpath at runtime.
See Also:
Embedding LanguagesParent topic: Plugin Javascript
16.4 Route Patterns
A Route Pattern defines a format used to match specific HTTP request paths. The pattern is matched against the path component of the request URI.
- Example
This section provides an example for route pattern. - Purpose of Route Patterns
This section is intended to clarify the purpose of the route pattern syntax. It serves as a recommended guideline rather than a standard.
Parent topic: Extending ORDS Functionality with Plugins
16.4.1 Example
This section provides an example for route pattern.
/objects/:object/:id?
/objects/emp/101: Matches a request for the item in theempresource with id101./objects/emp/: Matches a request for the emp resource, because the:idparameter is annotated with the?modifier which indicates that theidparameter is optional.
Parent topic: Route Patterns
16.4.2 Purpose of Route Patterns
This section is intended to clarify the purpose of the route pattern syntax. It serves as a recommended guideline rather than a standard.
The syntax of route patterns is similar to and is inspired by the pattern routing syntax found in a number of web frameworks, including:
- Angular Routes
- Ruby on Rails Routing
Route patterns address the need to create a formal definition of the ad-hoc pattern syntax that these and similar frameworks have popularised.
The goal of Route Patterns is to ensure that it is not possible to define a suite of route patterns that are ambiguous, for any given request path only one or zero route patterns can be chosen to match against the path. As a result, the route pattern syntax may be less flexible or expressive than the ad-hoc syntaxes used in the frameworks.
This is a conscious design trade-off. In the ad-hoc syntaxes, any ambiguity is resolved by the order in which the patterns are declared, the first declared pattern is tested first, the second declared pattern is tested second and so on. Developers can order the pattern declarations to ensure that more specific patterns are tested before less specific patterns. This requires one central code location where routes are declared and requires careful ordering of the patterns to avoid errors. These requirements may not scale to larger applications where many developers are defining route patterns, and may not be fully aware of conflicting or overlapping route patterns, or to the applications where route patterns need to be defined in many different locations (for example: In a pluggable architecture).
The route pattern syntax is also somewhat similar to the URI template syntax, but the applications of URI templates and route patterns differ. URI templates focus on forming concrete URIs from a template, Route Patterns focus on decomposing the path portion of a URI into its component parts.
Parent topic: Route Patterns
16.5 Route Pattern Syntax Rules
This section describes the route pattern syntax rules.
A route pattern is a string of printable unicode characters that contains zero or more embedded variable expressions. An expression can be a named parameter, delimited by a leading colon (:) and a trailing slash (/), or end of string, or an expression can be a glob parameter indicated by the wildcard character (*). A pattern that contains one or more named parameters is termed as a named pattern. A pattern that contains a glob parameter is termed as a glob pattern. A pattern must not contain a mixture of named patterns and glob expressions. A pattern lacking any variable expressions is termed as a literal pattern.
- Path Separator
This section describes the path separator. - Reserved Characters
This section describes the reserved characters. - Literal Values
The characters outside of expressions and path separators in a route pattern are termed as literal values. - Named Parameters
This section describes the named parameters.
Parent topic: Extending ORDS Functionality with Plugins
16.5.1 Path Separator
This section describes the path separator.
The slash (/) character delimits the pattern into path segments. A path separator must not be followed by another path separator. The leading path separator in a route pattern is implied and can be omitted.
Examples
- The patterns
a/band/a/bare equivalent - The patterns
*and/*are equivalent - The patterns
a/banda/b/are not equivalent, the trailing path separator is significant and cannot be ignored
Parent topic: Route Pattern Syntax Rules
16.5.2 Reserved Characters
This section describes the reserved characters.
reserved = gen-delims / sub-delimsgen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
Parent topic: Route Pattern Syntax Rules
16.5.3 Literal Values
The characters outside of expressions and path separators in a route pattern are termed as literal values.
Literal values can contain any printable unicode character except the reserved characters.
Parent topic: Route Pattern Syntax Rules
16.5.4 Named Parameters
This section describes the named parameters.
The start of a named parameter is indicated by the colon character (‘:’). The end of a named parameter is indicated by a path separator or the end of string. The named pattern can be suffixed with a modifier. A given parameter name must only appear once in each route pattern. A route pattern can have zero or more named patterns.
Example:
named-expression-pattern = *(literal / path-separator / named-expression )
valid-name = [a-zA-Z0-9] / '-' / '_'
char = [a-zA-Z]
name = char valid-name*
param-decl = name ('*' / '?' )
named-expression = ':' param-decl path-separator /
':' param-decl <eos>- Modifiers
This section describes the modifiers.
Parent topic: Route Pattern Syntax Rules
16.5.4.1 Modifiers
This section describes the modifiers.
A modifier modifies the matching behavior of a named parameter. Only a single named parameter in a route pattern can contain a modifier and it must be the last named parameter in the pattern. A modifier is suffixed at the end of a named parameter expression.
- Eager Modifier
This section describes the eager modifier. - Optional Modifier
This section describes the optional modifier. - Compound Named Parameter
This section describes the compound named parameter. - Glob Parameter
This section describes glob parameter.
Parent topic: Named Parameters
16.5.4.1.1 Eager Modifier
This section describes the eager modifier.
The eager modifier is indicated by the asterisk character (‘*’) and instructs the matcher to eagerly consume all characters matching the named pattern including the path separator character through the end of the string.
Example
/foo/:all-children*This pattern matches the following paths:
/foo/bar: all-children is associated tobar/foo/bar/: all-children is associated tobar, the eager modifier consumes all characters including the path separator/foo/bar/baz: all-children is associated tobar/baz, the eager modifier consumes all characters to the end of the string
The eager modifier must match at least one character, so the preceding pattern does not match the following path:
/foo/: matching this path would require all-children to
be associated to the empty string, which is not permitted.
Parent topic: Modifiers
16.5.4.1.2 Optional Modifier
This section describes the optional modifier.
The optional modifier is indicated by the question mark character (?) and instructs the pattern matcher that the Named Pattern matches zero or more characters until the end of string is reached.
Example
/foo/:item?
This pattern matches the following paths:
/foo/bar: item is associated tobar/foo/: item is associated to the empty string, the optional modifier causes the named parameter to match the zero length string
Parent topic: Modifiers
16.5.4.1.3 Compound Named Parameter
This section describes the compound named parameter.
A compound named parameter is a named parameter where the matching text in the request path is decomposed into named components. Each component is delimited by the comma character (,). A compound named parameter can have an optional modifier, but must not have an eager modifier.
Example:
/line-items/:order_id,item_id/detailParent topic: Modifiers
16.5.4.1.4 Glob Parameter
This section describes glob parameter.
glob-pattern = *(literal / path-separator / ) / path-separator '*'A
glob parameter matches zero or more characters until the end of the
string.- /*: Matches all paths
- /foo/*: Matches all paths starting with
/foo/including the/foo/path.
Parent topic: Modifiers
16.6 Pattern Matching Rules
This section describes the matching rules of the route pattern.
- Path Separator
- Literal Value
- Named Parameter
- Named Compound Parameter
- Glob Parameter
A route pattern is matched against the URL-encoded form of a request path, with each token matching its corresponding segment of the request path.
The tokens are matched from left to right order, the first token matching the left-most segment of the request path, the second token matching the next left most segment and so on. The rules for matching each token type are defined in the following sections.
- Path Separator Matching
This section describes the path separator matching rule. - Literal Value Matching
This section describes the literal value matching rule. - Named Parameter Matching
This section describes the named parameter matching, - Compound Named Parameter Matching
This section describes the compound named parameter rules. - Glob Parameter Matching
This section describes the glob parameter matching token.
Parent topic: Extending ORDS Functionality with Plugins
16.6.1 Path Separator Matching
This section describes the path separator matching rule.
Each path separator token must match exactly one ‘/’ character in the request path. A
Path separator must not match the URL encoded form of the ‘/’ character. That is, it
must not match the octets: %2F or the octets: %2f.
Since the leading path separator in a route pattern is optional, the leading path
separator in a request path is also optional and can be omitted.
Examples:
- The pattern
/a/bmatches the request paths:a/band/a/b - The equivalent pattern
a/balso matchesa/band/a/b - The pattern
/a/bdoes not match the request paths:a%2Fbor%2fa%2Fb
Parent topic: Pattern Matching Rules
16.6.2 Literal Value Matching
This section describes the literal value matching rule.
Each literal value token must match the exact same characters in the request path. Each literal value must be an URL encoded and compared to the URL encoded request path.
Examples
a/b matches the following request paths:
a/b/a/b/%61/%62 – ‘%61’is the percent encoded form of the ‘a’ character, ‘%62’ is the percent encoded form of the ‘b’ character.
Parent topic: Pattern Matching Rules
16.6.3 Named Parameter Matching
This section describes the named parameter matching,
A named parameter token matches one or more characters until the next occurrence of a path separator or end of the string.
Optional Modifier Matching
If a named parameter has an optional modifier, then it matches zero or more characters up to the end of the string.
Eager Modifier Matching
If a named parameter has an eager modifier, then it matches all characters up to the end of the string.
Examples
The pattern /test/:item matches the following paths:
test/101/test/true%2Ffalse/test/a,b,c
The pattern does not match the following paths:
/test/101/: extra trailing slash/test/: named parameter must match at least one character
Parent topic: Pattern Matching Rules
16.6.4 Compound Named Parameter Matching
This section describes the compound named parameter rules.
A compound named parameter token matches one or more characters up to the next
occurrence of a path separator or end of string, where the matched characters are
further delimited by the comma (‘,’) character. If the compound named parameters have
N components, then there must be at most N-1
commas in the matched text. If there are more than N-1 comma characters
(that is, N+1), then there must not be a match. Trailing comma
characters can be omitted in the matched request path.
Component values in the request path that must contain the comma character must use the percent encoded form of the comma character (%2C)
- Optional Modifier Matching
This section describes optional modifier matching
Parent topic: Pattern Matching Rules
16.6.4.1 Optional Modifier Matching
This section describes optional modifier matching
If a compound named parameter has an optional modifier, then it matches zero or more characters up to the end of the string.
Examples
-
The pattern
/line-items/:order_id,item_id/detailmatches the following paths:/line-items/101,493/detail – order_idis bound to 101,item_idis bound to 493/line-items/101,/detail– order_idis bound to 101,item_idis bound tonull/line-items/,493/detail – order_idis bound tonull,item_idis bound to 493/line-items/,/detail – order_idis bound tonull, item_idis bound to null
-
The pattern
/line-items/:order_id,item_id,category_id/detail/categorymatches the following paths:/line-items/101,493,14/detail/category – order_idis bound to 101,item_idis bound to 493, category is bound to 14/line-items/101,/detail/category – order_idis bound to 101,item_idis bound tonull, category is bound tonull/line-items/,493/detail/category – order_idis bound tonull,item_idis bound to 493, category is bound tonull/line-items/,,493/detail/category – order_idis bound tonull,item_idis bound tonull, category is bound to 493/line-items/,/detail/category – order_idis bound tonull,item_idis bound tonull, category is bound tonull-
Trailing comma separators may be omitted so the following path is also matched
: /line-items/101/detail, order_idis bound to 101,item_idis bound tonull
/books/title,author,
then:
/books/So%20Long%2C%20and%20Thanks%20for%20All%20the%20Fish,Douglas%20Adamsmatches, the comma character ias it is percent encoded/books/Eats,%20Shoots%20%26%20Leaves,Lynne%20Trussthere are two comma characters in the matched range, since only one comma character was expected, the match fails.
Parent topic: Compound Named Parameter Matching
16.6.5 Glob Parameter Matching
This section describes the glob parameter matching token.
A glob parameter token matches zero or more characters up to the end of the string.
Example
The pattern /foo/* matches the following paths:
/foo/matches the empty string/foo/barmatchesbar/foo/bar/matchesbar//foo/bar/bazmatchesbar/baz
Parent topic: Pattern Matching Rules
16.7 Route Pattern Sets
A collection of route patterns is termed a route pattern set.
A route attern Set must be unambiguous, meaning that for a given request path it should be possible to choose at most one route pattern from the set to match the request path. route patterns must be ordered within the route pattern Set from most specific pattern to least specific pattern. Matching a request path against a route pattern set must be performed in order from the most specific pattern to the least specific pattern. Matching stops at the first route pattern that matches.
- Equivalent and Overlapping Patterns
This section describes the equvalent and overlapping patterns. - Token Precedence
This section describes an approach to determine the precedence order of the route patterns.
Parent topic: Extending ORDS Functionality with Plugins
16.7.1 Equivalent and Overlapping Patterns
This section describes the equvalent and overlapping patterns.
Equivalent or overlapping route patterns must not occur in the same route pattern Set.
Equivalent Patterns
Named patterns are equivalent if the only difference between the patterns is the names assigned to parameters.
Example
/a/:b/a/:c
Overlapping Patterns
Overlapping patterns are route patterns where, for a subset of request paths, more than one route pattern matches, and the token precedence ordering described below does not resolve which route pattern should be selected.
Overlapping Modifiers
A route pattern set must not contain two or more named patterns that differ only in the use of a modifier.
Example
The following three patterns are not permitted in the same route pattern set because the only difference is the modifier assigned to the named parameter:
/a/:b/a/:b?/a/:b*
Overlapping Literal and Glob Pattern
An optional named pattern must not overlap with a literal pattern within the same route pattern set.
Example
Following is the route pattern set:
/a/b/c/d/c/d/a/1/a/b/c/d/e/
/c/d/a/1/c/d/b/a/b/c/d/e//a
Parent topic: Route Pattern Sets
16.7.2 Token Precedence
This section describes an approach to determine the precedence order of the route patterns.
The different token types are assigned a precedence order, from most specific to least specific, to enable a deterministic sorting of a route pattern set.
Literal Values and Path Separators
Literal values and path separators have the highest precedence because they require an exact match. Literal values are ordered in reverse lexicographical order, ensuring that longer literal tokens are tested before the shorter ones.
Compound Named Parameters
A compound named parameter has second highest precedence, as the requirement to match the comma characters within the matching value makes it more specific than a named parameter.
Optional Compound Name Parameters
An optional compound named parameter has third highest precedence, it is less specific than a compound named parameter because it can match an empty string.
Named Parameters
A named parameter has fourth highest precedence, matching one or more characters until the next path separator or end of the string.
Optional Named Parameters
An optional named parameter has the fifth highest precedence and matches zero or more characters, excluding the path separator, up to the end of the string.
Eager Named Parameters
An eager named parameter has the sixth highest precedence and matches one or more characters, including the path separator, up to the end of the string.
Glob Parameters
Glob parameters have the lowest precedence, as they are the least specific patterns and match zero or more characters up to the end of the string.
Examples
Given the following Route Pattern Set:
/*/foo/*/a/:p1/a/:p1/c/:p1/b/c/b/:p1?/b/c/:p1*/a/:p1/c/:p2
/foo/*/b/c/:p1*/b/:p1?/a/:p1/c/:p2/a/:p1/c/a/:p1/:p1/b/c/*
Example Implementation of Ordering
Note:
This section is non-normative.One approach for implementing the specified route pattern ordering is to convert each pattern into a canonical string representation, then sort these canonical strings in reverse lexicographical order. This is achieved through replacing each different parameter token in the pattern with a single low value character. with the lowest precedence pattern getting the lowest value character, and the highest precedence getting the highest value character as outlined in the following list:
- Glob -> ‘!’
- Eager Named -> ‘#’
- Optional Named -> ‘$’
- Named -> ‘'’
- Optional Compound -> ‘(’
- Compound -> ‘)’
/foo/!/b/c/#/b/:$/a/'/c/'/a/'/c/a/'/'/b/c/!
The substitute characters are part of the reserved character set, ensuring they do not conflict with any literal tokens and thus eliminate any ambiguity between patterns.
Parent topic: Route Pattern Sets