Demand, Sales and Workload Forecasting Software

Web Services Tutorial with Java for Time-Series Forecasting

Developers » Web Services » Here

Web Services Tutorial with Java

Last revision: 2007-01-16


Target audience: developers, system administrators

In this tutorial, we will see how to use Lokad Web Services in a simple application written in Java. Lokad specializes in business time-series forecasting, but this document is neither an introduction to Lokad nor an introduction to Web Services.

Download: source-code for this tutorial

Requirements
(*) The Free subscription plan is sufficient to reproduce this tutorial, with the exception of the last section Retrieving the forecasts which requires that the Enterprise plan be selected in your Lokad account.

Introduction

Web Services are an industrial-strength standard currently embraced by most (if not all) major software vendors. Lokad features Web Services: all the operations that can be performed from the web application can also be performed programmatically through our Web Services. The Lokad Web Services can be reached at


In this tutorial, we will see how to use Lokad Web Services in a simple application written in Java. First, we will see how proxy classes can be generated using NetBeans, a free open-source IDE for Java—similar functionality should be available in other IDEs. Then we will examine how to use those classes to perform the typical Lokad operation life-cycle: upload data and retrieve forecasts.

Project setup in NetBeans

Warning: for development purposes, you should replace ws.lokad.com by sandbox-ws.lokad.com in the WSDL URL and create a Lokad sandbox account.

In this tutorial, we will use NetBeans, a common IDE for Java. Let's begin by creating our application. Fire NetBeans and choose New Project from the File menu. Default values for Categories and Projects should be General and Java Application, check this and click Next. Enter a project name such as LokadWS-java-tutorial, set main class to org.lokad.ws.Main, choose your favorite location and you are done.

Image

NetBeans has a very good support for Web Services: it is able to retrieve the XML description of the Web Services and to generate directly a proxy implementation in Java within a project. Right-click on the project in the Projects view and choose New > Web Service Client..

Image

A dialog pops-up; select the WSDL URL line and enter the follwing URL: http://ws.lokad.com/TimeSeries.asmx. Enter a name for the package: org.lokad.ws and click Finish. The Output window displays some feedback as NetBeans downloads the WSDL from the Internet, analyses it and generates code.

Image
A Web Service References folder has appeared in the Projects view. Expand it. You will find there the WSDL and all its methods.

Now you have a number of classes within the package org.lokad.ws that provide all the functionalities you need to interact with the web services.

Web Services Authentication

Lokad Web Services are authenticated, which means that you have to provide the username and password associated with your Lokad Web Account to access our Web Services. Technically, Lokad relies on SOAP headers to authenticate a Web Method call.

JAX-WS does not have a ready-made interface for this task, so we will have to use a workaround. Since we do not want to modify the code that was automatically generated by wsimport from the WSDL, we will use a SOAP Handler. It will intercept all queries to the webservices and add the authentication header. This Handler is defined in the file named LokadAuthenticationSOAPHandler.java. Locate it in the source-code for the tutorial and copy it in folder src/org/lokad/ws/ within your project, which will automatically add it to the org.java.ws package.

You can copy Main.java as well, replacing the empty Main.java generated by NetBeans. Edit lines 33 and 34 to match you login and password for Lokad. Press F6 to compile and run the project. The console displays some output as the tests are run.

All the source code presented further comes from this file. Thus you can refer to the relevant parts of the tests as you read.

The code provided below illustrates how you can check that your call is correctly authenticated.

// change this to your login and password for Lokad
String username = "myemail@mycompany.com";
String password = "mypassword";

TimeSeries timeSeries = new TimeSeries(); TimeSeriesSoap timeSeriesPort = timeSeries.getTimeSeriesSoap();

System.out.println("Is Authenticated: " + timeSeriesPort.isAuthenticated());

System.out.println("Adding username/password."); LokadAuthenticationSOAPHandler authHandler = new LokadAuthenticationSOAPHandler(username, password); authHandler.addTo(timeSeriesPort);

System.out.println("Is Authenticated: " + timeSeriesPort.isAuthenticated());

As you may already have noticed, TimeSeries and TimeSeriesSoap are part of the org.lokad.ws package. Those types have been generated by wsimport when you imported the WSDL. The TimeSeriesSoap class is a proxy implementation that let you seamlessly access Lokad Web Services in a typical Java manner. The expected console output for the above code sample is:

Is Authenticated: false
Adding username/password.
Is Authenticated: true

Time-series management

Lokad specializes in time-series forecasting. Before actually performing a forecast, you first need to upload your data; and this can be easily achieved with our Web Services. Technically, a time-series is a class containing a named array of time-value pairs and some meta-data. Let's see how a time-series can be generated programmatically. Since the Web Services rely on XML serialization, we must use the datatypes defined in javax.xml.datatype, which makes the code slightly heavy.

