47
Java Programmer's Guide
323
if you use it directly you need to store a reference to it throughout your application.
Using Request.registerCluster(cluster) means that you can blissfully ignore the
RequestExecutor and lifecycle rules.
The RequestExecutor contains the necessary state and logic which provides the load balancing and
failover features. Using the RequestExecutor directly also offers a few extra features, which we will
look at.
RequestExecutor Features
When the RequestExecutor is not connected to a repository, you can dynamically add remove
RoboServers, by calling addRoboServer(..) and removeRoboServer(..). These methods
modifies the distribution list used inside the RequestExecutor.
RequestExecutor.getTotalAvailableSlots() returns the number of unused execution slots
across all RoboServers in the internal distribution list.
By using these methods you can dynamically add RoboServers to your RequestExecutor once the
number of available execution slots becomes low.
When you create the RequestExecutor you may optionally provide an
RQLEngineFactory
[api/com/kapowtech/robosuite/api/java/rql/engine/
hotstandby/RQLEngineFactory.html]. The RQLEngineFactory allows you to customize
which RQLProtocol
[api/com/kapowtech/robosuite/api/java/rql/engine/
remote/RQLProtocol.html] is used when connecting to a RoboServer. This is only needed under
very rare circumstances, for instance if you want use a client certificate to increase security, check API
Client Certificates for details.
Web applications
The RequestExecutor [api/com/kapowtech/robosuite/api/java/rql/engine/
hotstandby/RequestExecutor.html] contains a number of internal threads used for sending
and receiving requests to RoboServers, as well as pinging each known RoboServer at regular intervals.
These threads are all marked as daemon, which means that they don't prevent the JVM from stopping
when the main thread exists, check Thread JavaDoc [http://docs.oracle.com/javase/1.4.2/docs/api/java/
lang/Thread.html] for details on daemon threads.
If you use the RequestExecutor inside a web application, the JVM has a longer lifespan than your web
application, and you can deploy and un-deploy your web application while the web container is running.
This means that a web application is responsible for stopping any threads that it has created, if it does
not a memory leak will be created when you un-deploy the web application. The memory leak occurs
because any objects referenced by running threads can't be garbage collected until the threads stop, so if
these threads are not stopped when the application is un-deployed, they will never be garbage collected.
If you use the RequestExecutor inside a web application your code is responsible
for shutting down these internal threads, this is done by calling Request.shutdown()
[api/com/kapowtech/robosuite/api/java/rql/Request.html#shutdown()] or
RequestExecutor.shutdown() [api/com/kapowtech/robosuite/api/java/rql/
engine/hotstandby/RequestExecutor.html#shutdown()] if your code created the
RequestExecutor explicitly.
This example show you how to use a ServletContextListener to shutdown the API correctly when
a web application is un-deployed. You must define the context listener in your applications web.xml.
Table 71. proper shutdown in web application
43
Java Programmer's Guide
324
import com.kapowtech.robosuite.api.java.repository.construct.*;
import com.kapowtech.robosuite.api.java.rql.*;
import com.kapowtech.robosuite.api.java.rql.construct.*;
import javax.servlet.*;
public class APIShutdownListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
RoboServer server = new RoboServer("localhost", 50000);
Cluster cluster = new Cluster("MyCluster", new RoboServer[]{ server}, false);
try {
Request.registerCluster(cluster);
}
catch (ClusterAlreadyDefinedException e) {
throw new RuntimeException(e);
}
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
Request.shutdown();
}
}
contextDestroyed is called when the web container un-deploys the application. Here we call
Request.shutdown() to ensure that all internal threads in the hidden RequestExecutor are
stopped correctly.
Since contextInitialized can't throw any unchecked exceptions we have to wrap the
ClusterAlreadyDefinedException in a RunTimeException. Developers may be tempted to
ignore the ClusterAlreadyDefinedException at this location, because they claim that it can't be
thrown, as our application has not defined any other clusters. However due to the class loader hierarchy
in java web containers it is actually possible to get this exception if the application is deployed twice. It
will however only occur if the API jar file was loaded by a common class loader and not by the individual
application's class loader.
API Debugging
Although this is rarely needed, the API can provide additional information for debugging purposes. To
enable API debugging you need to configure the system property DEBUG_ON. The value of this property
must be a package/class name the API.
For instance, if you are interested in the data transmissions between the API and RoboServer, you could ask
for debugging information for package com.kapowtech.robosuite.api.java.rql.io. While
you are developing you can do this by directly setting the system property in code, like this:
Table 72. Enabling Debug
System.setProperty("DEBUG_ON", "com.kapowtech.robosuite.api.java.rql.io");
RoboServer server = new RoboServer("localhost", 50000);
Cluster cluster = new Cluster("MyCluster", new RoboServer[]{ server}, false);
Request.registerCluster(cluster);
37
Java Programmer's Guide
325
If you are debugging an application in production, you would define the system property via the command
line, like this
Table 73. Enabling Debug
java -DDEBUG_ON=com.kapowtech.robosuite.api.java.rql.io Tutorial1
If you are interested in debug from multiple packages, you separate the package names by , (comma).
Instead of a package name, you can provide the argument ALL, to have debug from all packages printed.
Repository API
The Repository API allows you to query the Management Console's Repository, to get a list of projects,
robots and the input required to call a robot. It also allows you to programmatically deploy robots, types
and resource files.
Dependencies
To use the Repository API you need the following libraries, all libraries can be found in the API/
robosuite-java-api/lib folder inside you Kapow Katalyst installation folder
• commons-logging-1.1.1.jar, or newer
• commons-codec-1.4.jar, or newer
• commons-httpclient-4.1.jar, or newer
• commons-ssl-0.3.8.jar, or newer - if your Management Console must be accessed through
HTTPs
You also need to use Java 1.5 or newer.
Repository Client
Communication with the repository is achieved through the RepositoryClient found in the
com.kapowtech.robosuite.api.java.repository.engine
Table 74. Create RepositoryClient
public static void main(String[] args) {
String username = "admin";
String password = "admin";
try {
RepositoryClient client = RepositoryClientFactory.createRepositoryClient("http://localhost:50080/", username, password);
Project[] projects = client.getProjects();
for (Project project : projects) {
System.out.println(project.getName());
}
}
catch (RepositoryClientException e) {
e.printStackTrace();
95
Java Programmer's Guide
326
}
}
Here we see a RepositoryClient configured to connect to Management Console's repository on
http://localhost:50080/ , with a username and password.
Once the RepositoryClient is created, we use the getProjects() method to query the repository
for a list of projects. Notice that when calling any of the RepositoryClient methods, a
RepositoryClientException will be thrown if an error occurs.
The RepositoryClient has the following eleven methods
Table 75. Methods of the RepositoryClient
Method signature
description
void
deleteResource(String
projectName, String resourceName,
boolean silent)
Deletes a resource from a project. If silent is true no
error is generated if the resource doesn't exist
void
deleteRobot(String
projectName,
String
robotName,
boolean silent)
Deletes a robot from a project
void
deleteSnippet(String
projectName, String snippetName,
boolean silent)
Deletes a snippet from a project
void
deleteType(String
projectName,
String
modelName,
boolean silent)
Deletes a type from a project
void
deployLibrary(String
projectName,
EmbeddedFileBasedRobotLibrary
library, boolean failIfExists)
Deploys a library to the server. Robots,
types and resources will be overridden unless
failIfExists is true.
void
deployResource(String
projectName, String resourceName,
byte[]
resourceBytes,
boolean
failIfExists)
Deploys a resource to a project. If a resource with
the given name already exist it can be overridden by
setting failIfExists to false.
void
deployRobot(String
projectName,
String
robotName,
byte[]
robotBytes,
boolean
failIfExists)
Deploys a robot to a project.If a robot with the given
name already exist it can be overridden by setting
failIfExists to false.
void
deploySnippet(String
projectName, String snippetName,
byte[]
snippetBytes,
boolean
failIfExists)
Deploys a robot to a project. If a snippet with the
given name already exist it can be overridden by
setting failIfExists to false.
void
deployType(String
projectName,
String
typeName,
byte[]
typeBytes,
boolean
failIfExists)
Deploys a type to a project. If a type with the given
name already exist it can be overridden by setting
failIfExists to false.
Project[] getProjects()
Returns the projects that exist in this repository
Cluster[] getRoboServerClusters()
Return the cluster configuration on the Management
Console
53
Java Programmer's Guide
327
Method signature
description
Robot[] getRobotsInProject(String
projectName)
Returns the robots available in the project with the
given name.
RobotSignature
getRobotSignature(String
projectName, String robotName)
Returns the robot signature, e.i. the input variables
required to execute this robot and a list of the types
it may return or store
Proxy servers must be specified explicitly when creating the RepositoryClient. Standard http proxy servers
without authentication are supported. NTLM proxy servers with authentication is also supported.
Check
the
RepositoryClient
[api/com/kapowtech/robosuite/api/java/repository/engine/
RepositoryClient.html] JavaDoc for additional details
Deployment via Repository Client
The following example shows how to deploy a robot and a type from the local file system using the
RepositoryClient
Table 76. Deployment using RepositoryClient
String user = "test";
String password = "test1234";
RepositoryClient client = new RepositoryClient("http://localhost:50080", user, password);
try {
FileInputStream robotStream = new FileInputStream("c:\\MyRobots\\Library\\Test.robot");
FileInputStream typeStream = new FileInputStream("c:\\MyRobots\\Library\\Test.type");
// Use the Kapow Java APIs StreamUtil to convert InputStream to byte[].
// For production we recommend IOUtils.toByteArray(InputStream i) in the commons-io library from apache.
byte[] robotBytes = StreamUtil.readStream(robotStream).toByteArray();
byte[] typeBytes = StreamUtil.readStream(typeStream).toByteArray();
// we assume that no one has deleted the Default project
client.deployRobot("Default project", "Test.robot", robotBytes, true);
client.deployType("Default project", "Test.type", typeBytes, true);
}
catch (RepositoryClientException e) {
// an error connecting to the repository
e.printStackTrace();
}
catch (FileNotFoundException e) {
System.out.println("Could not load file from disk " + e.getMessage());
}
catch (IOException e) {
System.out.println("Could not read bytes from stream " + e.getMessage());
}
catch (FileAlreadyExistsException e) {
// either the type or file already exist in the give project
System.out.println(e.getMessage());
}
97
Java Programmer's Guide
328
Repository REST API
The repository API is actually a group of restful services (and URLs where data can be posted).
All the Repository Client methods that retrieve information from the repository sends XML to the
Repository, and the Repository responds with XML. All deploy methods post bytes to the Repository
(information encoded in URL) and the Repository return XML to acknowledge. The format of the
XML sent and received is governed by a DTD found here [http://www.kapowtech.com/robosuite/
repository_1_3.dtd].
Here is an example of all the XML based requests, all messages must start with the following declaration
Table 77.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE repository-request PUBLIC "-//Kapow Technologies//DTD Repository 1.3//EN" "http://www.kapowtech.com/robosuite/repository_1_3.dtd">
If the Management Console is deployed at http://localhost:8080/ManagementConsole,
the requests must be posted to http://localhost:8080/ManagementConsole/secure/
RepositoryAPI?format=xml
Table 78. REST operations
Method
Example request
Example response
delete-
file
(robot)
<repository-request> <delete-
file
file-type="robot"
silent="true">
<project-
name>Default
project</
project-name>
<file-
name>InputA.type</file-name>
</delete-file> </repository-
request>
<repository-response><delete-
successful/></repository-
response>
delete-
file
(type)
<repository-request> <delete-
file
file-type="type"
silent="false">
<project-
name>Default
project</
project-name>
<file-
name>InputA.type</file-name>
</delete-file> </repository-
request>
<repository-response><error
type="file-not-found">Could
not
find
a
Type
named
InputA.type
in
project
'Default
project'</error></
repository-response>
delete-
file
(snippet)
<repository-request> <delete-
file
file-type="snippet"
silent="true">
<project-
name>Default
project</
project-name>
<file-
name>InputA.type</file-name>
</delete-file> </repository-
request>
<repository-response><delete-
successful/></repository-
response>
delete-
file
(resource)
<repository-request> <delete-
file
file-type="resource"
silent="true">
<project-
name>Default
project</
<repository-response><delete-
successful/></repository-
response>
Documents you may be interested
Documents you may be interested