Apache Camel + JBoss 5 + Spring

This is a followup to my previous post about how to get Apache Camel 1.6.0 to work using JBoss 5.  In this post, I provide some code to use configure Camel using Spring.  As with vanilla Camel, you cannot do this with JBoss 5 due to the fact that Camel’s ResolverUtil does not understand JBoss’s “virtual file system”.

Please note Claus Ibsen’s comment in my original post that he plans to allow the ResolverUtil to be configurable in Apache Camel 2.0.  In the meantime, you can refer to code I have provided at GitHub, which is based on camel-osgi.

As before, a pom.xml file is provided to build the jar.  Place the jar file in your JBoss 5 installation’s server/[config]/lib directory.

In your spring xml, be sure to use the new namespace that I have defined for Camel + JBoss 5 when you define your camelContext.  For example:

<?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:jboss="http://example.org/camel/schema/jboss"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://example.org/camel/schema/jboss http://example.org/camel/schema/jboss/camel-jboss.xsd
       http://activemq.apache.org/camel/schema/spring http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">

  <jboss:camelContext xmlns="http://activemq.apache.org/camel/schema/spring">
    <route>
      <from uri="imaps://imap.gmail.com?username=[yourusername]&amp;password=[yourpassword]&amp;
          deleteProcessedMessages=false&amp;consumer.delay=120000"/>
      <to uri="log:example.camel?level=DEBUG"/>
    </route>
  </jboss:camelContext>
</beans>

7 comments so far

  1. alesj on

    What’s the part that’s not understood via our VFS?

    As I have some JBoss MC + Camel patch lying around,
    where I need to find the time to push it in,
    hence I can check that as well.

    • roberth on

      alesj: It’s easiest if you look at the Apache Camel’s org.apache.camel.util.ResolverUtil class, in particular the find method. When attempting to resolve annotations in the classes of a given package, it grabs the URL for the package using the ClassLoader’s getResources() method, and then can either (1) iterate through the directory using java.io.File, or (2) iterate through a jarfile using java.util.jar.JarEntry. However, it is not aware of how to handle the case where the URL uses the “vfszip” protocol.

  2. Claus Ibsen on

    In Camel 2.0 we have added SPI interfaces for providing your own resolver.

    This allows eg JBoss to provide an implementation and easily configure Camel to use it.

    See:
    http://camel.apache.org/pluggable-class-resolvers.html

    Basicaully the old ResolverUtil is @deprecated and we provide the SPI interface
    org.apache.camel.spi.PackageScanClassResolver

    which you can implement to cater for JBoss.

    You can also extend the Default impl in
    org.apache.camel.impl.DefaultPackageScanClassResolver which is basically the old ResolverUtil.

    For simple single class loading we also provide a SPI interface: ClassResolver you can provide your own impl. and configure Camel to use. This resolver will be given a FQN stringname of a class to load, in case JBoss eg. needs to do something special instead of relying on JDK class loaders.

  3. alesj on

    I’ve hacked this (will be part of the patch I provide):

    public class VFSPackageScanClassResolver extends DefaultPackageScanClassResolver
    {
    @Override
    protected void find(PackageScanFilter test, String packageName, ClassLoader loader, Set classes)
    {
    if (LOG.isTraceEnabled())
    {
    LOG.trace(“Searching for: ” + test + ” in package: ” + packageName + ” using classloader: ”
    + loader.getClass().getName());
    }

    Enumeration urls;
    try
    {
    urls = getResources(loader, packageName);
    if (!urls.hasMoreElements())
    {
    LOG.trace(“No URLs returned by classloader”);
    }
    }
    catch (IOException ioe)
    {
    LOG.warn(“Could not read package: ” + packageName, ioe);
    return;
    }

    while (urls.hasMoreElements())
    {
    URL url = null;
    try
    {
    url = urls.nextElement();
    if (LOG.isTraceEnabled())
    {
    LOG.trace(“URL from classloader: ” + url);
    }
    VirtualFile root = VFS.getRoot(url);
    root.visit(new MatchingClassVisitor(test, classes));
    }
    catch (IOException ioe)
    {
    LOG.warn(“Could not read entries in url: ” + url, ioe);
    }
    }
    }

    private class MatchingClassVisitor extends AbstractVirtualFileVisitor
    {
    private PackageScanFilter filter;
    private Set classes;

    private MatchingClassVisitor(PackageScanFilter filter, Set classes)
    {
    super(VisitorAttributes.RECURSE_LEAVES_ONLY);
    this.filter = filter;
    this.classes = classes;
    }

    public void visit(VirtualFile file)
    {
    if (file.getName().endsWith(“.class”))
    {
    String fqn = file.getPathName();
    addIfMatching(filter, fqn, classes);
    }
    }
    }
    }

  4. roberth on

    Thanks to both Claus and alesj for resolving the issue for Camel 2.0. I am impressed by the speed of their responses.

  5. alesj on

    So this means my impl works?
    As I unfortunately had no time to test/check it. 🙂

    • roberth on

      I hacked your hack to make it work 🙂

      Of course, this only applies to Camel 2.0.

      See below, and also Claus’ link regarding how to use it in the spring configuration.


      package codeandtell.camel.jboss;

      import java.net.URL;
      import java.io.IOException;
      import java.util.Enumeration;
      import java.util.Set;
      import org.apache.camel.impl.DefaultPackageScanClassResolver;
      import org.apache.camel.spi.PackageScanFilter;
      import org.jboss.virtual.plugins.vfs.helpers.AbstractVirtualFileVisitor;
      import org.jboss.virtual.VisitorAttributes;
      import org.jboss.virtual.VFS;
      import org.jboss.virtual.VirtualFile;

      public class VFSPackageScanClassResolver extends DefaultPackageScanClassResolver
      {
      @Override
      protected void find(PackageScanFilter test, String packageName, ClassLoader loader, Set classes)
      {
      if (LOG.isTraceEnabled())
      {
      LOG.trace("Searching for: " + test + " in package: " + packageName + " using classloader: "
      + loader.getClass().getName());
      }

      Enumeration urls;
      try
      {
      urls = getResources(loader, packageName);
      if (!urls.hasMoreElements())
      {
      LOG.trace("No URLs returned by classloader");
      }
      }
      catch (IOException ioe)
      {
      LOG.warn("Could not read package: " + packageName, ioe);
      return;
      }

      while (urls.hasMoreElements())
      {
      URL url = null;
      try
      {
      url = (URL) urls.nextElement();
      if (LOG.isTraceEnabled())
      {
      LOG.trace("URL from classloader: " + url);
      }
      VirtualFile root = VFS.getRoot(url);
      root.visit(new MatchingClassVisitor(test, classes));
      }
      catch (IOException ioe)
      {
      LOG.warn("Could not read entries in url: " + url, ioe);
      }
      }
      }

      private class MatchingClassVisitor extends AbstractVirtualFileVisitor
      {
      private PackageScanFilter filter;
      private Set classes;

      private MatchingClassVisitor(PackageScanFilter filter, Set classes)
      {
      super(VisitorAttributes.RECURSE_LEAVES_ONLY);
      this.filter = filter;
      this.classes = classes;
      }

      public void visit(VirtualFile file)
      {
      if (file.getName().endsWith(".class"))
      {
      String fqn = file.getPathName();
      addIfMatching(filter, fqn.substring(fqn.indexOf("/") + 1), classes);
      }
      }
      }
      }


Comments are closed.

%d bloggers like this: