Search code examples
javadesign-patternsproxy-classes

Using Proxy pattern to write a server a good idea?


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?


Solution

  • 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.