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

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

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
          |-- plugin-echo-cmd
              |-- 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

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:

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:

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 javax.inject.jar

This library provides the JSR-330 API types such as @Inject.

About servlet-api-2.5.jar

This library provides the Java Servlet 2.5 API types, such as HttpServlet.

About ojdbc6.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.

import java.io.IOException;
import java.sql.*;
import javax.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;
}

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 in the Web Application Archive (WAR)

Update the ords.war archive to add plugin-demo.jar as follows:

Invoke the plugin command to package plugin-demo.jar into ords.war:

$ java -jar ../../../ords.war plugin built/plugin-demo.jar

Test the Plugin

Start the updated ords.war in standalone mode.

$ cd ${ORDS_HOME}
$ java -jar ords.war

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