Monday, May 18, 2015

JBoss' apiman's API Manager REST services API

In this, the third article in our series on apiman, JBoss' new open source API Management framework, we'll examine apiman’s API Manager REST services API. apiman’s Management UI utilizes this API in the implementation for all of its user-visible features, and you can also use the same API to automate tasks with apiman.

Introduction

It's inevitable that, after you work with a product's UI for a while that you encounter tasks that are better suited to a scripting or batch interface. For example, if you have to perform a similar task for a large of related data items, the time that it can require to perform these tasks through an interactive UI can be prohibitive. Also, it's easy for repetitive tasks to become error prone as you can lose focus, even if you are working in a well designed and easy to use interface such as apiman.

One solution to this problem is to augment the UI with a command line or scripting interface. This can lead to a whole separate set of issues if the new interface is built on a different set of underlying routines than the UI. A better approach to allow access to the same routines in which the UI is constructed. This approach removes any duplication, and also enables you to replicate manual UI based tasks with automated or scripted tools.  

JBoss apiman follows this second approach with its REST interface.  All the services provided by apiman in its Management UI are directly supported in the API Manager REST services API. You can also directly access these same services through the REST API.

Prerequisites

Like a lot of things with JBoss open source software, installing the REST API is easy. In fact, if you have apiman installed, then you already have the REST API installed. (You can’t get much easier than that!)

