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 tutorialRequirements
(*) 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.

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