Java Annotation and Processor to mark a method as so it can be called once and only once?

1k Views Asked by At

I need to be able to mark methods so that they throw a RuntimeException if they are called more than once.

I am trying to enforce some single assignment semantics and the number of parameters to my class is too large to put in a single constructor and I need to be able to make these classes JAXB aware as well, so the objects need to be mutable but I want to enforce single assignment semantics.

I am pretty sure I can do this with Aspects, but I would really like to be able to use my own Annotations processor instead.

I know how to do this with Decorators in Python.

How do I write an Annotation processor that can intercept calls to the annotated method at runtime and not just at compile time?

I think I am on to something with with Dynamic Proxies intercepting the method calls, I just need to figure out how to integrate them with my Annotation processor.

Dynamic Proxies require you to use an Interface, that is way to cumbersome, I have a CGLib MethodInterceptor working now, much less requirements on what gets intercepted and decorated, at the expense of adding a dependency.

5

There are 5 best solutions below

3
On

Nope, there's nothing ready-to-use. And AspectJ seems the only way to make it work in a more general manner. As JB Nizet noted - the annotation should have a parser to parse it.

However, I would advise for a better and simpler solution - the Builder pattern. What does it look like:

  • you have a FooBuilder (it may also be a static inner class) which is mutable and has a setter and getter for each of the fields
  • FooBuilder has a build() method that returns an instance of Foo
  • Foo has a constructor that takes only FooBuilder, and you assign each field there.

That way:

  • Foo is immutable, which is your end goal
  • It is easy to use. You only set the fields that you need. Something like:

    Foo foo = new Foo.FooBuilder().setBar(..).setBaz(..).build();
    

That way the builder can be JAXB-aware. For example:

FooBuilder builder = (FooBuilder) unmarshaller.unmarshal(stream);
Foo foo = builder.build();

JAXB objects need to be mutable, and your requirement is an immutable object. Hence the builder comes handy to bridge that.

9
On

Instead of using an annotation you can use.

assert count++ != 0;

You would need one counter per method.

0
On

You can configure JAXB to use field (instance variable) access using @XmlAccessorType(XmlAccessType.FIELD). This will allow you to do what you need to with the set method:

You can also use JAXB's XmlAdapter mechanism to support immutable objects:

0
On

I had a similar requirement. Long story short when you inject components in Spring the cyclic dependency situation like A depends on B and B depends on A is perfectly fine, but you need to inject these components as fields or setters. Constructor injection causes a stack overflow. Therefore I had to introduce a method init() for these components, which unlike constructors might be erroneously called more than once. Needless to say boilerplate code like:

private volatile boolean wasInit = false;
public void init() {
  if (wasInit) {
    throw new IllegalStateException("Method has already been called");
  }
  wasInit = true;
  logger.fine("ENTRY");
  ...
}

started to emerge everywhere. Since this is nowhere close to being a critical spot of the application, I made a decision to introduce an elegant thread-safe one-liner solution favoring conciseness over speed:

public class Guard {
  private static final Map<String, Object> callersByMethods = new ConcurrentHashMap<String, Object>();
  
  public static void requireCalledOnce(Object source) {
    StackTraceElement[] stackTrace = new Throwable().getStackTrace();
    String fullClassName = stackTrace[1].getClassName();
    String methodName = stackTrace[1].getMethodName();
    int lineNumber = stackTrace[1].getLineNumber();
    int hashCode = source.hashCode();
    // Builds a key using full class name, method name and line number
    String key = new StringBuilder().append(fullClassName).append(' ').append(methodName).append(' ').append(lineNumber).toString();
    System.out.println(key);

    if (callersByMethods.put(key, source) != null) {
      throw new IllegalStateException(String.format("%s@%d.%s() was called the second time.", fullClassName, hashCode, methodName));
    }
  }
}

Now, since I prefer building applications within DI frameworks it might sound natural to declare Guard as a component, then inject it, and call an instance method requireCalledOnce instead. But due to its universal flavor, static reference yields more sense. Now my code looks like this:

private void init() {
  Guard.requireCalledOnce(this);
  ...
}

and here is an exception upon the second invocation of init of the same object:

Exception in thread "main" java.lang.IllegalStateException: [email protected]() was called the second time.
    at my.package.Guard.requireCalledOnce(Guard.java:20)
    at my.package.MyComponent.init(MyComponent.java:232)
    at my.package.MyComponent.launch(MyComponent.java:238)
    at my.package.MyComponent.main(MyComponent.java:48)
0
On

This question shows some resemblance with question Applying CGLib Proxy from a Annotation Processor.

If you want to be able to change the behavior of the original source code in an annotation processor have a look at how http://projectlombok.org/ achieves this. The only downside IMO is that lombok relies on com.sun.* classes.

Since I need this kind of stuff myself I wonder if someone knows of a better way to achieve this, still using annotation processors.