views:

94

answers:

6

I have a lot of methods for logging, like logSomeAction, logAnotherAction etc.

Now I want all these methods make a small pause after printing messages (Thread.sleep).

If I do it manually, I would do something like this:

//before:
public static void logSomeAction () {
   System.out.println (msg(SOME_ACTION));
}

//after:
public static void logSomeAction () {
   System.out.println (msg(SOME_ACTION));
   try {
      Thread.sleep (2000);
   } catch (InterruptedException ignored) { }
}

I remember that Java has proxy classes and some other magic-making tools. Is there any way avoid copy-n-pasting N sleep-blocks to N logging methods?

+6  A: 

You could use Aspects to add extra "orthogonal" functionality to your methods.

If that sounds too esoteric, a simpler, down-to-earth solution would be to add the sleep in a separate method, then call that method in each of your logging methods. The first time you do this, you need to touch each method, but the next time if you want to modify the extra behaviour or add something else, you can do it in one single place.

Péter Török
Much as I dislike aspects, this is exactly what they're for. Note that you need to use a separate compiler, as aspects aren't part of the standard Java implementation; [AspectJ](http://www.eclipse.org/aspectj/) is a common one for Java
Michael Mrozek
@Péter Török: I like the idea of aspects, I've even learnt something about aspects in Spring. But I don't have enough knowledge yet to do this solution myself but I need to complete the task. Do you have appropriate code or at least sscce? which dependencied should be resolved?
Roman
Aspects have the disadvantage that the code you see in your editor, is not necessarily the code being run.
Thorbjørn Ravn Andersen
A: 

Make a utility class that has a static SleepFor method which includes your try ... catch block and call that from every method you want a sleep in?

Donnie
+2  A: 

It looks like you want to use Aspect Oriented Programming. You could use Spring for AOP, or AspectJ.

FarmBoy
No, I dont. I want to use plain old proxies http://java.sun.com/j2se/1.3/docs/guide/reflection/proxy.html but I don't remember the syntax and I've never had enough experience with them. btw, AOP also uses proxies under the hood (maybe some other proxy implementations but idea is the same).
Roman
Spring AOP yes, but AspectJ does not (it is using bytecode-weaving)
arjan
A: 

Replace all the System.out.println(msg(SOME_ACTION)); with printAndWait(SOME_ACTION); You should be able to do that with find and replace. Then create a method

public static void printAndWait(Object someAction) {
   System.out.println (msg(someAction)); 
   try { 
      Thread.sleep (2000); 
   } catch (InterruptedException ignored) {
      Thread.currentThread.interrupt();
   } 
} 

That way the code appears once and you can change it easily in one place.

Peter Lawrey
A: 

Replace all of your logSomeAction() methods with a single logAction(Action a) method. This way, when you add more actions in the future, you will not be repeating your code for handling the action log and the thread sleep.

Finbarr
+1  A: 

The OP mentions in a comment that the preferred solution is to use plain java proxies. The current code is implemented as static methods - for java proxies to be of any use, the logger class will need to be reworked as an interface. Something like this:

public interface SomeActionLogger
{
   void logSomeAction();
   void logSomeOtherAction();
   // etc..
}

You then create your concrete implementation

public class SystemOutActionLogger implements SomeActionLogger
{
   public void logSomeAction () {
      System.out.println (msg(SOME_ACTION));
   }
}

You can then have Java proxies wrap the SomeActionLogger interface

class DelayAfterInvocationHandler implements InvocationHandler
{
    private Object delegate;
    private int duration;

    DelayAfterInvocationHandler(Object delegate, int duration)
    {
        this.delegate = delegate;
        this.duration = duration;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        Object returnValue = method.invoke(delegate, args);
        Thread.sleep(duration);
        // you may want to catch InterruptedEception
        return returnValue;
    }
}

To hide some of the not-so-pretty proxy code, you can then have a method that wraps your logger to create the delay, e.g.

public ActionLogger addDelay(SomeActionLogger logger, int delay)
{
    return (ActionLogger)Proxy.newProxyInstance(
       impl.getClass().getClassLoader(),
       new Class[] { SomeActionLogger.class }, 
       new DelayAfterInvocationHandler(logger, delay));
}

So you then write

SomeActionLogger log = addDelay(new SystemOutActionLogger(), 2000);

Note that the DelayInvocationHandler is orthogonal to the logging interface - it can be used to add delay to any interface. You might then create a generic wrapping method like this:

public <T> T addDelay(T delegate, int delay, Class<T> interfaceType)
{
    return (T)Proxy.newProxyInstance(
       delegate.getClass().getClassLoader(),
       new Class[] { type }, 
       new DelayAfterInvocationHandler(delegate, delay));
}
mdma