You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@abdera.apache.org by James M Snell <ja...@gmail.com> on 2006/10/28 22:09:30 UTC

Extensions

Ok, so I've spent some time this morning implementing a way of
separating extensions from the underlying Axiom implementation.  The
mechanism is surprisingly simple.  I haven't checked this in yet but
will do so if no one cries Stop!

I've attached the patch for review.

To create an extension element, a developer would extend either
ElementWrapper or ExtensibleElementWrapper.

  public class FooElement extends ElementWrapper {
    protected FooElement(Element internal) {
      super(internal);
    }
    protected FooElement(Factory factory) {
      super(factory, TestExtensionFactory.FOO);
    }
    public String getFoo() {
      return getText();
    }
    public void setFoo(String foo) {
      setText(foo);
    }
  }

Then register an ExtensionFactory like before, but the methods have
changed a bit:

  public class TestExtensionFactory implements ExtensionFactory {

    public static final QName FOO = new QName("urn:foo", "foo");

    @SuppressWarnings("unchecked")
    public <T extends Element> T getElementWrapper(Element internal) {
      QName qname = internal.getQName();
      if (qname.equals(FOO)) return (T)new FooElement(internal);
      return (T) internal;
    }
    public List<String> getNamespaces() {
      return java.util.Arrays.asList(new String[] {"urn:foo"});
    }
    public boolean handlesNamespace(String namespace) {
      return namespace.equals("urn:foo");
    }
  }

We can then use FooElement seamlessly with the core API

    Abdera abdera = new Abdera();
    Factory factory = abdera.getFactory();
    factory.registerExtension(new TestExtensionFactory());
    Entry entry = factory.newEntry();
    FooElement foo = entry.addExtension(TestExtensionFactory.FOO);
    foo.setFoo("testing");

Or...

    Abdera abdera = new Abdera();
    Factory factory = abdera.getFactory();
    factory.registerExtension(new TestExtensionFactory());

    String s = "<foo xmlns='urn:foo'>testing</foo>";
    ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());

    Parser parser = abdera.getParser();
    ParserOptions options = parser.getDefaultParserOptions();
    options.setFactory(factory);

    Document<FooElement> doc = parser.parse(in, null, options);
    FooElement foo = doc.getRoot();
    System.out.println(foo.getFoo());

Thoughts?

- James

Re: Extensions

Posted by Garrett Rooney <ro...@electricjellyfish.net>.
On 10/29/06, James M Snell <ja...@gmail.com> wrote:
> Yeah, I was toying around with the idea of using a WeakHashMap inside
> ExtensionFactoryMap.  That should be enough to avoid the potential leak
> issue.  The other alternative would be to tie ExtensionFactoryMap to the
> Document rather than to the Factory, but even that runs into a number of
> potential issues.

Yeah, you could still retain references if you parsed a document and
then removed extension elements from it.  I think the WeakHashMap
approach is probably best.

-garrett

Re: Extensions

Posted by James M Snell <ja...@gmail.com>.
Yeah, I was toying around with the idea of using a WeakHashMap inside
ExtensionFactoryMap.  That should be enough to avoid the potential leak
issue.  The other alternative would be to tie ExtensionFactoryMap to the
Document rather than to the Factory, but even that runs into a number of
potential issues.

- James

Garrett Rooney wrote:
> [snip]
> One thing that makes me pause as I read over the actual diff is the
> ExtensionFactoryMap stuff.  It's storing the mapping between elements
> and their wrapper class, so when we have an underlying OMElement
> instance we can go back to the wrapper object and pass that back to
> the user.  Ok, that seems like a fine solution, but I wonder if we're
> going to be setting ourselves up for memory leaks by storing
> references to all the wrapper elements a factory creates in that map.
> Should this be using some sort of weak reference type thing here to
> avoid that?
> 
> In general though, I'm very +1 on this, and if the patch is done (and
> the extensions converted) I'd love to get it in before our proposed
> release.
> 
> -garrett
> 

Re: Extensions

Posted by Garrett Rooney <ro...@electricjellyfish.net>.
On 10/28/06, James M Snell <ja...@gmail.com> wrote:
> Ok, so I've spent some time this morning implementing a way of
> separating extensions from the underlying Axiom implementation.  The
> mechanism is surprisingly simple.  I haven't checked this in yet but
> will do so if no one cries Stop!
>
> I've attached the patch for review.
>
> To create an extension element, a developer would extend either
> ElementWrapper or ExtensibleElementWrapper.
>
>   public class FooElement extends ElementWrapper {
>     protected FooElement(Element internal) {
>       super(internal);
>     }
>     protected FooElement(Factory factory) {
>       super(factory, TestExtensionFactory.FOO);
>     }
>     public String getFoo() {
>       return getText();
>     }
>     public void setFoo(String foo) {
>       setText(foo);
>     }
>   }
>
> Then register an ExtensionFactory like before, but the methods have
> changed a bit:
>
>   public class TestExtensionFactory implements ExtensionFactory {
>
>     public static final QName FOO = new QName("urn:foo", "foo");
>
>     @SuppressWarnings("unchecked")
>     public <T extends Element> T getElementWrapper(Element internal) {
>       QName qname = internal.getQName();
>       if (qname.equals(FOO)) return (T)new FooElement(internal);
>       return (T) internal;
>     }
>     public List<String> getNamespaces() {
>       return java.util.Arrays.asList(new String[] {"urn:foo"});
>     }
>     public boolean handlesNamespace(String namespace) {
>       return namespace.equals("urn:foo");
>     }
>   }
>
> We can then use FooElement seamlessly with the core API
>
>     Abdera abdera = new Abdera();
>     Factory factory = abdera.getFactory();
>     factory.registerExtension(new TestExtensionFactory());
>     Entry entry = factory.newEntry();
>     FooElement foo = entry.addExtension(TestExtensionFactory.FOO);
>     foo.setFoo("testing");
>
> Or...
>
>     Abdera abdera = new Abdera();
>     Factory factory = abdera.getFactory();
>     factory.registerExtension(new TestExtensionFactory());
>
>     String s = "<foo xmlns='urn:foo'>testing</foo>";
>     ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
>
>     Parser parser = abdera.getParser();
>     ParserOptions options = parser.getDefaultParserOptions();
>     options.setFactory(factory);
>
>     Document<FooElement> doc = parser.parse(in, null, options);
>     FooElement foo = doc.getRoot();
>     System.out.println(foo.getFoo());
>
> Thoughts?

In concept I like this a lot.  It's very similar to what I did when I
was playing with decoupling the extension mechanism from the parser
around ApacheCon, with the key difference being that James knows
enough about the FOM code to make the idea actually work ;-)

One thing that makes me pause as I read over the actual diff is the
ExtensionFactoryMap stuff.  It's storing the mapping between elements
and their wrapper class, so when we have an underlying OMElement
instance we can go back to the wrapper object and pass that back to
the user.  Ok, that seems like a fine solution, but I wonder if we're
going to be setting ourselves up for memory leaks by storing
references to all the wrapper elements a factory creates in that map.
Should this be using some sort of weak reference type thing here to
avoid that?

In general though, I'm very +1 on this, and if the patch is done (and
the extensions converted) I'd love to get it in before our proposed
release.

-garrett