OCSelot
- A Lightweight Java Middleware
<< Manual main page   The client >>

The proxy

is a client-side representative of a server-side counterpart called "subject".

Proxies come in two varieties, "class proxies" and "interface proxies". While playing the same role, they differ technically in a number of respects.

Class proxies

subclass their subject's classes, so that instances of the proxy class can stand in for instances of the subject class in practically every situation.

Creating a class proxy

The code for a class proxy is generated by the OCSClassProxyFactory.

This factory takes a class and generates a new subclass of it that provides corresponding constructors and an override for every method. To do this, the factory doesn't need the source of the base class, so you can generate class proxies of virtually every class, those of standard libraries included.

The factory can be called both from within a program and, more conveniently, from the command-line.

As has been shown in the Overview, to generate class proxy code for a class named my.pack.Clazz, you would enter at the command-line:

java -cp ocs.jar;ocs-tools.jar;. tw.net.ocs.OCSClassProxyFactory -class my.pack.Clazz
(omitting the classpath for my.pack for the sake of simplicity).

This generates the source file tw/net/ocs/proxy/MyPackClazz.java. Simply compile this file and make sure the generated tw.net.ocs.proxy.MyPackClazz is in the classpath.

Note that class proxy classes get placed in package tw.net.ocs.proxy and how their name is derived from the qualified name of their base classes.

The demonstrated way to generate the class proxy will suffice in many cases. There are a few options In addition: you can make selected methods cache their results (so that repeated invocations with the same arguments can be answered without a call to the remote subject), you can exclude selected methods from the proxy's code (which makes sense in case some action need not be answered by the subject), and you can specify the directory which should be the root for the tw/net/ocs/proxy source path.

Please see the API for details.

In order to forward requests to the proxy's subject, the class proxy overrides each of the subject class methods so that, in general, you can treat the proxy class just as you would the subject class.

There's a few things that deserve some attention, though. Let's look at constructors first and then at some modifiers to classes and methods.

Constructors

For each public constructor in the subject class, the proxy class provides two constructors: one has exactly the parameters of the base class constructor, the other has those plus an additional three parameters.

To illustrate, let's use my.pack.Clazz again, with some added detail:

package my.pack;

public class Clazz {
   private String s_;

   public Clazz(String s) {
      s_=s;
   }

   public String getString() {
      return s_;
   }
}

The proxy class derived from this, named tw.net.ocs.proxy.MyPackClazz, has these constructors: One "pure", with parameters as in Clazz,

public MyPackClazz(java.lang.String a)

the other "rich", with the added parameters ocsClient, create, and subjectID:

public MyPackClazz(
   java.lang.String a, // the only original parameter, used to create the subject
   tw.net.ocs.OCSClient ocsClient, // the OCSClient to use for networking
   boolean create, // create vs. attach to a subject
   String subjectID // the ID of the subject
)

The "rich" constructor

allows you to specify a particular OCSClient instance. The OCSClient is responsible for routing method invocations to the server, and as the local process may be in touch with several servers, there can be several OCSClient instances around.
If null is passed in for ocsClient, an OCSClient installed in the proxy class is used, if available, otherwise (typically) the current OCSClient standard instance.

There always exists an OCSClient instance and when you create a new instance, you can decide to make it the new standard instance to be used by subsequently created proxies. See the discussion of the client for details.

The create parameter allows you to choose between allocation or attachment. If set to true, a new subject will be allocated on the server side and then associated with the proxy. If set to false, the proxy will try to attach to an existing subject.

The subjectID parameter allows you to specify the ID of the subject to create or attach to. If no subject ID is specified, a new subject will be given an ID automatically or a subject with an arbitrary ID will be chosen, respectively.

Note the effects of different combinations of create, subjectID, and the "subject situation" on the server:

Digression: How to find out what happened?

In those cases where subject creation or attachment is not decided by the parameters to the constructor alone, you don't get an automatic feedback about what happened on the server. What you can do to find out, however, is check the situation on the server before and after the creation of the proxy.

Class OCSServer has the methods

boolean existsSubject(java.lang.Class a) 
boolean existsSubject(java.lang.Class a, java.lang.String b)

which inform their caller whether an instance of some class, of arbitrary or specified ID, exists on the server.

These methods can be called using the object returned by method

public OCSServerUserInterface getServerUserProxy()

of an OCSClient instance.

Note that this doesn't rule out every uncertainty, since another client, attaching to the same server, or even an operation on the server itself, might change the situation in between "your" calls...

Note also the method

public String getOCSSubjectID()

which is part of every proxy class and returns the symbolic ID of the subject associated with a proxy.

The "pure" constructor

uses a default OCSClient instance, asks the server to create a new instance of the subject class, and lets the server assign an ID to the subject.

Hence, this construction of a proxy of Clazz

new MyPackClazz("string")

is identical to that one:

new MyPackClazz("string",null,true,null)

Final classes

cannot be subclassed, hence no proxy class of the "class proxy" type can be created.

You can still make a final class serve as a remote subject, however, using an "interface proxy".

Abstract classes

can be subclassed, but the proxy subclass isn't equipped with a public constructor so that no instance can be allocated the normal way. There may, however, exist a "virtual constructor" among the static methods allowing to create a class proxy.

Note that in the absence of a virtual constructor, you can still use "interface proxies" to communicate with remote instances of abstract classes.

Final methods

cannot be overridden, as is well-known. The OCSClassProxyFactory doesn't unconditionally surrender, though. It creates methods with related names, allowing you to call the subject's final methods through the proxy.

To give an example: Clazz is derived from Object, so it inherited the final method

public final java.lang.Class getClass()

The proxy class MyPackClazz provides the method

public final java.lang.Class final_getClass()

