Using Proxy pattern to write a server a good idea?

743 views Asked by At

For a school project, I need to write a simple Server in Java that continuously listens on an incoming directory and moves files from this directory to some place else. The server needs to log info and error messages, so I thought I could use the Proxy pattern for this. Thus, I created the following ServerInterface:

public interface ServerInterface extends Runnable {

    public void initialize(String repPath, ExecutorInterface executor, File propertiesFile) throws ServerInitException;

    public void run();

    public void terminate();

    public void updateHTML();

    public File[] scanIncomingDir();

    public List<DatasetAttributes> moveIncomingFilesIfComplete(File[] incomingFiles);

}

Then I've created an implementation Server representing the real object and a class ProxyServer representing the proxy. The Server furthermore has a factory method that creates a ProxyServer object but returns it as a ServerInterface.

The run-method on the proxy-object looks like this:

@Override
    public void run(){

        log(LogLevels.INFO, "server is running ...");

        while( !stopped ){

            try {

                File[] incomingContent = scanIncomingDir();
                moveIncomingFilesIfComplete(incomingContent);
                updateHTML();
                pause();

            } catch (Exception e) {
                logger.logException(e, new Timestamp(timestampProvider.getTimestamp()));
                pause();
            }

        }

        log(LogLevels.INFO, "server stopped");
    }

The functions that are called within the try statement simply log something and then propagate the call to the real object. So far, so good. But now that I've implemented the run-method this way in the proxy object, the run-method in the real object becomes obsolete and thus, is just empty (same goes for the terminate-method).

So I ask my-self: is that ok? Is that the way the proxy pattern should be implemented?

The way I see it, I'm mixing up "real" and "proxy"-behaviour ... Normally, the real-server should be "stuck" in the while-loop and not the proxy-server, right? I tried to avoid mixing this up, but neither approaches were satisfying:

  • I could implement the run-method in the real object and then hand over the proxy object to the real object in order to still be able to log during the while-loop. But then the real object would do some logging, which is I tried to avoid by writing a proxy in the first place.

  • I could say, only Proxy-Server is Runnable, thus deleting run and terminate from the Interface, but this would break up the Proxy pattern.

Should I may be use another design? Or I am seeing a problem where there is none?

4

There are 4 answers

3
Jasper Blues On BEST ANSWER

You're definitely thinking in the right way. You've hit upon an interesting notion.

Features like logging, as you've described, are an example of what we call cross-cutting concerns in Aspect Oriented programming.

  • A cross-cutting concern is a requirement that will be used in many objects.

. . therefore, they have the tendency to break object oriented programming. What does this mean?

  • If you try to create a class that is all about moving files from place A to place B, and the implementation of a method to do that first talks about logging (and then transactions, and then security) then that isn't very OO is it? It breaks the single responsibility principle.

Enter Aspect Oriented Programming

This is the reason we have AOP - it exists to modularize and encapsulate these cross-cutting concerns. It works as follows:

  • Define all the places where we want the cross-cutting feature to be applied.
  • Use the intercept design pattern to "weave" in that feature.

Ways we can "weave" in a requirement with AOP

  • One way is to use a Java DynamicProxy as you've described. This is the default in for example the Spring Framework. This only works for interfaces.
  • Another way is to use a byte-code engineering library such as asm, cglib, Javassist - these intercept the classloader to provide a new sub-class at runtime.
  • A 3rd way is to use compile-time weaving - to change the code (or byte-code) at compile-time.
  • One more way is to use a java agent (an argument to the JVM).

The latter two options are supported in AspectJ.

In Conclusion:

It sounds as though you're moving towards Aspect Oriented Programming (AOP), so please check this out. Note also that the Spring Framework has a lot of features to simplify the application of AOP, though in your case, given this is a school assignment, its probably better to delve into the core concepts behind AOP itself.

NB: If you're building a production-grade server, logging may be a full-blown feature, and thus worth using AOP. . in other cases its probably simple enough to just in-line.

2
yechabbi On

You can make your proxy aware of the real object. Basically your proxy will delegate the call to run method to the real implementation.

Before the delegation, the proxy first logs the startup. After delegation, the proxy logs the "shutdown":

// Snapshot from what should look like the run method implementation
// in your proxy.

public ServerInterfaceProxy(ServerInterface target){
    this.proxiedTarget = target;
}

public void run(){
    log(LogLevels.INFO, "server is running ...");
    this.proxiedTarget.run();
    log(LogLevels.INFO, "server is running ...");
}

This implementation can also be perceived as a Decorator pattern implementation. IMHO, I believe that to some extent (when it comes to implementation) Proxy and Decorator are equivalent : Both intercept/capture behavior of a target.

3
kittylyst On

Look at Java 7's WatchService class.

Using Proxy behaviour for this is almost certainly overkill.

1
user987339 On

You should use Observer pattern in this case:

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

Your Observable will observe changes in directory, by time pooling, or as already was suggested here, with WatchService. Changes of directory will notify Observer which will take action of moving files. Both Observable and Observer should log their actions.

You shold also know that Observer pattern became a part of Java JDK by implementing java.util.Observable and java.util.Observer.