We covered installing apiman in the first article in this series (see: http://java.dzone.com/articles/impatient-new-users), so we won't repeat that information here. A minimal set of install instructions are always available on the apiman site (http://www.apiman.io/latest/). The current set of instructions as of this writing are:

 mkdir ~/apiman-1.1.2.Final  
 cd ~/apiman-1.1.2.Final  
 wget http://download.jboss.org/wildfly/8.2.0.Final/wildfly-8.2.0.Final.zip  
 wget http://downloads.jboss.org/overlord/apiman/1.1.2.Final/apiman-distro-wildfly8-1.1.2.Final-overlay.zip  
 unzip wildfly-8.2.0.Final.zip  
 unzip -o apiman-distro-wildfly8-1.1.2.Final-overlay.zip -d wildfly-8.2.0.Final  
 cd wildfly-8.2.0.Final  
 ./bin/standalone.sh -c standalone-apiman.xml  


Now that our apiman server is up and running, we can start to access the API Manager REST services API. However, before we start placing calls to services through the REST API, let’s take a look at how the API is organized.

The Organization of the API

The documentation for the apiman REST API is available (for free, of course), here: http://www.apiman.io/latest/api-manager-restdocs.html

The services and their endpoints represented in the API are divided into these groups:


Within each group, GET operations are defined to return information, and GET and POST operations are defined to make changes to apiman elements. Data passed to and returned from services through the API is in the form of JSON along with a return code.

Accessing Services in the API Manager REST services API

The best way to learn about the API Manager REST services API is to see it in action. Since these are REST services, it’s easy to access them. For example, we should be able to access the system status service with a simple GET operation at this endpoint:


Let’s try this with curl. If we execute the following command, we should see the current system status:


Well, that error is not exactly what we expected. There was no output.

What went wrong? What’s missing? The answer is that our call to the service was missing authorization. In the same way that a user must login and be authorized to use the apiman Management UI, calls to the REST API must be authorized.

For our example, we’ll keep things simple and stick to using basic authorization. (We’ll take a more extensive look at apiman and security in a later article in this series.)

In order to resolve this failure, we have to send a properly encoded basic authorization header with every request you make. In Java8, we can generate a key by base64 encoding this string for the OOTB admin username and password: admin:admin123! with this statememt:

base64encode(concat('admin', ':', 'admin123!'))

This yields a key with a value of: YWRtaW46YWRtaW4xMjMh

Now, let’s try that curl command again, but this time, we’ll include the key in the authorization header:

$ curl -H "Authorization: Basic YWRtaW46YWRtaW4xMjMh"

And the result is:

{"up":true,"version":"1.1.2-SNAPSHOT"}

As we mentioned earlier in this article, the responses returned by calls to the REST API are in in the form of JSON. In the case of our call to the http://localhost:8080/apiman/system/status endpoint, the expected format of the response is:

{
    version:string
    up:boolean
}

Let’s carry on by looking at a more extensive example, where we use the REST API to automate a task that would be tedious if performed in the apiman Management UI.

A Larger Example

Let’s say that you want to create multiple new organizations. You could of course manually enter these into the apiman Management UI. But, if you have a large number of organizations, for example, one for each of the countries in the EU, or each of the states in the USA, this would be a tedious and error prone task. This is an ideal candidate task for automation with the REST API.

The coding for this example is simple. All you have to do is account for the encoding of the authorization to access the API Manager REST services API, and pass the information related to each organization that you create to this endpoint: http://localhost:8080/apiman/organizations

Here’s an example program with an ice hockey flavor - the highlights are noted below:

1:  package apimanExample; 
2:  import java.io.IOException;  
3:  import java.io.OutputStreamWriter;  
4:  import java.net.HttpURLConnection;  
5:  import java.net.URL;  
6:  import java.nio.charset.StandardCharsets;  
7:  import java.util.Base64;  

8:  public class SimplePutOrg {  

9:    public static void main(String[] args) throws Exception {  

10:     String [] [] originalSix = {  
11:         { "Boston", "Chicago", "Detroit", "Montreal", "New York", "Toronto"},  
12:         { "Bruins", "BlackHawks", "Red Wings", "Canadiens", "Rangers", "Maple Leafs"}  
13:     };  
14:     for (int i = 0; i < 6; i++) {  
15:       System.out.println ("Creating new apiman org for: " + originalSix [0][i] + ":" + originalSix [1][i]);  
16:       System.out.println ("Return code = " + createNewOrg (originalSix [0][i], originalSix [1][i]));  
17:     }  
18:    }  

19:    private static int createNewOrg (String newOrgName, String newOrgDescription) throws IOException {  
20:      URL url = new URL("http://localhost:8080/apiman/organizations");  
21:      HttpURLConnection connection = (HttpURLConnection) url.openConnection();  

22:      Base64.Encoder encoder = Base64.getEncoder();  
23:      String normalString = "admin:admin123!";  
24:      String encodedString = encoder.encodeToString(normalString.getBytes(StandardCharsets.UTF_8));  

25:      connection.setRequestProperty("Authorization", "Basic "  + encodedString);  
26:      connection.setRequestMethod("POST");  
27:      connection.setDoOutput(true);  
28:      connection.setRequestProperty("Content-Type", "application/json");  
29:      connection.setRequestProperty("Accept", "application/json");  

30:      OutputStreamWriter osw = new OutputStreamWriter(connection.getOutputStream());  
31:      osw.write(String.format("{\"name\":\"" + newOrgName + " a new org\",\"description\":\"" + newOrgDescription + "\"}"));  
32:      osw.flush();  
33:      osw.close();  

34:      return connection.getResponseCode();     
35:    }  
36:  }  

Code highlights:
  • Lines 10-13: Here's where we define the test data. For this example, we'll use the National Hockey League's "original 6" teams. 
  • Line 20: Here's the URL for the service to which we'll connect to create the new organizations.
  • Lines 22-24 - Here's where we set up the encoding for the BASIC authorization.
  • Lines 25-29 - And here's where we create the HttpURLConnection that we will use to connect to the service.
  • Line 26: Note that we will be performing a POST operation. 
  • Lines 30-33 - And finally, here's where we connect to the service and send our POST requests to create the new organizations.


When we run this example, the following output indicates that the calls to the service through the REST API were successful, based on the return code of 200:

 Creating new apiman org for: Boston:Bruins  
 Return code = 200  
 Creating new apiman org for: Chicago:BlackHawks  
 Return code = 200  
 Creating new apiman org for: Detroit:Red Wings  
 Return code = 200  
 Creating new apiman org for: Montreal:Canadiens  
 Return code = 200  
 Creating new apiman org for: New York:Rangers  
 Return code = 200  
 Creating new apiman org for: Toronto:Maple Leafs  
 Return code = 200  

And - here’s the server output that tracks the creation of the new organizations:

 21:17:53,205 INFO [stdout] (default task-19) Created organization Boston a new org: OrganizationBean [id=Bostonaneworg, name=Boston a new org, description=Bruins, createdBy=admin, createdOn=Sun May 17 21:17:53 GMT-05:00 2015, modifiedBy=admin, modifiedOn=Sun May 17 21:17:53 GMT-05:00 2015]  
 21:17:53,233 INFO [stdout] (default task-21) Created organization Chicago a new org: OrganizationBean [id=Chicagoaneworg, name=Chicago a new org, description=BlackHawks, createdBy=admin, createdOn=Sun May 17 21:17:53 GMT-05:00 2015, modifiedBy=admin, modifiedOn=Sun May 17 21:17:53 GMT-05:00 2015]  
 21:17:53,253 INFO [stdout] (default task-23) Created organization Detroit a new org: OrganizationBean [id=Detroitaneworg, name=Detroit a new org, description=Red Wings, createdBy=admin, createdOn=Sun May 17 21:17:53 GMT-05:00 2015, modifiedBy=admin, modifiedOn=Sun May 17 21:17:53 GMT-05:00 2015]  
 21:17:53,275 INFO [stdout] (default task-25) Created organization Montreal a new org: OrganizationBean [id=Montrealaneworg, name=Montreal a new org, description=Canadiens, createdBy=admin, createdOn=Sun May 17 21:17:53 GMT-05:00 2015, modifiedBy=admin, modifiedOn=Sun May 17 21:17:53 GMT-05:00 2015]  
 21:17:53,295 INFO [stdout] (default task-27) Created organization New York a new org: OrganizationBean [id=NewYorkaneworg, name=New York a new org, description=Rangers, createdBy=admin, createdOn=Sun May 17 21:17:53 GMT-05:00 2015, modifiedBy=admin, modifiedOn=Sun May 17 21:17:53 GMT-05:00 2015]  
 21:17:53,316 INFO [stdout] (default task-29) Created organization Toronto a new org: OrganizationBean [id=Torontoaneworg, name=Toronto a new org, description=Maple Leafs, createdBy=admin, createdOn=Sun May 17 21:17:53 GMT-05:00 2015, modifiedBy=admin, modifiedOn=Sun May 17 21:17:53 GMT-05:00 2015]  

And finally, here are the organizations as displayed in the apiman Management UI:

orgs.png

(It’s been a tough year for Boston Bruins’ fans. It’s nice to see the team listed first, even if it’s just an alphabetic list.  ;-)

In Conclusion

The architecture of the apiman Management UI is that the UI is built on top of a REST API. This architecture makes it possible for you to directly access the services exposed by the API, and enables you to automate the tasks that you perform in the UI.

Author Acknowledgements

As always, the author thanks the members of the apiman team (especially Eric Wittman) for their input and for their efforts in creating apiman!



Monday, February 23, 2015

Customizing apiman Through Policy Plugins

This is the second in a series of articles exploring API management with JBoss apiman. The first article (http://java.dzone.com/articles/impatient-new-users) was a general introduction to apiman for impatient users where in only 10 minutes we installed apiman, created users and organizations, and services, policies, contracts, and applications. In this article, we'll take the first step toward customizing apiman by creating new plugins to implement service policies.

The major new feature added to release 1.0.2 of apiman (http://www.apiman.io/) is the ability to extend its features through a custom plugin framework. Since policies perform the most important apiman runtime operations, plugins that enable you to create new policies are the first type of plugins to be supported.

In this article, we'll examine all the steps that you must perform to create a new policy plugin, and then import it into apiman and configure a service to put it into use.

Prerequisites

For this article, we’ll assume that you have completed all the steps in the first article in this series. The first article guides even the most impatient user through installing and using apiman.

Accessing the Example Plugins

Like all JBoss software, apiman provides functioning example code that you can use as a starting point for your own plugins. The easiest way to create and package an apiman is as a standard maven project packaged as a .war file.

To download a copy of the example plugins, execute this git command:
 git clone https://github.com/apiman/apiman-plugins  

When the git clone operation completes, you’ll have the source code for multiple example plugins. The exact set of plugins that you have downloaded may change, depending on when you download them, as more examples are always being added. For the purposes of this article, we’ll focus on the “config-policy” example plugin. This plugin demonstrates how a plugin can be used to set the value of properties in a message being processed by a service. Specifically, this plugin defines properties in the request and response headers and enables you to set their values to “true.”

Requirements for a Plugin Implementation

The easiest way that an apiman policy plugin can be build and packaged is as a standard maven project packaged as a .war file. There are just a few modifications that are required to convert a .war into a plugin.

Let’s take a closer look at the files and directories that comprise the apiman config-policy plugin, and the changes that are needed to implement a plugin:

├---pom.xml
└---src
    └---main
       ├---apiman
       │   ├---plugin.json
       │   └---policyDefs
       │       ├---config-policyDef.json
       │       └---schemas
       │                └---config-policyDef.schema
       └---java
           └---io
               └---apiman
                   └---plugins
                       └---config_policy
                           ├---ConfigBean.java
                           └---ConfigPolicy.java

The most obvious difference is the addition of the “apiman” directory. This directory is used to contain the configuration files for the plugin. (In other words, the files that make the project a plugin.) The only project-wide configuration change that you have to make is to modify the plugin’s maven pom.xml file to include the apiman directory when the project is built. The following lines are added to the plugin’s pom.xml file:

      <resource>  
        <directory>src/main/apiman</directory>  
        <targetPath>META-INF/apiman</targetPath>  
        <filtering>true</filtering>  
       </resource>  




The result of these statements in the pom.xml file is that the contents of the apiman directory are included in the project build’s META-INF directory. (We’ll take a look at this later when we build the plugin project.) Setting filtering to true enables maven property expansion during the creation of the plugin war file.

Configuration files for the plugin are contained in the apiman directory. The primary configuration file is the “plugin.json” file. This file is required for all apiman plugins, regardless of the plugin type. The meta-data contained in this file describes the plugin and is displayed in the API Manager UI.

Since our example is contributing a policy, it must contain a JSON file that defines the policy. This JSON file is contained in the apiman/policyDefs directory and is named “config-policyDef.json.” This file defines the following fields for the policy:

  • id - A unique identifier for the policy
  • name - A user friendly (in other words, a human readable) name for the policy. This policy name is displayed in the API Manager UI
  • description - A description of the policy
  • policyImpl - This is the fully qualified classname name of the Java class that actually implements the policy. This value must be properly formatted to include information about the plugin, including placeholders for maven properties, for example, project version.
  • icon - This is the name of a Font Awesome icon (http://fortawesome.github.io/Font-Awesome/) that is displayed for the Policy in the API Manager UI.
  • formType - The type of policy configuration UI form. Our example uses JsonSchema.
  • form - This is a relative path to the policy configuration UI form contained in the plugin. Our examples uses the JSON Schema that defines the configuration data format. (More on this file in a minute.

In our example, the config-policyDef.json looks like this:


 {  
  "id" : "config-policy",  
  "name" : "Config Policy",  
  "description" : "A policy used to showcase policy configuration.",  
  "policyImpl" : "plugin:${project.groupId}:${project.artifactId}:${project.version}:${project.packaging}/io.apiman.plugins.config_policy.ConfigPolicy",  
  "icon" : "sliders",  
  "formType" : "JsonSchema",  
  "form" : "schemas/config-policyDef.schema"  
 }  

We mentioned that since our example uses the JSON schema (http://json-schema.org/documentation.html), we have to also define the configuration data format. This is done in the policyDefs/schemas/config-policyDef.schema file. This file defines the format used in the policy configuration file data and is used by the API Manager UI to generate a form that is used to fill in the values to be used to configure the policy. In our example, the policy’s JSON schema defines two properties: requestHeader and responseHeader. 

 {  
  "title" : "Configure HTTP Headers",  
  "description" : "Set the HTTP request header to populate with the value 'true' when the request is made. Also set the HTTP response header to populate with the value 'true' after the response is received from the back-end service.",  
  "type" : "object",  
  "properties": {  
   "requestHeader": {  
    "title" : "Request Header",  
    "type" : "string",  
    "minLength" : 1,  
    "maxLength" : 64  
   },  
   "responseHeader": {  
    "title" : "Response Header",  
    "type" : "string",  
    "minLength" : 1,  
    "maxLength" : 64  
   }  
  }  
 }  

That finishes the description of the policy’s configuration files. To complete the policy plugin, the example also requires the Java implementation for the policy itself. The policy implementation is contained in the src/main/java/io/apiman/plugins/config_policy/ConfigPolicy.java file. The policy is very simple as it merely appends a header to the http request and response.

Java policy classes must implement the apiman IPolicy interface. This example takes that a step further by actually extending the io.apiman.gateway.engine.policies.AbstractMappedPolicy class. By doing so, the policy Java code can take advantage of the AbstractMappedPolicy’s class use of the Jackson (https://github.com/FasterXML/jackson) JSON parser to parse the policy configuration data into a Java bean. (In this example, the bean is implemented in  src/main/java/io/apiman/plugins/config_policy/ConfigBean.java.) If the policy implemented the IPolicy interface, instead of extending AbstractMappedPolicy, then the policy Java class would have to implement its own parser.

Building the Plugin and Installing it into the Maven Repo

Building the plugin with maven is easy. Just execute this command from the directory that contains the plugin’s pom.xml file.

 mvn install  

In case you’re wondering why, we’re installing the plugin into the maven repo. The answer is simply that in the current release of apiman, the only supported install path is from the maven repo. Future releases of apiman will likely support additional installation paths.

Installing a Plugin into apiman

In apiman, a policy plugin, once installed, is available on a system-wide basis. Accordingly, plugins can only be installed by an admin user. After you login as admin, you’ll see this in the admin UI:

apiman-1.png

After selecting “Manage Plugins,” you’ll see a screen that looks like this:

apiman-2.png
And, if you then select “Add Plugin,” you’ll see this screen:

apiman-3.png

The GroupId, ArtifactId, and Version information is all available in the example plugin’s pom.xml file:

GroupId: io.apiman.plugins
ArtifactId: apiman-plugins-config-policy
Version: 1.0.3-SNAPSHOT

After you enter this information for the plugin and click on “Add Plugin,” you’ll see this displayed in the UI:

apiman-4.png
Congratulations! The plugin is installed and is ready to be used! Let’s add it to a service and see it in action.

Using the Installed Plugin

First, we have to log out of the admin account in the Admin UI and then log back in as the “serprov” service provider. Then, select our “echo” service, and create a new version of the service, based on the original version of the service:
apiman-6.png
And, when you add a new policy to the service, you’ll see the example config service that we just installed:

apiman-5.png
When you select the Config Policy, you’ll be presented with a dialog where you can specify the values for the two properties defined in the policy:

apiman-7.png

Let’s fill in some easy to remember values:

apiman-8.png

After adding the policy, you’ll see it displayed in the policies defined for the service:

apiman-9.png

Then publish the service. In order to consume the service, login to the API Manager UI as the application developer “appdev,” and create a new application that uses the new service. (We covered the creation of new applications in detail in the first article in this series - http://java.dzone.com/articles/impatient-new-users)

And when the service is invoked, you’ll see something like this:

apiman99.png

And this:

apiman100.png


In Conclusion

OK, let’s recap. apiman is a rapidly growing and developing project. Each new release brings new features. In release 1.0.2, it became possible for users to contribute custom policies into their apiman installation through the API Manager UI.

Acknowledgements

As always, the author would like to acknowledge Eric Wittmann for his (never impatient) review comments and suggestions on writing this post, and for adding new features to apiman!

References

The first article in this series - http://java.dzone.com/articles/impatient-new-users