Using XML parser implementation as OSGi service
Asked Answered
I

2

7

I am developing an application using OSGi (Equinox platform), and one of the bundles needs to parse XML files. So far I implemented this with SAX (javax.xml.parsers.SAXParserFactory) and I would like to retrieve the SAXParserFactory from the platform.

I saw the OSGi standard provides for a XMLParserActivator to allow JAXP implementations to register themselves (http://www.osgi.org/javadoc/r4v41/org/osgi/util/xml/XMLParserActivator.html), so my guess is that there should be some bundles that offer the SAXParserFactory as a service.

However, I could not figure out which bundle to add as dependency in order to find a service that offers a SAXParserFactory. I try to retrieve a service reference using

context.getServiceReferences(SAXParserFactory.class.getName(), "(&(parser.namespaceAware=true)(parser.validating=true))")

Given that XML parsing is a rather common thing to do, I suppose there are implementations available, or other means for getting a XML parser service from the platform.

Any help would be very welcome!

Interpose answered 21/7, 2009 at 11:9 Comment(2)
Hi, I'm not confident with OSGi but why not to use the XML stack bundled with the JRE?Nape
Hi, yes it is, but the OSGi classloader mechanism differs - and hence calling the SAXParserFactory.newInstance() may yield problems, since the JAXP loader mechanism expects to find the parser in the current's thread classloader and that may not necessarily be the case.Interpose
M
3

Generally it is not a good idea to use JAXP in OSGi (because of the classloading mechanism primarily) and a much better idea to get the factory like a service.

If you are using equinox, the SaxParserFactory (using the JRE/JDK one you are running on) is actually provided by the System Bundle, which means you don't need extra bundles:

{javax.xml.parsers.SAXParserFactory}={service.id=6} Registered by bundle: System Bundle [0]

If you want to write code that deals with the lifecycle layer of the OSGi platform, I would suggest to track the reference, rather than looking it up directly. There are many approaches for this; I have written about one I call ServiceMediator here.

e.g. for your case (code is under Apache 2 License, Coalevo Project):

        import org.osgi.framework.*;

    import javax.xml.parsers.SAXParserFactory;

    import net.wimpi.telnetd.util.Latch;

    /**
     * Implements a mediator pattern class for services from the OSGi container.
     * <p/>
     *
     * @author Dieter Wimberger (wimpi)
     * @version @version@ (@date@)
     */
    class ServiceMediator {

      private BundleContext m_BundleContext;

      private SAXParserFactory m_SAXParserFactory;
      private Latch m_SAXParserFactoryLatch;

      public SAXParserFactory getSAXParserFactory(long wait) {
        try {
          if (wait < 0) {
            m_SAXParserFactoryLatch.acquire();
          } else if (wait > 0) {
            m_SAXParserFactoryLatch.attempt(wait);
          }
        } catch (InterruptedException e) {
          e.printStackTrace(System.err);
        }

        return m_SAXParserFactory;
      }//getSAXParserFactory

      public boolean activate(BundleContext bc) {
        //get the context
        m_BundleContext = bc;

        m_SAXParserFactoryLatch = createWaitLatch();

        //prepareDefinitions listener
        ServiceListener serviceListener = new ServiceListenerImpl();

        //prepareDefinitions the filter
        String filter = "(objectclass=" + SAXParserFactory.class.getName() + ")";

        try {
          //add the listener to the bundle context.
          bc.addServiceListener(serviceListener, filter);

          //ensure that already registered Service instances are registered with
          //the manager
          ServiceReference[] srl = bc.getServiceReferences(null, filter);
          for (int i = 0; srl != null && i < srl.length; i++) {
            serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, srl[i]));
          }
        } catch (InvalidSyntaxException ex) {
          ex.printStackTrace(System.err);
          return false;
        }
        return true;
      }//activate

      public void deactivate() {
        m_SAXParserFactory = null;

        m_SAXParserFactoryLatch = null;

        m_BundleContext = null;
      }//deactivate

      private Latch createWaitLatch() {
        return new Latch();
      }//createWaitLatch

      private class ServiceListenerImpl
          implements ServiceListener {

        public void serviceChanged(ServiceEvent ev) {
          ServiceReference sr = ev.getServiceReference();
          Object o = null;
          switch (ev.getType()) {
            case ServiceEvent.REGISTERED:
              o = m_BundleContext.getService(sr);
              if (o == null) {
                return;
              } else if (o instanceof SAXParserFactory) {
                m_SAXParserFactory = (SAXParserFactory) o;
                m_SAXParserFactory.setValidating(false);
                m_SAXParserFactory.setNamespaceAware(true);
                m_SAXParserFactoryLatch.release();
              } else {
                m_BundleContext.ungetService(sr);
              }
              break;
            case ServiceEvent.UNREGISTERING:
              o = m_BundleContext.getService(sr);
              if (o == null) {
                return;
              }  else if (o instanceof SAXParserFactory) {
                m_SAXParserFactory = null;
                m_SAXParserFactoryLatch = createWaitLatch();
              } else {
                m_BundleContext.ungetService(sr);
              }
              break;
          }
        }
      }//inner class ServiceListenerImpl

      public static long WAIT_UNLIMITED = -1;
      public static long NO_WAIT = 0;

    }//class ServiceMediator
Mashhad answered 27/1, 2010 at 21:9 Comment(2)
I would suggest also to look at "OSGI Dynamic Services" or "Spring DM" both have similar means for declaring/resolving dependencies of a bundle declaratively. And with these mechanisms the code above would simplify considerably.Claiborn
The code above is not really complicated and can easily be machine generated (the Latch can be take from different places if required for the Runtime Environment). I don't doubt that DS (I assume that you refer to Declarative Services) is an alternative, however, you may as well end up with some pretty complex configuration files sometimes. Anyway, it's always good to have alternatives to pick from :)Mashhad
B
-1

You can use Apache Xerces for Sax parsing. A suitable bundle is available from the Eclipse Orbit project. I don't know that the Xerces bundle registers a SAXParserFactory service but you can add a dependency on the bundle and use the Sax parser directly.

Bareback answered 9/1, 2010 at 4:27 Comment(1)
Avoid Bundle dependencies wherever possible.Castleman

© 2022 - 2024 — McMap. All rights reserved.