Skill Level: Any Skill Level

Familiarity with REST web services and Json serialization required.

This recipe shows how to integrate process data from the OSIsoft PI data historian with the Watson platform by using PI Web API and WebSphere Liberty on Bluemix.


  • OSIsoft PI Server
  • OSIsoft PI Web API
  • Eclipse Java EE
  • IBM Cloud Foundry Tools for Eclipse


  1. Check Authentication Methods for PI Web API

    You will need an installation of PI Web API 2016 R2 to provide access to your PI and AF Server data.  PI Web API supports Kerberos, Basic, and Anonymous authentication.  Since we will be using a Liberty WebSphere service hosted in Bluemix, you should check with your PI administrator to ensure the AuthenticationMethods configuration item includes Basic as one of the supported methods.  Obtain a user name and password with read access (minimally).

  2. Create a Liberty Websphere Web Service

    The web service will act as a client to PI Web API to obtain data from the PI system.  Once it is in the Watson environment, you can use any Watson service such as MQTT to move data into Watson analytical or cognitive tools.  You can also, as we will demonstrate shortly, convert the data returned by PI Web API into native objects in Liberty and work on them in code.

    There are several ways to start a Liberty profile web service project.  For the purposes of this recipe, we will use the Liberty App Accelerator.  Make the following selections:

    1. Technologies: REST
    2. Deploy: Deploy to Bluemix (if you select Local, you can modify your POM file later to deploy to Bluemix)
    3. Download: Provide a name for your service.  Take note of the URL for the endpoint

    In step 3, click “Download Now” to generate the starter code and download a compressed file. Expand the compressed file and open it in Java Eclipse EE. ¬†The generated code consists of several projects. ¬†The source for the REST interface is found in the myProject-application project at src/main/java/application/rest/

  3. Create REST Methods

    The web service will obtain PI data in response to calls to its REST interface, then perform some action on the data.  For the purposes of the recipe, we will create methods that mimic PI Web API methods so that the parameters passed into the service are the same as those needed to access PI.  An actual web service obtaining data for analytics will need to translate the parameters needed by its use cases into parameters for PI Web API calls.

    The documentation for PI Web API details the controllers available, the types of PI data served, and the methods of each controller.

    A common PI Web API call involves plot values.  This call requires a start time, end time, and number of intervals.  The PI Web API call for plot values is a REST GET.  The method signature in Liberty looks like this:

    public String PlotValues(@QueryParam (“startTime”) String start, @QueryParam(“endTime”) String end, @QueryParam(“intervals”) String intervals)

    @GET denotes the HTTP verb. @Path is the relative path from the REST endpoint for this method.  @QueryParam flags method parameters as REST parameters in the web service.  Thus, for a server with a REST endpoint of /myLibertyApp/rest, a call for the last eight hours of plot values would be a GET with the*-8H&endTime=*&intervals=10.



  4. Make Calls to PI Web API

    The parameters are used in conjunction with a Web ID, which is the immutable label PI Web API uses to represent a PI tag on a specific server.  Web IDs may be found by navigating through the PI Web API endpoint to a particular resource or through the search interface of PI Web API.  For this example, we have identified the tag producing the time series data we want and have hard coded the Web ID. Since the code for all PI Web API GET requests will differ only in the URL, we have created a private helper method, QueryPI, that takes the fully formed URL.  Our plotvalues call requires that we insert the parameter values passed to the Liberty web service into the known URL:

    String request = String.format(“”, start, end, intervals);

    Replace with the name of your publically facing PI Web API server.  The known endpoint is piwebapi, and PI tag data is served by the streams controller. The Web ID for our tag on the desired PI server is P0W6Wlk0_Utku9vWTvxg45oAAwAAAAUElTUlYxXENEVDE1OA, and plot indicates plot value as the desired data type.

    From here, we simply need to make HTTP calls with Basic authentication using a suitable client class in Java.  We'll use the class.  Basic authentication requires that we pass the user name and password in a Base64 encoded header called Authorization:

    String authVal = “your_user” + “:” + “your_password!”;
    String authEncoded = java.util.Base64.getEncoder().encodeToString(authVal.getBytes());

    Note the colon between the value of the user name and the value of the password.  Now setup and execute the call:

        URL dataEndpt = new URL(fullRequest);
        HttpURLConnection conn = (HttpURLConnection) dataEndpt.openConnection();
    ¬† ¬† conn.setRequestMethod(“GET”);
    ¬† ¬† conn.setRequestProperty(“Accept”, “application/json”);
    ¬† ¬† conn.setRequestProperty(“Authorization”, “Basic ” + authEncoded);

        if (conn.getResponseCode() != 200)
    ¬† ¬† ¬† ¬† return CreateErrorMessage(“No good going to PI”);
            JsonReader reader = Json.createReader(conn.getInputStream());
            JsonObject retVal = reader.readObject();
            return retVal;

    There are several things to note in the above code.  We are requesting Json serialization with the Accept header value. The value of the Authorization header is the keyword Basic followed by a space followed by the Base64 encoded username and password. The call is actually transmitted when the connection object's getResponseCode method is called.  Finally, our utility method, QueryPI, simply returns a JsonObject.  This is a class in the JAVAX package Liberty uses.  You can use other serializer packages, such as Google's gson, but we want to keep the overhead low in this example.  This will have some implications when we convert from Json to native objects later on.

    Given the code in the try block, your code should catch exceptions of the types MalformedURLException, ProtocolException, and IOException.

  5. Processing the Data

    What you do with the data depends on what Watson services you want to use.  A very simple to use solution is to create a Node-RED workflow.  In this case, you need not transform the Json data coming from PI Web API.  Merely assign the serialized data to the Data portion of an MQTT message and pick it up in Node-RED.

    If you wish to process the events in Java code or work with other Watson APIs, you will want to convert Json-serialized objects into native, typed objects.  Some Json serializers, like Google gson, can perform this step for you.  JAVAX has very limited capabilities along these lines.  Instead, follow these steps:

    1. Create a class mirroring the PI data you wish to process.  For our time series events with floating point values, we use:

    public class PIFloatPoint
        public GregorianCalendar Timestamp;
        public double Value;
        public String UnitsAbbreviation;
        public Boolean Good;
        public Boolean Questionable;
        public Boolean Substituted;

    2. Locate the Items array in the returned JsonObject instance and iterate through it.  Assuming retVal is the JsonObject returned from our PI WebAPI query:

    JsonArray arr = retVal.getJsonArray(“Items”);
    ArrayList<PIFloatPoint> typedEvents = new ArrayList<PIFloatPoint>();
    List<JsonObject> events = arr.getValuesAs(JsonObject.class);
    Iterator<JsonObject> iterEvents = events.iterator();

    3. Create an instance of the typed class and populate it from the proeprties of the JsonObject:

    while (iterEvents.hasNext())
        JsonObject jEvent =;
        PIFloatPoint event = new PIFloatPoint();
    ¬† ¬† event.Good = jEvent.getBoolean(“Good”);

        if (event.Good)
    ¬† ¬† ¬† ¬† String dtg = jEvent.getJsonString(“Timestamp”).toString();
    ¬† ¬† ¬† ¬† event.Timestamp = parseDTG(jEvent.getJsonString(“Timestamp”).toString());
            int hrs = event.Timestamp.get(Calendar.HOUR_OF_DAY);
            dtg = event.Timestamp.toString();
    ¬† ¬† ¬† ¬† event.Value = jEvent.getJsonNumber(“Value”).doubleValue();
    ¬† ¬† ¬† ¬† event.UnitsAbbreviation = jEvent.getString(“UnitsAbbreviation”);
    ¬† ¬† ¬† ¬† event.Questionable = jEvent.getBoolean(“Questionable”);
    ¬† ¬† ¬† ¬†event.Substituted = jEvent.getBoolean(“Substituted”);


    Once you have native, typed objects, you can perform any other processing desired.

  6. Create a Liberty Web App

    Login to your Bluemix account.  Go to the Catalog, find the Cloud Foundry group and select Liberty for Java.


    Enter an App name.  By default, the hostname is set to the App name.  The domain will be  Click Create to create an empty Liberty service on Bluemix.

  7. Deploy to Bluemix Using IBM Eclipse Tools for Bluemix

    You will now create a server in your Java EE project and use the Bluemix tools for Eclipse to deploy your Liberty app.

    Select the Java EE perspective in Eclipse. On the Servers tab, right click and select New > Server.  Select IBM Bluemix. Enter a friendly name by which to identify your server in Eclipse.  Click Next.


    Enter your Bluemix account information and select a location.  Click Next and select your organization and the Space within which you created the Liberty web app.  Click Next.

    Select the application project and click Add.  Click Finish.


    Back on the Servers tab, find the application project under the newly created server, right click and select Push.  If necessary, or on subsequent use, click Start to start the app.

    Since the sample web method is an HTTP GET, you can test the method in a web browser.

Join The Discussion