whose invocation will lead to that of the final method getClass() on the remote Clazz-instance.

Make sure to really use these special methods instead of their originals since the original final methods are all there and, technically, nothing will prevent you from calling them. As they are not overriden, they will be executed on the proxy's base.

Static methods

can be used just as fine as instance methods: When invoked, they forward their invocation to the corresponding method of the remote subject class.

For class methods, OCSClient instances that may be associated to proxy instances are of course inaccessible. Instead, class methods can use an OCSClient which has been installed in the proxy class using the static method ocsSetClientForClassMethods(OCSClient c). If unspecified, the OCSClient standard instance is used.

By the way, remember your Java: Static methods in a subclass do not override corresponding static methods in their superclass. Therefore, to invoke a static method of a proxy class make sure to use that class, not its base.

Virtual constructors

For those not familiar with the term: a virtual constructor is a class method that returns an object of the same class (or a subclass thereof).

The proxy class implements virtual constructors in the desirable way: it delegates the method invocation to the server side, where it will be invoked on the subject class to create a subject, then the proxy class creates an instance of itself, associates it with this server-side subject, and returns it.

Therefore, virtual constructors of a proxy class can be used as expected: like normal proxy constructors, they return a proxy associated with a subject.

Private, default, and protected methods

are not included in the interface of the proxy class.

Superclass methods

are included in the proxy class and overridden just as those declared in the subject class itself. So their invocations are delegated to the server side just as any other.

Interface proxies

do not subclass their subject's class but implement one or more interfaces that declare methods of the subject class.

Note that an interface implemented by an interface proxy need not occur in the implements section of the subject class! It suffices that the methods on both sides have the same signatures. This is a notable fact, since it allows you to create interface proxies for all classes, existing interfaces or not.

Creation

Interface proxies are created by the OCSInterfaceProxyFactory. Other than the OCSClassProxyFactory, this factory does not generate source code, but returns proxy objects at runtime, ready for use.

To give an example, let's assume there were an interface my.pack.ClazzIF which declares the methods of my.pack.Clazz. To create an interface proxy that implements my.pack.ClazzIF and attaches to an existing my.pack.Clazz subject named "clazzy", call

my.pack.ClazzIF p = (my.pack.ClazzIF)OCSInterfaceProxyFactory.generateProxyFor(
   my.pack.ClazzIF.class,	// the interface to implement 
   my.pack.Clazz.class,  // the class of the subject to talk to 
   "clazzy" // the name of the subject
);

The method returns an object that can be cast to the implemented interface. The invocations of methods declared by the interface get forwarded to the remote subject.

For variants of this method (you can make the proxy implement more than one interface, select a particular OCSClient, and check whether interface and subject class do perfectly match), see the API.

Interface proxies differ from class proxies in that they can't allocate a subject. For want of a constructor, an interface proxy can only attach to an existing subject. This must be provided on the server by other means, by creating it from the client side with a class proxy or by adding it to the OCSServer directly.

Generate interfaces

If there is a class a subject of which you want to address through an interface proxy, but which per se isn't equipped with an appropriate interface, you can simply create one. Use class OCSInterfaceFactory, contained in "ocs-tools.jar", for the purpose.

In the simplest case, e.g. to create an interface for class my.pack.Clazz, run

java -cp ocs.jar;ocs-tools.jar tw.net.ocs.OCSInterfaceFactory -class my.pack.Clazz

This will generate the file "MyPackClazzIF.java". You compile this file, make sure the result is in the classpath, and then let the OCSInterfaceProxyFactory generate an interface proxy implementing the new interface.

See the API for details and variants of interface generation.

Class vs. interface proxy: when to use which?

Frequently, both types of proxies can be used, so that the choice is a matter of taste. There are differences between the two, however, giving one priority over the other in certain contexts.

Type situation

If you have to deal with a given method which needs a parameter of the subject class, you can't but use a class proxy since its type is a subclass of the subject class and hence fits in whereas the interface proxy doesn't.

Vice versa: if the method requires a parameter of the interface type, you're forced to use an interface proxy implementing this interface.

Subject creation

If you can't or don't want to add a subject to the server from inside the server process, a class proxy is a good choice since, simply by allocating an instance, you can create a subject on the server. This an interface proxy cannot do.

It should be noted, however, that subjects may also be installed on the server from the client side with the help of an OCSServerUserInterface that can be obtained from the client. This allows you to combine using an interface proxy with doing everything client-side.

Proxy base constructor

Since a class proxy's class is a subclass of the subject class, the creation of a class proxy is inevitably accompanied by the construction of its base.

In order to avoid unwanted side effects as much as possible and create proxies that confine themselves to forwarding their invocations, the OCSClassProxyFactory writes constructor code with the simplest base class constructors it can. It does so by choosing super()-calls with the least possible number of arguments.

But the best may be not good enough. In this case, the more lightweight interface proxy, which doesn't introduce any client-side overhead, will be superior.

Exceptions

The invocation of methods on proxies can result in an OCSRuntimeException being thrown. This indicates an error on the server side, like missing authentication, the inability to find a subject to attach to, the attempt to create a subject of a class which users are not allowed to instantiate, or other.

An unchecked exception is used because neither class nor interface proxies allow the use of checked, declared exceptions:

With class proxies, the exception is thrown by methods that override superclass methods. Since checked exceptions are part of a method's signature, the declaration of a checked exception would yield a different signature and hence break the override.
As regards interface proxies, they don't expose methods in a way that would allow the declaration of exceptions in the first place.
Therefore, the use of an unchecked exception is the only option.

Since the compiler doesn't enforce it, it is up to the implementer to take the necessary precautions and enclose proxy method invocations in:

try {...} catch (OCSRuntimeException x} {...}
.
Last modified Jan 18, 2006