|
The HTTP transport layer conveniently adds support for receiving remote method invocations over the
internet. It opens the door to rich-client interfaces that can from now on use an object-oriented approach
to communicating with servers over the WAN, something that is usually implemented with XML. Of course, this does
not invalidate XML for such use, but provides an alternative if clients are in Java, and if getting up and running fast
is a must.
Ubik's HTTP Tranport layer is implemented by two transport providers, for two
different usages: 1) stand-alone; 2) embedding Ubik within a servlet - for use within a servlet container.
Servlet Transport
In short, the
ServletTransportProvider can be used to export remote objects over HTTP,
from within a servlet. This means the following:
- A Ubik server can receive remote method calls over HTTP;
- A Ubik server can be embedded within an already-running servlet container (Tomcat, WebSphere, WebLogic...);
- A Ubik server can benefit from existing HTTP infrastructure setups (firewalls, load-balancers, integration with Apache, SSL, etc.);
Server-Side
The code below is taken from the examples that come with Ubik. Although simple and meaningless, it clearly demonstrates
the steps necessary to embed Ubik within a servlet:
public class HttpFooServlet extends HttpServlet{
private Foo _foo;
private ServletTransportProvider _provider;
// this would not be hard-coded like this
// normally - but rather configured in servlet
// init parameters.
static final String SERVLET_URL =
"http://localhost:8080/ubik";
public void init(ServletConfig conf)
throws ServletException {
_provider = new ServletTransportProvider();
TransportManager.registerProvider(_provider);
Properties props = new Properties();
// we would normally get value for this
// property from init parameters...
props.setProperty(
ServletConsts.SERVLET_URL_KEY,
SERVLET_URL);
// this tells ubik "under" which transport
// our object will be exported
props.setProperty(
Consts.TRANSPORT_TYPE,
ServletConsts.DEFAULT_SERVLET_TRANSPORT_TYPE);
try{
_foo = new UbikFoo();
Hub.exportObject(_foo, props);
}catch(RemoteException e){
throw new
ServletException("Could not export Foo", e);
}
}
protected void service(HttpServletRequest req,
HttpServletResponse res)
throws ServletException,
IOException {
_provider.handleRequest(req, res);
}
/**
* @see javax.servlet.GenericServlet#destroy()
*/
public void destroy() {
try {
Hub.shutdown(30000);
} catch (Exception e) {
getServletContext()
.log("Exception caught performing Hub shutdown",
e);
}
}
|
Note, in the code above, that passing the URL of the servlet that
embeds Ubik when exporting our object is mandated by the underlying
transport provider (in this case, the ServletTransportProvider).
This URL eventually ends up in the stubs that are received by clients: stubs will
perform remote method calls through this URL.
Normally, the servlet URL would not be hard-coded like this (as comments in the code
indicate). Rather, it would be passed to the servlet through an initialization parameter.
For the constants that are used, see the
ServletConsts interface.
Client-Side
On the client-side, the code is straightforward:
public class HttpFooServletClient {
public static void main(String[] args) {
try {
TransportManager.registerProvider(
new ServletTransportProvider());
Foo foo = (Foo) Hub.connect(new ServletAddress(
"http://localhost:8080/ubik"));
System.out.println(foo.getBar().getMsg());
} catch (Throwable t) {
t.printStackTrace();
}
}
}
|
Stand-Alone
The
HttpTransportProvider can be used when wishing to export objects over HTTP using
an underlying stand-alone HTTP server. In this case, you do not embed Ubik in a servlet, but rather
use it as you would normally - to the exception that you must register the appropriate transport
provider with the Hub.
Preferrably use this implementation when needing a quick-and-dirty stand-alone HTTP-based Ubik server that
does not need a full-fledge infrastructure (security, load-balancing, etc.).
This transport layer builds on the Simple API,
and allows to plug in custom Simple services.
Server-Side
The code below (again taken from Ubik's examples) demonstrates how a remote object is exported
over HTTP:
public class HttpFoo {
public static void main(String[] args) {
try {
TransportManager.registerProvider(
new HttpTransportProvider());
Properties props = new Properties();
props.setProperty(Consts.TRANSPORT_TYPE,
HttpConsts.DEFAULT_HTTP_TRANSPORT_TYPE);
props.setProperty(HttpConsts.HTTP_PORT_KEY, "8080");
Hub.exportObject(new UbikFoo(), props);
while (true) {
Thread.sleep(100000);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
|
See the
HttpConsts interface for the constants that are used in the code.
Note that in the above example, the URL of the server is not specified; the URL that is used then
is of the format http://local_ip_address:port/ubik. The port is
in this case expected through a property (as shown in the code above); if not specified, the underlying
HTTP server will be listening on the default port: 8080. The URL will eventually be used by stubs on the
client-side; if this is not what you want (because your server might be behind a firewall/load-balancer, and
stubs in this case cannot connect to your server directly), you can specify the URL to which stubs will
connect:
// Do not forget the trailing "/ubik" path
// when using Ubik HTTP in standalone (not in a
// servlet container)
props.setProperty(HttpConsts.SERVER_URL_KEY,
"http://www.somedomain.com/ubik");
|
| Note that the above feature really makes sense only if your HTTP-remoted object
hides behind some proxy, and that all clients are expected to originate from beyond such a proxy. |
If you wish that the path to which the exported object corresponds be mapped to something else then
"/ubik" (the default), and that you are not using the URL substitution scheme - explained previously, then you
can specify a different path:
props.setProperty(HttpConsts.PATH_KEY, "/somePath");
|
Client-Side
The client corresponding to the above server would look like this:
public class HttpFooClient {
public static void main(String[] args) {
try {
TransportManager.registerProvider(new HttpTransportProvider());
Foo foo = (Foo) Hub.connect(new HttpAddress(Uri.parse(
"http://localhost:8080/ubik")));
System.out.println(foo.getBar().getMsg());
} catch (Throwable t) {
t.printStackTrace();
}
}
}
|
Custom Simple Services
As was mentioned, Ubik's stand-alone HTTP transport layer is built on top of
Simple. The Simple API allows
implementing custom services that can benefit from the HTTP infrastructure. Ubik makes
this feature available to developers that would want to plug their Simple services (see Simple's
API for more on how to implement them) in its HTTP layer. A Simple service can be added
to a HttpTransportProvider in the following way:
HttpTransportProvider prov = new HttpTransportProvider();
MyService mySvc = new MyService();
prov.getServiceMapper().addService("/myPath", mySvc);
// here register provider with hub and export remote object...
|
The above is an interesting feature: it allows handling remote method calls and other types of
HTTP requests with the same underlying HTTP server.
Conclusion
So there you have it, at last: remote method invocations over HTTP, in two flavors, depending on your
needs. This transport layer is especially suited to receive remote method calls from clients across the
internet. It also opens the door to rich client interfaces, and frees you from XML if you do not need it.
Using with Firewalls
If you want clients to be able to connect to your Ubik/HTTP servers through firewalls, you must configure
the "external" address of your firewall when initializing the transport provider (as illustrated in the examples
above). The URL that is given to clients is thus NOT the address of the host/port on which our Ubik server is listening...
Then, of course, at your firewall, you need to forward the calls target at a Ubik server to the host/port
corresponding to that server.
Using with Load-Balancers
You should work with the servlet transport provider if you wish to use Ubik with load-balancers. To dispatch to the
appropriate servers, load-balancers typically either use cookies, SSL session IDs, or some other sort of string identiers
that are part of the request/response cycle between HTTP clients and servers. In the case of Ubik, it is important that once a
client has acquired a stub, all remote method calls be performed in the context of a session.
Indeed, when you export an object through HTTP, the stub that the client obtains for that object must connect to that
original object whenever a remote method call is performed using the acquired stub.
The best way to ensure that a HTTP session is indeed established, call the getSession(true)
method on the HttpServletRequest
instance every time your servlet receives a request.
Of course, the above explanation is generic, and you should discuss with your sysadmin in order to figure out what's most
appropriate...
|