[Java] Niestandardowe procedury obsługi protokołów w środowisku OSGi

W klasycznych aplikacjach Javy własne procedury obsługi protokołów możemy wykorzystać na rożne sposoby. Niestety, większość z nich nie zadziała w kontenerze OSGi.

  • rejestracja własnej faktorii protokołów URLStreamHandlerFactory w metodzie URL.setURLStreamHandlerFactory. To rozwiązanie nie działa, ponieważ kontenery OSGi rejestrują własne faktorie protokołów i wymagają ich do poprawnej pracy. W przypadku Equinox’a jest to org.eclipse.osgi.framework.internal.protocol.StreamHandlerFactory rejestrowana w metodzie Framework.installURLStreamHandlerFactory(BundleContext, FrameworkAdaptor) przy starcie framework’a.
  • -Djava.protocol.handle.pkgs=<handlerPkgs>. Rejestrowana przez framework faktoria uwzględnia co prawda ten parametr w procesie wyszukiwania procedury obsługi protokołu, ale w większości przypadków występują problemy z widocznością klas. Jedynym sposobem jaki znam na obejście tych problemów do czasu kiedy kontenery OSGi zaczną wspierać pakunki częściowe dla pakunku systemowego jest zastosowania opcji -Xbootclasspath
  • dostarczenie klasy obsługującej protokół pod nazwą sun.net.www.protocol.<protocol>.Handler. Nie trzeba wówczas stosować opcji -Djava.protocol.handle.pkgs, ale ogólnie rozwiązanie cierpi na te same problemy co poprzednie.
  • przekazanie procedury obsługi protokołu w konstruktorze klasy URL. To akurat rozwiązanie działa w środowisku OSGi, ale jest mało praktyczne

Szczęśliwie twórcy specyfikacji OSGi przewidzieli te problemy i zaproponowali sposób ich rozwiązania. Szczegóły można znaleźć w specyfikacji OSGi Service Platform Core Specification w sekcji URL Handlers Service Specification ale zasadniczo sprowadza się ono do zarejestrowani usługi klasy org.osgi.service.url.URLStreamHandlerService z atrybutem url.handler.protocol ustawionym na nazwę protokołu. W dalszej części postaram się dokładniej opisać to rozwiązanie na podstawie fikcyjnego protokołu resource. Więcej informacji o procedurach obsługi protokołów można znaleźć tutaj.

Implementacja

Implementacja protokołu składa się z dwóch klas (z których dla zachowania zwięzłości tekstu usunięte zostały nieistotne szczegóły)

package jarekprzygodzki.protocols.resource.internal;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

/**
 * Reprezentuje procedurę obsługi protokołu
 */
public class ResourceURLStreamHandler extends URLStreamHandler {

   static final String RESOURCE_PROTOCOL="resource";

   @Override
   protected URLConnection openConnection(URL u) throws IOException {
      return new ResourceProtocolConnection(u);
   }

}
package jarekprzygodzki.protocols.resource.internal;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;

/**
 * Reprezentuje połączenia za pośrednictwem określonego protokołu.
 */
public class ResourceProtocolConnection extends URLConnection {

   public ResourceProtocolConnection(URL url) throws IOException {
      super(url);
   }

   @Override
   public void connect() throws IOException {

   }

}

Rejestracja w środowisku OSGi

Można ją przeprowadzić na kilka sposobów. Pierwszy to

Wykorzystanie aktywatora pakunku

Wymaga dodania wpisu

Bundle-Activator: jarekprzygodzki.protocols.resource.internal.ResourceProtocolHandlerActivator

w manifeście pakunku.

package jarekprzygodzki.protocols.resource.internal;

import java.util.Hashtable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.url.URLConstants;
import org.osgi.service.url.URLStreamHandlerService;

public class ResourceProtocolHandlerActivator implements BundleActivator {

   private ServiceRegistration serviceRegistration;

   @Override
   public void start(BundleContext context) throws Exception {
      Hashtable properties = new Hashtable<>();
      properties.put(URLConstants.URL_HANDLER_PROTOCOL,
            new String[] { ResourceURLStreamHandler.RESOURCE_PROTOCOL });
      serviceRegistration = context.registerService(
            URLStreamHandlerService.class.getName(),
            new ResourceStreamHandlerService(), properties);
   }

   @Override
   public void stop(BundleContext context) throws Exception {
      if (serviceRegistration != null) {
         serviceRegistration.unregister();
         serviceRegistration   = null;
      }
   }

}

Jeśli korzystamy z

Spring Dynamic Modules

możemy zastosować bardziej deklaratywne podejście wykorzystując np. anonimowy bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
   <osgi:service interface="org.osgi.service.url.URLStreamHandlerService">
      <osgi:service-properties>
         <entry key="url.handler.protocol" value="resource" />
      </osgi:service-properties>
      <bean
         class="jarekprzygodzki.protocols.resource.internal.ResourceStreamHandlerService">
      </bean>
   </osgi:service>
</beans>

Pełny kod opisanego przykładu postaram się zamieścić na swoim koncie SkyDrive

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s