Skip to content

XML Attributes on Annotations

Peter Kriens edited this page Mar 20, 2015 · 4 revisions

Requirements

  • Provide general XML attributes to annotation processors that create XML
  • Support adding attributes to any element in the generated XML
  • Custom annotations
  • Support multiple XML Attribute annotations per element

Nice to have

  • Type safe
  • Minimal work for the annotation reader
  • Attributes fully generic

Current State

The currently proposed model is:

@ObjectClassDefinition
@Attribute(attributes = {"foo=bar"}, namespace = "org.foo/xmlns/foo/1", prefix = "foo")
@Attributes({@Attribute(attributes = {"foo1=bar1"}, namespace = "org.foo/xmlns/foo/2", prefix = "foo")})
public static interface TestExtensions { }

A bridge extension that is aware of ObjectClassDefinition and Attributes is then used to add the attributes.

Problems

  • Too many strings in the annotations which are very hard to get right
  • Too much boilerplate
  • Afraid of explosion in bridges

Proposal

First, how should it look like? Assume we have a DS special implementation that can support field injection. How do we want this to look like?

@Component
public MyComp {
  @FieldInjection(dynamic=true)
  FooService   fooService;

  ...
}

Beautiful! How would this work? We must define the annotation and annotate it with the XML Attribute annotation:

@XMLAttribute( 
    xmlns="http://www.example.com", 
    embedIn={ 
          "http://www.osgi.org/ds/v1.2.3",
          "http://www.osgi.org/ds/v1.2.0"
    }, 
    where="component/fields/${name}")
@interface FieldInjection {
    boolean dynamic() default false;
    boolean eager() default false;
}

Design

The hard part is the placement of the attribute. I'd like to use XPath for this since it is incredibly powerful. Preferably, the update of the XML Document should be mostly specified by an XPath expression. The attributes on the annotated annotation are then used to insert the values. I have not worked out the details, however, it is clear that not all use cases would be able to be supported that way. I have hopes we could come up with a design for the 90+ % cases.

For the remaining use cases we need to escape to code. One trick I've used is using a constant in the annotation:

public interface XMLAttributeHandler<T extends Annotation> {
     void process( Node member, T ann );
}

@interface FieldInjection {
    FieldInjectionHandler HANDLER=null;
    boolean dynamic() default false;
    boolean eager() default false;
}

During the final processing of the DOM, the annotation reader would then use the type information about the HANDLER field in the annotation to create an instance for the document and call it with the members and found annotations.

The bad part of this solution is that this will be the first time (as far as I know) that we will instantiate classes from the target environment. This has some security implications as well as environment requirements.

Alternative

Alternatively, we could use a more traditional plugin mechanism.

interface XMLAttributeHandler  {
     boolean process( String namespace, Document doc, Member member, T ann );
}

The handler should return immediately if the namespace is not recognized.

Clone this wiki locally