// ********* ExtendedServer ******** public class ExtendedServer extends Server { private void shakeRattleAndRoll() { System.out.println("SHAKE ... RATTLE AN' ROLL!!!"); } private void getUpAndBoogy() { System.out.println("GET UP ... AND BOOOOOOOGGGYYYYY!!!"); } private class ShakeRattleAndRoll extends VirtualMethod { public void call() { shakeRattleAndRoll(); } } private class GetUpAndBoogy extends VirtualMethod { public void call() { getUpAndBoogy(); } } public ExtendedClient getClient() { return new Client(new ShakeRattleAndRoll(), new ShakeRattleAndRoll()); } } // ********* ExtendedClient ******** public class ExtendedClient extends Client { private VirtualMethod shakeRattleAndRoll, getUpAndBoogy; ExtendedClient(VirtualMethod shakeRattleAndRoll, VirtualMethod getUpAndBoogy) { this.shakeRattleAndRoll = shakeRattleAndRoll; this.getUpAndBoogy = getUpAndBoogy; } public void demonstrateAccess() { shakeRattleAndRoll.call(); getUpAndBoogy.call(); } }
There are many variations of the Access Control pattern. The following version allows only Server
to call a method on Client
that inserts a VirtualMethod
into Client
. Calling this method multiple times (to insert multiple VirtualMethod
s) eliminates the clutter and inconvenience demonstrated above, and makes it easy to increase Client
's access to Server
's private members.
Access Control pattern improved
Here is the revised version of the Accesss Control pattern:
// ********* VirtualMethodScope ******** package vault; public abstract class VirtualMethodScope { protected abstract class VirtualMethod { protected abstract void call(); } }
Both Client
and Server
descend from VirtualMethodScope
, and both also live in a different package than VirtualMethodScope
. Therefore, within the scope of the working package (the package of Client
and Server
), only Client
, Server
, and their descendents can access VirtualMethod
, so this version's result is tighter access control. However, the principle argued above still holds, since a Client
developer can still expose a public
method that calls the private
method we've entrusted him with!
// ********* ClientBaseClass ******** package vault; import java.util.HashMap; public abstract class ClientBaseClass extends VirtualMethodScope { private HashMap virtualMethods = new HashMap(); final void addMethod(String methodName, VirtualMethod method) { virtualMethods.put(methodName, method); } protected final void call(String methodName) { VirtualMethod method = (VirtualMethod)virtualMethods.get(methodName); method.call(); } }
The ClientBaseClass
's addMethod()
has "friendly" access, and is therefore visible only within the vault
package. This effectively restricts anyone from calling addMethod()
, except for the ServerBaseClass
listed below. Also, note that call()
is protected
, so that Client
(which does not reside in the vault
package) can call its own call()
method. call()
is also final
, meaning we don't trust the Client
developer to modify it. But the Client
developer can still expose a public
method that calls call()
!
// ********* ServerBaseClass ******** package vault; public abstract class ServerBaseClass extends VirtualMethodScope { final protected void addMethod(ClientBaseClass client, String methodName, VirtualMethod method) { client.addMethod(methodName, method); } }
ServerBaseClass
's addMethod()
is protected
, so that Server
(which does not reside in the vault
package) can call its own addMethod()
method. Declaring this method final
prevents it from being overridden with less restrictive access. Within the scope of the working package (the package of Client
and Server
), only Client
, Server
, and their descendents can access this addMethod()
method:
// ********* Client ******** public class Client extends vault.ClientBaseClass { // NOT in the vault package!!! public void demonstrateAccess(String methodName) { call(methodName); } } // ********* Server ******** public class Server extends vault.ServerBaseClass { // NOT in the vault package!!! private void shakeRattleAndRoll() { System.out.println("SHAKE ... RATTLE AN' ROLL!!!"); } private class ShakeRattleAndRoll extends VirtualMethod { protected void call() { shakeRattleAndRoll(); } } public Client getClient() { Client client = new Client(); addMethod(client, "shakeRattleAndRoll", new ShakeRattleAndRoll()); return client; } }
With this pattern's improved version, you can easily customize both Client
and Server
behavior, simply by extending Server
:
// ********* ExtendedServer ******** public class ExtendedServer extends Server { private void getUpAndBoogy() { System.out.println("GET UP... AND BOOOOOGGYYYYY!!!"); } private class GetUpAndBoogy extends VirtualMethod { protected void call() { getUpAndBoogy(); } } public Client getClient() { Client client = super.getClient(); addMethod(client, "getUpAndBoogy", new GetUpAndBoogy()); return client; } }
Infinite resolution
Inner classes may extend regular (non-inner) classes, and the inner child may be private
even though the parent is public
. This permits access to public
interface methods, while restricting access to the implementation of that interface. Therefore, a "server" class may selectively offer "client" classes instances of the server's private
, inner implementation of that public
interface, thereby granting access to certain server's private
fields and methods. The resolution of this technique is essentially infinite -- any client class in any package may be granted or denied access to the server's sensitive private methods.
Learn more about this topic
- Access the source code for this article
http://www.javaworld.com/jw-09-2001/access/HighSpeedFineGrainedAccessControl.jar - Sun's Java tutorial discussion on Java's built-in access control
http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html - Richard Baldwin compares access control in Java and C++ on his Website
http://home.att.net/~baldwin.dick/Intro/Java040.htm - "The Truth about Private," Tony Sintes (JavaWorld, October 2000) explains how Java handles inheritance and access control
http://www.javaworld.com/javaworld/javaqa/2000-10/03-qa-1020-private.html - "Explore the Dynamic Proxy API," Jeremy Blosser (JavaWorld, November 2000) introduces a way to add access control to your Java objects using the dynamic proxy
http://www.javaworld.com/javaworld/jw-11-2000/jw-1110-proxy.html - "Secure a Web Application, Java-Style," Michael Cymerman (JavaWorld, April 2000) delegates security to the Java Access Control Model
http://www.javaworld.com/javaworld/jw-04-2000/jw-0428-websecurity.html - Read more Security articles in JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-security-index.shtml - Read about other Design Patterns in JavaWorld's Topical Index
http://www.javaworld.com/channel_content/jw-patterns-index.shtml - Subscribe to JavaWorld's free weekly email newsletters
http://www.idg.net/jw-subscribe - You'll find a wealth of IT-related articles from our sister publications at IDG.net