Some Reporting tricks with class-transformations

Every time I used to see some duplication of code, I used to move that code to a new method. With Tapestry, you begin to think differently. Now every time I see duplication, my first thought is “Can I create a worker for it”.

In my current project, I am using a few new ones. So I thought why not share them with you.

@ReportException

My onSuccess methods usually are of the form


@InjectComponent
private Form form

@OnEvent(EventConstants.SUCCESS)
void success()
{
   try
   {
      do_my_stuff();
   }catch(MyException ex)
   {
      form.recordError(ex.getMessage());
   }
}

Here is how I wanted my code to look.


@ReportException
@OnEvent(EventConstants.SUCCESS)
void success()
{
   do_my_stuff();
}

So for implementation, we first need an annotation


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ReportException
{
}

and then a worker


public class ReportExceptionWorker implements ComponentClassTransformWorker2
{
    private Environment environment;

    public ReportExceptionWorker(Environment environment)
    {
        this.environment = environment;
    }

    @Override
    public void transform(PlasticClass plasticClass, TransformationSupport support,
        MutableComponentModel model)
    {
        for(PlasticMethod method : plasticClass.getMethodsWithAnnotation(ReportException.class))
        {
            method.addAdvice(new ReportExceptionAdvice());
        }
    }

    private class ReportExceptionAdvice implements MethodAdvice
    {
        @Override
        public void advise(MethodInvocation invocation)
        {
            ValidationTracker tracker = environment.peek(ValidationTracker.class);

            if(tracker != null)
            {
                try
                {
                    invocation.proceed();
                } catch(MyException ex)
                {
                    tracker.recordError(ex.getMessage());
                    ex.printStackTrace();
                }
            }
        }
    }
}

Here the advice gets the ValidationTracker from the Environment service and does the exception handling and reporting for us.

Finally the contribution in Module.


public static void contributeComponentClassTransformWorker(
        OrderedConfiguration<ComponentClassTransformWorker2> configuration)
    {
        configuration.addInstance("ReportExceptionWorker",
            ReportExceptionWorker.class);
    }

@ReportExceptionAsAlert

Another close use case is when you have an ajax call which may result in an exception but you just want to show the user a simple alert with the message.

The annotation


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ReportExceptionAsAlert
{
}


The Worker


public class ReportExceptionAsAlertWorker implements ComponentClassTransformWorker2
{

    private final AjaxResponseRenderer ajaxResponseRenderer;

    private final Request request;

    public ReportExceptionAsAlertWorker(
        AjaxResponseRenderer ajaxResponseRenderer,
        Request request)
    {
        this.ajaxResponseRenderer = ajaxResponseRenderer;
        this.request = request;
    }

    @Override
    public void transform(PlasticClass plasticClass, TransformationSupport support,
        MutableComponentModel model)
    {
        for(PlasticMethod method : plasticClass.getMethodsWithAnnotation(
            ReportExceptionAsAlert.class))
        {
            method.addAdvice(new ReportExceptionAsAlertAdvice());
        }
    }

    private class ReportExceptionAsAlertAdvice implements MethodAdvice
    {
        @Override
        public void advise(MethodInvocation invocation)
        {

            if(request.isXHR())
            {
                try
                {
                    invocation.proceed();
                } catch(final MyException ex)
                {
                    ajaxResponseRenderer.addCallback(new JavaScriptCallback()
                    {

                        @Override
                        public void run(JavaScriptSupport javascriptSupport)
                        {
                            javascriptSupport.addScript("alert('%s');", ex.getMessage());
                        }

                    });
                    ex.printStackTrace();
                }
            }
        }
    }
}

About these ads

Tagged: , ,

2 thoughts on “Some Reporting tricks with class-transformations

  1. Howard M. Lewis Ship February 3, 2012 at 10:43 PM Reply

    A few notes. Firstly, did you test ReportExceptionAsAlertWorker? It looks to me like it prevents the underlying method from being invoked UNLESS its an Ajax request; is that what you wanted?

    Secondly; rather than using alert(‘foo’) … why not use the AlertManager service instead? You’ll get a much better result on the client side.

    Finally, you are creating new advice for each advised method … but the advice is stateless, so there’s no reason you can’t have a final variable in your worker holding shared advice for all methods.

    • tawus February 4, 2012 at 12:52 AM Reply

      Yes, in my use case if the request is not ajax it should be ignored. The reason is to prevent a non-ajax submission in case the javascript is disabled, as the javascript confirmation is required.

      Reason for using alert() is to keep the UI consistent with the existing applications. Although I would have preferred AlertManager or jGrowls.

      Regarding a single instance for an advice, it is a pure MISTAKE. Will fix it in the post.

      As always thank you very much for your comments.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 90 other followers

%d bloggers like this: