Project Labrador (Web Services Hub)
Version 0.7
Maintained by Tom
Bradford
Copyright (c) 2003 The dbXML Group, L.L.C.
All rights reserved.
Labrador is an open source modular Web Services Hub. Labrador is designed to be incredibly simple to use for developers who are programming Web Services. Typically, only one line of code is needed to make your Object instance available.
Labrador is also designed to be easy to extend for those developers who need the hub to provide additional functionality. For example, Labrador also supports the ability to execute stored procedures and other SQL statements using any of the protocols that Labrador exposes.
Labrador is meant to allow objects to be exposed as Web Services using a variety of protocols, such as SOAP, REST and XML-RPC. It does not attempt to provide full-featured toolkit-style implementations of the protocols that it supports, and doesn't provide any client functionality. Its focus is to be a simple, and easily configured Web Services Hub that allows multiple protocol handlers, instance resolvers, and service models.
If you're looking for a complete XML-RPC implementation, you should use Apache XML-RPC (formerly Helma). If you're looking for a complete implementation of SOAP, you should check out the Apache Axis project.
Labrador is written in Java, and is being made available under The Apache License.
Before moving forward, some of the terminology that is used when speaking about Labrador should be clarified.
| Term | Definition |
| Handler | A Handler is a class that implements a specific transport protocol. Handlers also retrieve IDs from a Request for use by Resolvers. |
| ID | ID is used to identify Instances for resolution and description. |
| Resolver | A Resolver is a class that resolves Instances based on IDs. Resolvers also resolve Discovery instances for Describers and Handlers. |
| Instance | An Instance is an interface that is used to invoke operations against a resolved web service. |
| Discovery | A Discovery interface is used to retrieve service operation meta-data for use by a web service Describer. |
| Describer | A Describer is used to produce web service descriptions such as WSDL against Discovery interfaces. |
| Filter | A Filter serves to filter a Request. Typically, this includes tasks like authentication. |
| Endpoint | An Endpoint represents a web service access point. Endpoints are responsible for producing Request and Response instances for use by the Broker. |
| Request Response |
Request and Response instances encapsulate an Endpoint's contextual information. This information is used to resolve objects and process web service operations. |
| Broker | Broker is the Labrador web service broker. It is essentially the glue for the entire service hub. |
| BrokerContext | BrokerContext is a thread-local interface that provides information about the current service context. |
Labrador already has three endpoints, a standard Servlet endpoint, a standard HTTP Server, and a secure HTTP Server. I am also considering working on an internal VM endpoint, so that a servlet/network interface isn't necessary to expose the Broker.
Currently, there are two Resolvers. One is the Standard Object reflection Resolver. The other is a JDBC Resolver that will allow you to map groups of parameterized SQL statements (Stored Procedures or Prepared Statements) to the methods of a particular Instance.
There are three Handlers, one is an XML-RPC Handler, another is a REST Handler, and the other is a SOAP Handler, which barely has any functioning code.The REST (REpresentational State Transfer) Handler allows a query string and POST data to be used to invoke objects and return their results as non-wrapped content.
The easiest way to expose objects via Labrador is to use the standard ObjectResolver to register an object instance by name. This assumes that the Broker has already been bootstrapped, and that the ObjectResolver is available. An example follows:
com.dbxml.labrador.objects.ObjectResolver.register("myName", new MyClass());
Alternatively, you can create a class that extends com.dbxml.labrador.objects.SimpleObject, and simply create a new instance of it. SimpleObject is a convenience class that provides several utility methods, and will automatically register an object when it is constructed. An example follows:
import com.dbxml.labrador.objects.*;
public class MyAutoClass extends SimpleObject {
public MyAutoClass(ID
id) {
super(id);
}
public String hello(String yourName) {
return "Hello "+yourName+"!";
}
}
If you're adventurous, you can use Labrador's internal HTTP server to bootstrap this class as a stand-alone service. To do this, simply add a main method to the class like the following:
public static void main(String[] args) {
// Create the new object instance...
It will automatically
// register itself with the ObjectResolver
new MyAutoClass(new ID("test"));
// Start the HTTP Server... By
default, it uses a Daemon
// Thread, so you have to turn this feature off to keep the
// server alive. The HTTPServer's default port is 7280
com.dbxml.labrador.http.HTTPServer server =
new com.dbxml.labrador.http.HTTPServer();
server.setDaemon(false);
server.start();
}
If this is too much coding, you can also bootstrap the class without a main method by using the HTTPServer startup script. Just make sure the class is somewhere on your CLASSPATH, and enter the following command from sh or bash:
~$ $LABRADOR_HOME/bin/labrador "test=MyAutoClass" ![]()
Labrador provides the ability to map SQL statements and stored procedure calls to web services. Currently, this mapping process must be performed manually, and the SQL must be typed explicitly. A future version of Labrador may provide a wizard for discovering stored procedures in a database catalog.
In order to expose SQL statements via Labrador, you must provide a custom Labrador configuration file to the hub. For an example, see the file jdbc-test.xml in the config directory. The key to exposing SQL is adding the Labrador JDBC resolver to your resolver list. For example:
<resolver class="com.dbxml.labrador.jdbc.JDBCResolver">
<connection driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/GeekDate"
username="root"
password="mysql">
<instance path="/db/test">
<method name="test1">
<statement type="resultset">
select * from Users
</statement>
</method>
<method name="test2">
<statement type="resultset">
select User_Name from Users where User_ID = ?
</statement>
<param name="userid" type="int"/>
</method>
</instance>
</connection>
</resolver>
Because Labrador doesn't provide client tools, you'll need to use a toolkit to call Labrador service methods. One thing you'll notice in the following examples is that Labrador uses prefixes in the URI to identify the type of request. This helps the Handler to identify the request without having to examine its content.
This example uses Apache XML-RPC to invoke a method using the XML-RPC protocol. In this case, the service's URI has a prefix of 'xmlrpc'.
import org.apache.xmlrpc.*;
import java.util.*;
public class CallTest {
public static void main(String[] args) {
try {
String url = "http://localhost:7280/xmlrpc/test";
XmlRpcClient client = new XmlRpcClient(url);
Vector v = new Vector();
v.add(args[0]);
System.out.println(client.execute("hello", v));
}
catch ( Exception e ) {
System.err.println(e);
e.printStackTrace(System.err);
}
}
}
SOAP will require a prefix of 'soap' to properly identify the request. Labrador will not examine MIME headers to identify SOAP requests.
Note:
Labrador's SOAP implementation is not yet complete.
REST will require a prefix of 'rest'. Method arguments using REST are provided as name/value pairs in the URL's query string, name/value pairs in URL encoded POST data, or as raw POST data. Because of this method of providing arguments, REST requires parameter names. Positional information cannot be relied on in a query string. There are a few ways to go about this.
Note:
To be completed
Discovering Labrador services is performed using the Labrador InterfaceService. The InterfaceService is exposed like any other Labrador web service, and can be invoked using XML-RPC, SOAP, or REST. The examples in this section will utilize REST, as it is the most convenient way to obtain interface information.
Note:
To be completed
Labrador is very flexible about how methods and their types are exposed. Strict typing gives way to type coercion wherever possible. Because of this, it will be difficult for a Handler and Resolver to properly nail down the correct method signature for a specific request. In order to avoid ambiguity in the resolution of methods, it's safer to qualify method names rather than to expose overloaded methods. This will ensure that your objects can interoperate with every possible Handler implementation.
For method parameters, Labrador supports several types, including all Java native types and their wrappers, Strings, Maps, Lists, Documents, DocumentFragments, Elements, Variants, and JAXB-generated classes. It also supports arrays of most of these types. For method results, Labrador supports the same types as those supported for parameters, including arrays of those types, but also allows any Object to be returned and converted to a String using the Java toString() method.
It's important to understand that the types Labrador supports internally may not be completely supported by various Handlers. A Handler should make every possible attempt to map its own types to satisfy the method that's being invoked. For example, the XML-RPC protocol has no way to represent XML content. Because of this, there is no standard way to transmit XML fragments using the protocol. Labrador's XMLRPCHandler, upon seeing that a parameter value is of a DOM type, will attempt to parse values into DOM nodes. Additionally, if the result value is of a DOM type, it will attempt to serialize the DOM node into an appropriate string representation.