You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@camel.apache.org by Andreas Gies <an...@wayofquality.de> on 2013/04/16 12:45:51 UTC

Using the Servlet component in a Blueprint container

Hello Community, 

I apologize for the long post upon list entry, but I thought this might be
interesting. 

I was working on a use case that involves running Camel inside an OSGi
Container and for configuration I am using Apache Aries 1.0.0.
As i have a HTTP Service already running and need to support HTTP posts, I
figured using the servlet component would make sense.

This having said, trying to use the configuration found here
(http://camel.apache.org/servlet.html) doesn't work out of the box.
The main reason seems to be that CamelServlet is a class rather than an
interface, so it can't be registered as an OSGi service
easily. Googling around indicated that using the attribute "ext:classes" on
the blueprint reference should do the trick, but I got
errors from Blueprint not being able to create the service proxy.

I stepped back and tried to understand what *should* happen and came up with
a solution / workaround that I thought was worth sharing.

1. I need to gain access to the CamelServlet by OSGI means.
2. I need to stick a HTTP Registry into that servlet.
3. I need to define an endpoint that wires the Registry and the Servlet
together. 

First, I have defined an Interface CamelServletProvider that has merely one
method:

public interface CamelServletProvider {

  public CamelServlet getCamelServlet();
}

with an associated implementation:

public class CamelServletProviderImpl implements CamelServletProvider {

  private CamelServlet camelServlet = null;

  public void setCamelServlet(CamelServlet camelServlet) {
    this.camelServlet = camelServlet;
  }

  @Override
  public CamelServlet getCamelServlet() {
    return camelServlet;
  }
}

The idea for this class is, that it lives alongside the CamelServlet and a
CamelServletProvider is registered as an OSGi Service like this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<blueprint
  xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
">

  <bean id="camelServlet"
class="org.apache.camel.component.servlet.CamelHttpTransportServlet" />

  <bean id="servletProvider"
class="de.woq.camel.sib.servlet.internal.CamelServletProviderImpl">
    <property name="camelServlet" ref="camelServlet" />
  </bean>

  <service ref="camelServlet" interface="javax.servlet.Servlet" >
    <service-properties>
      <entry key="alias"                value="/camel/services" />
      <entry key="matchOnUriPrefix"     value="true" />
      <entry key="servlet-name"         value="CamelServlet"/>
      <entry key="osgi.web.contextpath" value="/camel/services" />
    </service-properties>
  </service>

  <service ref="servletProvider"
interface="de.woq.camel.sib.servlet.CamelServletProvider">
    <service-properties>
      <entry key="servlet-name"         value="CamelServlet"/>
    </service-properties>
  </service>

</blueprint>

Now we have a bundle that exposes a Camel Servlet as HTTP transport which I
can use in endpoints to come in another bundle.


To make this work I need a HTTP Registry, that I can stick into Blueprint
and that reacts to CamelServletProviders coming and going.
The DefaultHTTPRegistry in Camel reacts to javax.servlet.Servlet.

I have simply cloned the DefaultHTTPRegistry and made it react to
CamelServletProviders:

public class  CamelServletHTTPRegistry implements HttpRegistry {
  private static final transient Logger LOG = LoggerFactory.getLogger(
CamelServletHTTPRegistry.class);

  private static HttpRegistry singleton;

  private final Set<HttpConsumer> consumers;
  private final Set<CamelServlet> providers;

  public  CamelServletHTTPRegistry() {
    consumers = new HashSet<HttpConsumer>();
    providers = new HashSet<CamelServlet>();
  }

  /**
   * Lookup or create a HttpRegistry
   */
  public static synchronized HttpRegistry getSingletonHttpRegistry() {
    if (singleton == null) {
      singleton = new  CamelServletHTTPRegistry();
    }
    return singleton;
  }

  @Override
  public void register(HttpConsumer consumer) {
    LOG.debug("Registering consumer for path {} providers present: {}",
     consumer.getPath(), providers.size());
    consumers.add(consumer);
    for (CamelServlet provider : providers) {
      provider.connect(consumer);
    }
  }

  @Override
  public void unregister(HttpConsumer consumer) {
    LOG.debug("Unregistering consumer for path {} ", consumer.getPath());
    consumers.remove(consumer);
    for (CamelServlet provider : providers) {
      provider.disconnect(consumer);
    }
  }

  @SuppressWarnings("rawtypes")
  public void register(CamelServletProvider provider, Map properties) {
    LOG.info("Registering provider through OSGi service listener {}",
properties);
    try {
      CamelServlet camelServlet = provider.getCamelServlet();
      camelServlet.setServletName((String) properties.get("servlet-name"));
      register(camelServlet);
    } catch (ClassCastException cce) {
      LOG.info("Provider is not a Camel Servlet");
    }
  }

  public void unregister(CamelServletProvider provider, Map<String, Object>
properties) {
    LOG.info("Deregistering provider through OSGi service listener {}",
properties);
    try {
      CamelServlet camelServlet = provider.getCamelServlet();
      unregister((CamelServlet)provider);
    } catch (ClassCastException cce) {
      LOG.info("Provider is not a Camel Servlet");
    }
  }

  @Override
  public void register(CamelServlet provider) {
    LOG.debug("Registering CamelServlet with name {} consumers present: {}",
     provider.getServletName(), consumers.size());
    providers.add(provider);
    for (HttpConsumer consumer : consumers) {
      provider.connect(consumer);
    }
  }

  @Override
  public void unregister(CamelServlet provider) {
    providers.remove(provider);
  }

  public void setServlets(List<Servlet> servlets) {
    providers.clear();
    for (Servlet servlet : servlets) {
      if (servlet instanceof CamelServlet) {
        providers.add((CamelServlet) servlet);
      }
    }
  }
}

The trick here is in the register / unregister method reacting to the
CamelServletProvider interface.

Now I was able to stick all of that together in a bundle configuration:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<blueprint
  xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:camel="http://camel.apache.org/schema/blueprint"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
  http://camel.apache.org/schema/blueprint
http://camel.apache.org/schema/blueprint/camel-blueprint-2.10.3.xsd
">

  <!-- Get an instance of the registry -->
  <bean id="httpRegistry"
class="de.woq.camel.sib.http.CamelServletHTTPRegistry" />

  <!-- Get hold of the Servlet Provider by its interface and servlet name
-->
  <reference
    interface="de.woq.camel.sib.servlet.CamelServletProvider"
    filter="(servlet-name=CamelServlet)"
    timeout="1000">
    <reference-listener ref="httpRegistry" bind-method="register"
unbind-method="unregister" />
  </reference>

  <!-- Create the servlet component -->
  <bean id="servlet"
class="org.apache.camel.component.servlet.ServletComponent">
    <property name="httpRegistry" ref="httpRegistry" />
  </bean>

  <camel:camelContext>

    <!-- Just convenience to stick file into a directory and have them
posted to a URL. -->
    <!-- /camel/services was the root context of my transport service above
-->
    <camel:route>
      <camel:from uri="file:///tmp/woq-in" />
      <camel:to uri="http://localhost:8080/camel/services/test" />
    </camel:route>

    <!-- The actual route -->
    <camel:route>
      <!-- Three /// are important, otherwise the endpoint will be
registered with path "/" -->
      <camel:from uri="servlet:///test" />
      <camel:to uri="file:///tmp/woq-out" />
    </camel:route>

  </camel:camelContext>

</blueprint>


If you think this is usefull, I am happy to stick it in the wiki rather than
in the user's list.


Best regards
Andreas
Andreas Gies

WoQ ­ Way of Quality UG



Geschäftsführer & CTO

eMail: andreas@wayofquality.de

Tel DE: +49 151 23470823

Tel UK: +44 755 1381918

Fax: +49 1805 006534 2114


Amtsgericht Landshut: HRB 8352
Ust.-Id.: DE274771254
Haftungsausschluss

Diese Email kann vertrauliche und/oder rechtlich geschützte Informationen
enthalten und ist ausschließlich für den/die benannten Adressaten bestimmt.
Sollten Sie nicht der beabsichtigte Empfänger sein oder diese Email
irrtümlich erhalten haben, ist es Ihnen nicht gestattet diese Mail oder
einen Teil davon ohne unsere Erlaubnis zu verbreiten, zu kopieren, unbefugt
weiterzuleiten oder zu behalten. Informieren Sie bitte sofort den Absender
telefonisch oder per Email und löschen Sie diese Email und alle Kopien aus
Ihrem System. Wir haften nicht für die Unversehrtheit von Emails, nachdem
sie unseren Einflussbereich verlassen haben.

Disclaimer

This email may contain confidential and/or privileged information and is
intended solely for the attention and use of the named addressee(s). If you
are not the intended recipient, or a person responsible for delivering it to
the intended recipient, you are not authorized to and must not disclose,
copy, distribute, or retain this message or any part of it without our
authority. Please contact the sender by call or reply email immediately and
destroy all copies and the original message. We are not responsible for the
integrity of emails after they have left our sphere of control.