|
The Java Specialists' Newsletter
Issue 005 2001-01-18
Category:
Language
Java version: Dynamic Proxies - Short Tutorialby Dr. Christoph G. Jung
Welcome to our fifth "The Java(tm) Specialists' Newsletter",
published by Maximum Solutions, the company who may not be
good at Java(tm), but Java is all we do, so we call ourselves
"Java Specialists". Thank you for not unsubscribing in
hordes (hmmm, I have not told you HOW to unsubscribe, so maybe
that's why ;-)
Isn't it funny how employment agencies ask you ALL the things
you know, like "which word processor have you used?" and "what
version of Turbo Pascal do you know?" What relevance is that
to today's newsletter? None.
We've had an interesting week sorting out small
Java problems and fixing bugs in my software. Many thanks
to Dr. Christoph for writing this week's newsletter, which
I really enjoyed reading. For this I owe you an Esplendido!
Christoph started writing Java code at the end of 1999 for a
very innovative ERP company in Germany called
infor Business
Solutions AG, and has quickly become a real fundi at writing
application server code. Prior to that, he did a doctorate
in Computer Science with emphasis on Robotics and Artificial
Intelligence. I don't know where Java comes in, but a PhD
is general education that is useful for many spheres of life,
and it does not really matter what your topic was. Believe me,
my PhD topic was even less useful ;-)
Enough from me, here is a "Short Tutorial to the Java2
platform Dynamic Proxy Facilities" ....... I wish we had known
about this before we started pasting access control onto our
570'000 line application ......
Short Tutorial to the Java2(TM) platform Dynamic Proxy Facilities
C.G.Jung
A Bad Example
One the most useful design patterns when it comes to building
extensible frameworks turns out to be the PROXY pattern. For
example, imagine your notoriously paniqued project manager
insisting on your introducing new security features into your
companies existing business model:
/**
* A Class representing a single salary
*/
class Salary {
Employee employee;
long amount;
public long getAmount() {
return amount;
}
}
Your straightforward approach to shield the objects from
exporting sensible information, such as exorbitant management
salaries, to ordinary users, such as you humble worm, would
be to introduce a dedicated SecurityChecker (this is not a
java.lang.Security tutorial, so please excuse the following
very naive code):
/**
* Realises The Systems Security Policies
*/
class SecurityChecker {
java.security.Permission manyBucksPermission;
long check(long amount) throws SecurityException {
if(amount>1000000)
SecurityManager.checkPermission(manyBucksPermission);
return amount;
}
}
/**
* A Modified Salary that is "Too-Much-Bucks"-Aware
*/
class Salary {
Employee employee;
long amount;
// ugly reference
static SecurityChecker sc;
public long getAmount() {
// isnt this the wrong place for doing that?
return sc.check(amount);
}
}
Now your project manager is happy because of being able to ask
for his next salary increase. But, you (and your colleague that
is the application but not the framework specialist) will quite
likely curse him three times when he returns with next terms
project plan that includes
- heavily extending the business logic (amount will no more
be stored immediately, but computed by a very complex,
profile-oriented function, an additional int getAmount()
is needed in Salary, an Order should be implemented, etc.)
- expanding the successfully introduced security policies
(maybe a check(Customer customer) should be added that
restricts access to data associated with particular
"high-sensitive" customers).
The reason for your deadly wishes is of course that in the above
solution, we have heavily mixed up application data and methods
(such as amount and long getAmount() ) with generic access
control (such as check(long amount)) Extending and updating
the 200 classes with 1000 methods of application logic involves
understanding of when and how to correctly apply the available
security checks. Because only you as the framework pro are able
to do that, you will end up with a quite suboptimal division
of responsibility and code.
The Proxy Solution
In order to free your colleague from the heavy burden of the
security framework and enable him to concentrate on his business,
the better choice is hence to leave his Salary class alone, but
rather provide him with an inherited PROXY class that intercepts
any getAmount() calls to interface the security framework:
/**
* proxy that behaves as a salary
* and that performs a security check
* before delegation
*/
class SalarySecurityProxy extends Salary {
// this is not application, hence the reference is ok
static SecurityChecker sc;
// the object to which we delegate the calls to
Salary realObject;
public long getAmount() {
// framework call after delegation
return sc.check(realObject.getAmount());
}
}
Now, your collegue is free to change and extend the application
logic to realise project goal a (e.g., manipulating amount
and getAmount()), while you can add additional independently
further proxies and intercepting/ delegation calls to pursue
project goal b. Strike.
If your initial application design has been smart by using
mucho, mucho interfaces (you know that you should do that
anyway, donīt you?), PROXY can be even made compatible with
complex inheritances:
/** use interfaces everywhere in your app-logic */
interface Salary {
...
}
/** and provide proper implementations */
class SalaryImpl implements Salary{
...
}
/** as well as framework proxies */
class SalarySecurityProxy implements Salary{
...
}
/** when you extend your model ... */
interface ManagementSalary extends Salary {
...
}
/** ... afterwards ... */
class ManagementSalaryImpl extends SalaryImpl {
...
}
/** ... it's very easy */
class ManagementSalarySecurityProxy extends SalarySecurityProxy {
...
}
Double Strike.
The possibilities of the proxy are nearly endless and range
from transparent SECURITY over LAZY RETRIEVAL, POOLING,
CACHING, and TRANSACTIONAL MARKUP up to DISTRIBUTION (remember
RMI stubs?). It is highly useful to combine such differently
focused proxies in a CHAIN OF RESPONSIBILITY to a complex, at
the same time extensible framework, e.g., SalarySecurityProxy
(performs security checks) --> SalaryPersistenceProxy (retrieves
the "real" salary from a database) --> Salary
Often, PROXY is used in combination with the FACTORY pattern in
order to allow the transparent insertion of proxies and chains
into the application logic (your colleague wouldīnt be bothered
by their construction at all ... no more endless discussions
... no more fruitless explanations ... must be like heaven).
Why Dynamic Proxies are needed
However, the main drawback of PROXY is that for each application
class, each application method, and each additional framework
functionality, you have to implement and maintain (sic!) a
dedicated interceptor class/method. Depending on the output of
your colleague, this can be quite a pain in the ass ... (BTW:
Iīm sure that some of you already collided with the winding
RMIC procedure which actually is an automated proxy-generator
and -compiler not to speak of its ubiquitious stub-deployment
problem)
If reminding the mostly general nature of framework code, you
ask yourself whether there is not a better solution to this
issue. Actually, there is. It is called DYNAMIC PROXIES and
one of the wonderful invents that came to your harddisk with
your JRE/JDK1.3 installation.
DYNAMIC PROXIES heavily rely on the already matured
reflection capabilities of the Java2 platform. They separate
the "interception logic", that is the part of simulating an
arbitrary interface, from the "invocation logic", that is the
part of calling the framework before or after delegating any
method call to the proper application target.
The "invocation logic", such as to connect to a method-based
security checker, is realised on purpose by implementing the
freshly introduced java.lang.reflect.InvocationHandler interface
(more specifically, its reflection-based invoke-Method):
/**
* a generic, method-based security handler
* that is not only useful for Salaries
*/
class SecurityHandler extends InvocationHandler {
static SecurityChecker sc;
final Object realObject;
/** contructor accepts the real subject */
public SecurityHandler(Object real) {
realObject=real;
}
/** a generic, reflection-based secure invocation */
public Object invoke(Object target,
java.lang.reflect.Method method, Object[] arguments)
throws Throwable {
try{
// call framework and then reflect the app-logic
return sc.check(method).invoke(realObject,arguments);
} catch(java.lang.reflect.InvocationTargetException e) {
// reconvert nested application exceptions
throw e.getTargetException();
}
}
}
The "interception logic" is already hardwired into the 1.3 vm and
the new java.lang.reflect.Proxy class. All it requires to combine
it with the above SecurityHandler code is to call the static:
ManagementSalary salary;
ManagementSalary smellsLikeSalary=(ManagementSalary)
java.lang.reflect.Proxy.newProxyInstance(
Salary.class.getClassLoader(),
new Class[] {ManagementSalary.class},
new SecurityHandler(salary));
The thus constructed smellsLikeSalary object now behaves like
an implementation of ManagementSalary. In fact, it *IS*
an implementation of ManagementSalary whose class has been
dynamically constructed at runtime, hence dynamically. You can
inspect it (smellsLikeSalary instanceof ManagementSalary),
cast it ((Salary) smellsLikeSalary) and call methods on it
(smellsLikeSalary.getAmount()). Method calls will in turn be
dispatched to the invoke method of the given invocation handler
(where in our example, target points to smellsLikeSalary, method
points to the internal Salary.getAmount() representation, and
the arguments array is empty). Return values and exceptions
will be automatically converted/casted into the compatible
types as given in the interface signature. Thatīs it.
(No, actually thatīs not: java.lang.reflect.Proxy has a lot
more advanced features and there are some tricky considerations
when building proxies for multiple/conflicting interfaces - but
thatīs too much for now, please refer to the API documentation
for these issues.)
If PROXY has broad applicability, this holds for DYNAMIC PROXIES
even more. For example, you could imagine a PersistenceHandler
that hides JDBC-access to a row in a relational database table
without requiring any additional object instances. For example,
you could think of a JRMPRemoteHandler that implements stub-less
(HHHOOOORRRAAAYYYY ... no more RMIC) RMI-access to a remote
object via the TCP/IP-based Java Remote Method Protocol.
The latter usage is especially supported by DYNAMIC
PROXIES, such as smellsLikeSalary actually being
serializable, if the tied handler classes are. In Rickard's
SmartWorld,
you will find executable code and documentation that extends
SUN-JRMP1.2 with suitable handlers that can be distributed
through naming services, files, etc. and that can be
straightforwardly enriched by load-balancing, failover,
clustering, and connection-pooling features.
The first (Open Source!) EJB product that uses these handlers
instead of remote stubs is jBoss whose
complementary server container design is indeed realised as
flexible CHAIN OF RESPONSIBILITY ... Long live the (DYNAMIC)
PROXIES!
Thanks for that, Christoph...
Next week will be a bit shorter and I will show you how to put
method bodies in Java interfaces, which you should never need
to do, IF the framework you are working within is perfect.
In a less-than-perfect world such a technique can be useful,
as fellow fish-chaser Niko Brummer told me. So, hold your
breath until next week.
Regards
Heinz and Christoph
Language Articles
Related Java Course
|