Adam
Adam

Reputation: 123

Proceed with variable arguments with AspectJ

I'm trying to normalize URIs across an application using AspectJ. I'm catching every call that is made to a method passing in a java.net.URI parameter using this code:

Object around() : execution(* *(..,java.net.URI,..)) {
    for ( Object arg : thisJoinPoint.getArgs() ) {
        if ( arg instanceof URI ) {
            // normalize
        }
    }
    return proceed();
}

However, since URI is immutable, I can't swap in the normalized value into the existing object. What I need is to call proceed with the new, normalized URI objects (and possibly passing along the other arguments unchanged). However, the proceed call only lets me pass along arguments that were collected by the join point. Is there any way to accomplish this for a variable number of arguments (mostly being interested in any URI argument(s), but willing to collect and pass along all arguments)?

Upvotes: 9

Views: 8265

Answers (3)

Raul Bertone
Raul Bertone

Reputation: 146

What follows should be a comment not a full answer but my limited rep doesn't allow me to comment...

The code in the question and also in the answer by Sean Patrick Floyd won't work because there can only be one occurrence of the wildcard ".." in a method signature pattern. A short explanation of why it is so can be found here.

Upvotes: 1

kriegaex
kriegaex

Reputation: 67297

I know this is an old question, but officially still unresolved. I just saw it, so I am going to answer it:

You mentioned that URIs are immutable. So basically this is a hint towards the right solution: intercept object creation (constructor call) and check the result in an around() advice. If it is OK, pass on the unchanged URI, otherwise create a new one, fixing or canonising whatever is wrong.

Assume you have this application class creating URIs:

package de.scrum_master.aspectj.sample.uri;

import java.net.URI;
import java.net.URISyntaxException;

public class URIApp {
    public static void main(String[] args) throws URISyntaxException {
        URI[] uris = {
            new URI("https://google.com?search=olympics"),
            new URI("http://yahoo.com/mail/login"),
            new URI("https://facebook.com/user?name=kriegaex"),
            new URI("http://stackoverflow.com/questions/123456")
        };
        for (URI uri : uris)
            System.out.println(uri);
    }
}

Further assume that we consider all URIs with a scheme/protocol of "http" insecure and thus wrong. We want to fix this by replacing the URI with another one using "https". We will do this in an aspect:

package de.scrum_master.aspectj.sample.uri;

import java.net.URI;
import java.net.URISyntaxException;

public aspect URIFixer {
    pointcut uriCreation() : call(URI.new(..)) && !within(URIFixer);

    URI around() throws URISyntaxException : uriCreation() {
        URI result = proceed();
        return isOk(result) ? result : fix(result);
    }

    boolean isOk(URI uri) {
        return "https".equals(uri.getScheme());
    }

    URI fix(URI uri) throws URISyntaxException {
        return new URI("https", uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment());
    }
}

I hope this answers your question, even though belatedly. I thought I should document the solution here for further reference by other users who might find it.

Upvotes: 0

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 298838

You need to change your advice's signature:

Object around(Object[] args) : execution(* *(..,java.net.URI,..)) && args(args) {
    for(int i = 0; i < args.length; i++){
        Object arg = args[i];
        if ( arg instanceof URI ) {
            args[i] = normalizeUrI((URI)arg);
        }
    }
    return proceed(args);
}

Update: the above code doesn't work unfortunately. But this should:

Object around() throws URISyntaxException : execution(* **.*(..,URI,..)) {
    final Object[] args = thisJoinPoint.getArgs();
    for(int i = 0; i < args.length; i++){
        final Object arg = args[i];
        if ( arg instanceof URI ) {
            args[i] = normalizeUrI((URI)arg);
        }
    }
    try{
        return ((ProceedingJoinPoint)thisJoinPoint).proceed(args);
    } catch(final Throwable e){
        throw new IllegalStateException(e);
    }
}

Upvotes: 3

Related Questions