# Introduction

This document describes how to develop and deploy plugins that integrate with the Oracle REST Data Services (ORDS) runtime.

## Further Information

For a more a more detailed reference, consult the developer guide.

## Examples

Below are some examples that give a flavour of the programming model.

### Hello World

The Hello World example below 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 via the @Provides annotation
- Advertise the class to the request dispatching framework via the @Dispatches annotation
- Advertise the request path that the class responds to via the @PathTemplate annotation
- Override the HttpServlet doGet () method to provide the logic of the handler.

### Injecting Dependencies

A plugin can express it’s dependencies on external APIs using the @Inject annotation on its constructor.

    @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 Logger service to log some debugging information.
- The set of services that can be injected is documented in the AvailableDependencies enum.

# Tutorial

## About the Tutorial Files

All of the files referenced in this tutorial are included in the product distribution under the `examples/plugins` folder:

    ${ORDS_HOME}
    |-- examples
         |-- plugins
              |-- lib
              |-- plugin-demo
                  |-- src

where `${ORDS_HOME}` is the location where the product distribution was unzipped.

## Prepare Environment

Consult the instructions in the Oracle REST Services Documentation to install and configure ORDS.

### Enable database schema

To make a database schema accessible to ORDS, it must be explicitly enabled. To demonstrate this we will create a new schema and enable it:

    $ sqlplus /nolog
    SQL> CONNECT SYS as SYSDBA
    Enter password: SYS_password
    SQL> create user TEST_SCHEMA identified by TEST_SCHEMA_password;
    SQL> grant connect, resource to TEST_SCHEMA;
    SQL> DISCONNECT
    SQL> CONNECT TEST_SCHEMA
    Enter password: TEST_SCHEMA_password
    SQL> execute ords.enable_schema;
    SQL> commit;

Note: Replace `TEST_SCHEMA_password` with your own choice of password.

#### Verify schema is enabled

To verify if a schema is enabled for ORDS access, check the `USER_ORDS_SCHEMAS` view:

    SQL> select * from USER_ORDS_SCHEMAS;

    ID PARSING_SCHEMA TYPE      PATTERN
    -- -------------- --------- -----------
     2 TEST_SCHEMA    BASE_PATH test_schema

## Creating a Plugin

In this guide we will walk through building and deploying the `plugin-demo` plugin that queries the database to determine the current database user and echo that information in the response. This example uses Apache ANT to manage the build process.

### Prerequisities

- JDK 1.8 or later.
- Apache Ant 1.8.2 or later.
- Installed and configured ORDS as detailed above.

### About the Plugin project folder structure

The `plugin-demo` source files are located within the `${ORDS_HOME}` folder at:

    ${ORDS_HOME}/examples/plugins/plugin-demo

The folder contains the following:

- `src` folder which contains:
  - `PluginDemo.java` - the Java source code of the plugin.
- `build.xml` - ANT build script, which compiles and packages the source code
- `built` - folder generated by `build.xml` which will contain the packaged plugin: `plugin-demo.jar`.

Located in the parent folder is a `lib/` folder which holds the `.jar` files required to compile plugins.

#### Required Libraries

To compile a plugin the following libraries must be in the classpath:

- plugin-api.jar
- plugin-apt.jar
- javax.inject.jar
- servlet-api-3.1.0.jar
- ojdbc11.jar

##### Required Library Locations

Each of the required jars is included in the product distribution in the `${ORDS_HOME}/examples/plugins/lib` folder.

##### 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 classes annotated with @Provides discoverable.

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

### About PluginDemo.java

The source code of the plugin is reproduced below, following that is a point by point explanation of the source code.

    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;
    }

- The `PluginDemo` type is annotated with `@Provides` to indicate that it offers a service, in this case it offers the `HttpServlet` service.
- The type is also annotated with the `@Dispatches` and `@PathTemplate` annotations which indicate the request URL pattern that the servlet listens for.
- The `PluginDemo` constructor is annotated with `@Inject` to indicate that this constructor should be used by the dependency injection framework to instantiate instances of the type.
- The arguments of the constructor indicate the types that `PluginDemo` depends on:
  - The first argument indicates that a database `Connection` is required, ORDS connects this connection instance to a database schema, which schema is determined by the request URL to database schema mapping rules.
  - The second argument is the `PathTemplates` service which provides a method for determining the `PathTemplate` bound to a request.
- `PluginDemo` overrides the `doGet()` method to indicate that it supports the `GET` HTTP method.
- It uses the `PathTemplates` service to determine the `PathTemplateMatch` bound to the request.
- It decodes the `PathTemplate` parameters, and extracts the value of the `who` parameter.
- The `Connection` instance is queried to determine the identity of the current database user.
- Finally the message showing the database user and the value of the `who` parameter is printed in the response.

### Build the Plugin

In the `plugin-demo` folder type the following command:

    $ ant

The source code will be compiled and packaged into an archive named `built/plugin-demo.jar`

### Package the Plugin

Copy plugin-demo.jar to extension library:

    $  cp built/plugin-demo.jar ${ORDS_HOME}/lib/ext/

# Test the Plugin

Start ords in standalone mode.

    $ cd ${ORDS_HOME}/bin
    $ ords --config config_dir serve

## About Request URLs

The plugin we have built needs a database connection to function. Therefore ORDS must be able to figure out what database and schema to connect to. ORDS does this by examine the Request URL and mapping the URL to the appropriate database pool and database schema.

If ORDS cannot determine a mapping, then it will report a `404 Not Found` status for the request URL.

### Configuring a database schema mapping

To map a database schema to a URL, you must enable the schema to be accessed by ORDS, as we did in the section titled ‘Enable Database Schema’.

## Try the Request URL

With ORDS running in standalone mode on `localhost`, try the following URL:

    http://localhost:8080/ords/test_schema/demos/plugin?who=Scott

The browser should display the following text:

    TEST_SCHEMA says hello to: Scott

- The `/test_schema` portion of the request URL maps the request to the `TEST_SCHEMA` database schema.
- A `Connection` instance connected to the `TEST_SCHEMA` schema is injected into the `PluginDemo` instance.
- `PluginDemo` queries the `Connection` to determine the current user and decodes the `{who}` parameter bound to the request URL, and uses this information to construct the message displayed above.