static TimeSerie makeRandomTimeSerie(String name, int length,
    String unitName, double utcOffset) {
    TimeSerie timeSerie =  new TimeSerie();
    
    ArrayOfTimeValue values = new ArrayOfTimeValue();
    try {
        // we create an XMLGregorianCalendar set at today, 0:00 AM
        java.util.GregorianCalendar today =
                new java.util.GregorianCalendar();
        javax.xml.datatype.DatatypeFactory factory =
                javax.xml.datatype.DatatypeFactory.newInstance();
        javax.xml.datatype.XMLGregorianCalendar calendar =
                factory.newXMLGregorianCalendar(
                today.get(java.util.GregorianCalendar.YEAR),
                today.get(java.util.GregorianCalendar.MONTH) + 1,
                today.get(java.util.GregorianCalendar.DAY_OF_MONTH),
                0, 0, 0, 0, 0);
        // Duration to increment the calendar by one day
        javax.xml.datatype.Duration day =
                factory.newDurationDayTime(true, 1, 0, 0, 0);
        
        // we create a list of random values, one each day
        for (int i = 0; i < length; i++) {
            TimeValue timeValue = new TimeValue();
            calendar.add(day);
            timeValue.setTime((javax.xml.datatype.XMLGregorianCalendar)
                              calendar.clone());
            timeValue.setValue(random.nextDouble());
            values.getTimeValue().add(timeValue);
        }
    } catch (javax.xml.datatype.DatatypeConfigurationException ex) {
        ex.printStackTrace();
    }
    
    timeSerie.setTimeValues(values);
    timeSerie.setName(name);
    timeSerie.setUnitName(unitName);
    timeSerie.setUtcOffset(utcOffset);
    
    return timeSerie;
}

The static method makeRandomTimeSerie() creates a time-series with random values. Note that we specify a UtcOffset in the example above; this value is expressed in hours according to the formula local time = UTC time + offset. Once the time-series is defined, 10 time-value pairs are added. In our sample, for simplicity, the values are regularly spaced (one day increment for each value) but this is not a requirement. The intervals between the successive values can vary arbitrarily.

Naming conventions: although the word serie (singular) does not exist in English, we have found it quite convenient to distinguish in source code serie (singular) from series (plural). So, when the term serie is used in our examples, it's not a mistake, it's on purpose.

The makeRandomTimeSerie() method provides a time-series instance. Let's add this time-series to your Lokad account.

String serieName = "TestSerie";
TimeSerie timeSerie = makeRandomTimeSerie(serieName, 10, "", 0.0);
timeSeriesPort.addSerie(timeSerie);

To delete a time-series from your account, you just need to specify the name identifier of the time-series in question.

timeSeriesPort.deleteSerie(serieName);

You can also retrieve the names of the existing time-series in your account; the following snippet prints to the console the names of the time-series in your account.

ArrayOfString serieNames = timeSeriesPort.getAllSerieNames();
for (String serieName : serieNames.getString())
    System.out.println(serieName);

Forecasting task management

Before you can retrieve a time-series forecast, you need to define a forecasting task that will specify how the forecasted time-series should be computed. In the Lokad data model, a forecasting task is always associated with a particular time-series; several forecasting tasks can be associated with the same time-series.

static Task makeTask(String name, Aggregator aggregator, Period period,
        int pastPeriods, int futurePeriods, String periodStart) {
    Task task = new Task();
    
    task.setName(name);
    task.setAggregator(aggregator);
    task.setPeriod(period);
    task.setPastPeriods(pastPeriods);
    task.setFuturePeriods(futurePeriods);
    task.setPeriodStart(periodStart);
    
    return task;
}

Tip: the time-series names should be unique within the scope of your Lokad account (you cannot have two distinct time-series identified by the same name). However, the names of the forecasting tasks only need to be unique within the scope of their base time-series. In other words, you can have several forecasting tasks identified by the same name but associated with different time-series.

You can add a Task to your Lokad account with the following code.

String taskName = "TestTask";
Task task = makeTask(taskName, Aggregator.SUM, Period.DAY,
                     10, 5, "");
timeSeriesPort.addTask(serieName, task);

Retrieving forecasts

To retrieve your forecasts through our Web Services, you need to activate the Enterprise subscription plan in your Lokad account. Note that you won't be charged upfront, but only at the end of each 30-day period (if you decide to continue with your subscription).

TimeSerie forecastData =
    timeSeriesPort.getForecast(serieName, taskName);
for (TimeValue timeValue : forecastData.getTimeValues().getTimeValue())
    System.out.println(timeValue.getTime()
                       + " "
                       + timeValue.getValue());

As you can see in the output below, 5 time-value pairs are actually returned, matching the futurePeriods value specified for the Task in the previous section.
SourceForge.net Logo

All add-ons developed by Lokad have been released as open source under the BSD license on SourceForge.net.

The BSD license is compatible with closed-source commercial applications. Integrating a forecasting technology has never been easier.

What people say

From a developer standpoint, we found that the Lokad API was very well documented and easy to use. The Lokad team was also extremely responsive to any questions we had. Most importantly, we were able to develop a Lokad plugin for our data browser in very short order. Jeff Engel, Kirix Corporation