Author: jstrachan
Date: Fri Oct 20 03:12:26 2006
New Revision: 466049

URL: http://svn.apache.org/viewvc?view=rev&rev=466049
Log:
added the ability to auto-deploy POJOs as endpoints if they have the @Endpoint 
annotation, provided that the <bean:component searchPackages="..."/> attribute 
is specified to describe which packages are searched for on the class loader

Added:
    
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
   (with props)
    
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
   (with props)
    
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
   (with props)
    
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
   (with props)
    
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
   (with props)
    
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
   (with props)
Modified:
    
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanComponent.java
    
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanEndpoint.java
    
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/ConsumerListener.java

Modified: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanComponent.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanComponent.java?view=diff&rev=466049&r1=466048&r2=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanComponent.java
 (original)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanComponent.java
 Fri Oct 20 03:12:26 2006
@@ -24,6 +24,8 @@
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationContext;
 import org.w3c.dom.DocumentFragment;
 
 import javax.jbi.servicedesc.ServiceEndpoint;
@@ -31,24 +33,25 @@
 import java.net.URI;
 import java.util.List;
 import java.util.Map;
+import java.util.ArrayList;
 
 /**
  * A JBI component for binding beans to the JBI bus which work directly off of 
the JBI messages
  * without requiring any SOAP Processing. If you require support for SOAP, 
JAX-WS, JSR-181 then you
  * should use the servicemix-jsr181 module instead.
  *
+ * @version $Revision: $
  * @org.apache.xbean.XBean element="component" description="Bean Component"
-  *
-  * @version $Revision: $
  */
-public class BeanComponent extends DefaultComponent implements 
BeanFactoryAware {
+public class BeanComponent extends DefaultComponent implements 
ApplicationContextAware {
 
     public final static String EPR_URI = "urn:servicemix:bean";
     public final static QName EPR_SERVICE = new QName(EPR_URI, 
"BeanComponent");
     public final static String EPR_NAME = "epr";
 
     private BeanEndpoint[] endpoints;
-    private BeanFactory beanFactory;
+    private String[] searchPackages;
+    private ApplicationContext applicationContext;
 
 
     public BeanEndpoint[] getEndpoints() {
@@ -59,12 +62,20 @@
         this.endpoints = endpoints;
     }
 
-    public BeanFactory getBeanFactory() {
-        return beanFactory;
+    public ApplicationContext getApplicationContext() {
+        return applicationContext;
     }
 
-    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
-        this.beanFactory = beanFactory;
+    public void setApplicationContext(ApplicationContext applicationContext) 
throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    public String[] getSearchPackages() {
+        return searchPackages;
+    }
+
+    public void setSearchPackages(String[] searchPackages) {
+        this.searchPackages = searchPackages;
     }
 
     public ServiceEndpoint resolveEndpointReference(DocumentFragment epr) {
@@ -72,7 +83,12 @@
     }
 
     protected List getConfiguredEndpoints() {
-        return asList(getEndpoints());
+        List list = new ArrayList(asList(getEndpoints()));
+        if (searchPackages != null) {
+            EndpointFinder finder = new EndpointFinder(this);
+            finder.addEndpoints(list);
+        }
+        return list;
     }
 
     protected Class[] getEndpointClasses() {
@@ -121,4 +137,10 @@
         return endpoint;
     }
 
+    /**
+     * Adds a new component dynamically
+     */
+    public void addEndpoint(BeanEndpoint endpoint) throws Exception {
+        super.addEndpoint(endpoint);
+    }
 }

Modified: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanEndpoint.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanEndpoint.java?view=diff&rev=466049&r1=466048&r2=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanEndpoint.java
 (original)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/BeanEndpoint.java
 Fri Oct 20 03:12:26 2006
@@ -57,6 +57,8 @@
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationContext;
 
 /**
  * Represents a bean endpoint which consists of a together with a [EMAIL 
PROTECTED] MethodInvocationStrategy}
@@ -65,9 +67,9 @@
  * @version $Revision: $
  * @org.apache.xbean.XBean element="endpoint"
  */
-public class BeanEndpoint extends ProviderEndpoint implements BeanFactoryAware 
{
+public class BeanEndpoint extends ProviderEndpoint implements 
ApplicationContextAware {
 
-    private BeanFactory beanFactory;
+    private ApplicationContext applicationContext;
     private String beanName;
     private Object bean;
     private BeanInfo beanInfo;
@@ -87,7 +89,7 @@
 
     public BeanEndpoint(BeanComponent component, ServiceEndpoint 
serviceEndpoint) {
         super(component, serviceEndpoint);
-        setBeanFactory(component.getBeanFactory());
+        setApplicationContext(component.getApplicationContext());
     }
 
     public void start() throws Exception {
@@ -114,12 +116,13 @@
         }
     }
 
-    public BeanFactory getBeanFactory() {
-        return beanFactory;
+
+    public ApplicationContext getApplicationContext() {
+        return applicationContext;
     }
 
-    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
-        this.beanFactory = beanFactory;
+    public void setApplicationContext(ApplicationContext applicationContext) 
throws BeansException {
+        this.applicationContext = applicationContext;
     }
 
     public String getBeanName() {
@@ -294,10 +297,10 @@
             return beanType.newInstance();
         } else if (beanName == null) {
             throw new IllegalArgumentException("Property 'beanName', 
'beanType' or 'beanClassName' must be set!");
-        } else if (beanFactory == null) {
+        } else if (applicationContext == null) {
             throw new IllegalArgumentException("Property 'beanName' specified, 
but no BeanFactory set!");
         } else {
-            Object answer = beanFactory.getBean(beanName);
+            Object answer = applicationContext.getBean(beanName);
             if (answer == null) {
                 throw new NoSuchBeanException(beanName, this);
             }
@@ -423,5 +426,4 @@
     public void 
setCorrelationExpression(org.apache.servicemix.expression.Expression 
correlationExpression) {
         this.correlationExpression = correlationExpression;
     }
-
 }

Added: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java?view=auto&rev=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
 (added)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
 Fri Oct 20 03:12:26 2006
@@ -0,0 +1,25 @@
+package org.apache.servicemix.bean;
+
+import javax.xml.namespace.QName;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * @version $Revision$
+ */
[EMAIL PROTECTED](RUNTIME)
[EMAIL PROTECTED]({ElementType.TYPE})
+public @interface Endpoint {
+
+    String name() default "";
+    
+    String uri() default "";
+
+    String serviceName() default "";
+    
+    String targetNamespace() default "";
+
+    boolean enabled() default true;
+}

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/Endpoint.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java?view=auto&rev=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
 (added)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
 Fri Oct 20 03:12:26 2006
@@ -0,0 +1,138 @@
+/**
+ *
+ * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License. 
+ *
+ **/
+package org.apache.servicemix.bean;
+
+import org.apache.servicemix.bean.support.ResolverUtil;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import javax.xml.namespace.QName;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Used to find POJOs on the classpath to be auto-exposed as endpoints
+ *
+ * @version $Revision$
+ */
+public class EndpointFinder implements ApplicationContextAware {
+    private ApplicationContext applicationContext;
+    private ResolverUtil resolver = new ResolverUtil();
+    private String[] packages = {};
+    private BeanComponent beanComponent;
+
+    public EndpointFinder(BeanComponent beanComponent) {
+        this.beanComponent = beanComponent;
+        this.packages = beanComponent.getSearchPackages();
+        this.applicationContext = beanComponent.getApplicationContext();
+    }
+
+    public String[] getPackages() {
+        return packages;
+    }
+
+    public void setPackages(String[] packages) {
+        this.packages = packages;
+    }
+
+    public ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    public void setApplicationContext(ApplicationContext applicationContext) 
throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+
+    public void addEndpoints(List list) {
+        resolver.findAnnotated(Endpoint.class, packages);
+        Set<Class> classes = resolver.getClasses();
+        for (Class aClass : classes) {
+            if (shouldIgnoreBean(aClass)) {
+                continue;
+            }
+            if (isClient(aClass)) {
+                registerClient(aClass);
+            }
+            else if (!Modifier.isAbstract(aClass.getModifiers())) {
+                list.add(createBeanEndpoint(aClass));
+            }
+        }
+    }
+
+    public void destroy() throws Exception {
+    }
+
+    /**
+     * Should the bean be ignored?
+     */
+    protected boolean shouldIgnoreBean(Class type) {
+        Map beans = applicationContext.getBeansOfType(type, true, true);
+        if (beans == null || beans.isEmpty()) {
+            return false;
+        }
+        // TODO apply some filter?
+        return true;
+    }
+
+    /**
+     * Returns true if the interface is a client side interface
+     */
+    protected boolean isClient(Class type) {
+        return type.isInterface() || Modifier.isAbstract(type.getModifiers());
+    }
+
+    protected void registerClient(Class type) {
+        /** TODO */
+    }
+
+
+    protected BeanEndpoint createBeanEndpoint(Class serviceType) {
+        Endpoint endpointAnnotation = (Endpoint) 
serviceType.getAnnotation(Endpoint.class);
+        if (endpointAnnotation == null) {
+            throw new IllegalArgumentException("Could not find endpoint 
annotation on type: " + serviceType);
+        }
+        BeanEndpoint answer = new BeanEndpoint();
+        answer.setBeanType(serviceType);
+        answer.setEndpoint(endpointAnnotation.name());
+        QName service = createQName(endpointAnnotation.serviceName(), 
endpointAnnotation.targetNamespace());
+        if (service == null) {
+            service = BeanComponent.EPR_SERVICE;
+        }
+        answer.setService(service);
+        return answer;
+    }
+
+    protected QName createQName(String localName, String uri) {
+        if (isNotNullOrBlank(localName)) {
+            if (isNotNullOrBlank(uri)) {
+                return new QName(uri, localName);
+            }
+            return new QName(localName);
+        }
+        return null;
+    }
+
+
+    protected boolean isNotNullOrBlank(String text) {
+        return text != null && text.trim().length() > 0;
+    }
+}

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/EndpointFinder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java?view=auto&rev=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
 (added)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
 Fri Oct 20 03:12:26 2006
@@ -0,0 +1,325 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.bean.support;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+/**
+ * <p>ResolverUtil is used to locate classes that are available in the/a class 
path and meet
+ * arbitrary conditions. The two most common conditions are that a class 
implements/extends
+ * another class, or that is it annotated with a specific annotation. However, 
through the use
+ * of the [EMAIL PROTECTED] Test} class it is possible to search using 
arbitrary conditions.</p>
+ *
+ * <p>A ClassLoader is used to locate all locations (directories and jar 
files) in the class
+ * path that contain classes within certain packages, and then to load those 
classes and
+ * check them. By default the ClassLoader returned by
+ *  [EMAIL PROTECTED] Thread.currentThread().getContextClassLoader()} is used, 
but this can be overridden
+ * by calling [EMAIL PROTECTED] #setClassLoader(ClassLoader)} prior to 
invoking any of the [EMAIL PROTECTED] find()}
+ * methods.</p>
+ *
+ * <p>General searches are initiated by calling the
+ * [EMAIL PROTECTED] #find(ResolverUtil.Test, String)} ()} method and supplying
+ * a package name and a Test instance. This will cause the named package 
<b>and all sub-packages</b>
+ * to be scanned for classes that meet the test. There are also utility 
methods for the common
+ * use cases of scanning multiple packages for extensions of particular 
classes, or classes
+ * annotated with a specific annotation.</p>
+ *
+ * <p>The standard usage pattern for the ResolverUtil class is as follows:</p>
+ *
+ *<pre>
+ *ResolverUtil&lt;ActionBean&gt; resolver = new 
ResolverUtil&lt;ActionBean&gt;();
+ *resolver.findImplementation(ActionBean.class, pkg1, pkg2);
+ *resolver.find(new CustomTest(), pkg1);
+ *resolver.find(new CustomTest(), pkg2);
+ *Collection&lt;ActionBean&gt; beans = resolver.getClasses();
+ *</pre> 
+ *
+ * @author Tim Fennell
+ */
+public class ResolverUtil<T> {
+    private static final transient Log log = 
LogFactory.getLog(ResolverUtil.class);
+
+    /**
+     * A simple interface that specifies how to test classes to determine if 
they
+     * are to be included in the results produced by the ResolverUtil.
+     */
+    public static interface Test {
+        /**
+         * Will be called repeatedly with candidate classes. Must return True 
if a class
+         * is to be included in the results, false otherwise.
+         */
+        boolean matches(Class type);
+    }
+
+    /**
+     * A Test that checks to see if each class is assignable to the provided 
class. Note
+     * that this test will match the parent type itself if it is presented for 
matching.
+     */
+    public static class IsA implements Test {
+        private Class parent;
+
+        /** Constructs an IsA test using the supplied Class as the parent 
class/interface. */
+        public IsA(Class parentType) { this.parent = parentType; }
+
+        /** Returns true if type is assignable to the parent type supplied in 
the constructor. */
+        public boolean matches(Class type) {
+            return type != null && parent.isAssignableFrom(type);
+        }
+
+        @Override public String toString() {
+            return "is assignable to " + parent.getSimpleName();
+        }
+    }
+
+    /**
+     * A Test that checks to see if each class is annotated with a specific 
annotation. If it
+     * is, then the test returns true, otherwise false.
+     */
+    public static class AnnotatedWith implements Test {
+        private Class<? extends Annotation> annotation;
+
+        /** Construts an AnnotatedWith test for the specified annotation type. 
*/
+        public AnnotatedWith(Class<? extends Annotation> annotation) { 
this.annotation = annotation; }
+
+        /** Returns true if the type is annotated with the class provided to 
the constructor. */
+        public boolean matches(Class type) {
+            return type != null && type.isAnnotationPresent(annotation);
+        }
+
+        @Override public String toString() {
+            return "annotated with @" + annotation.getSimpleName();
+        }
+    }
+
+    /** The set of matches being accumulated. */
+    private Set<Class<? extends T>> matches = new HashSet<Class<?extends T>>();
+
+    /**
+     * The ClassLoader to use when looking for classes. If null then the 
ClassLoader returned
+     * by Thread.currentThread().getContextClassLoader() will be used.
+     */
+    private ClassLoader classloader;
+
+    /**
+     * Provides access to the classes discovered so far. If no calls have been 
made to
+     * any of the [EMAIL PROTECTED] find()} methods, this set will be empty.
+     *
+     * @return the set of classes that have been discovered.
+     */
+    public Set<Class<? extends T>> getClasses() {
+        return matches;
+    }
+
+    /**
+     * Returns the classloader that will be used for scanning for classes. If 
no explicit
+     * ClassLoader has been set by the calling, the context class loader will 
be used.
+     *
+     * @return the ClassLoader that will be used to scan for classes
+     */
+    public ClassLoader getClassLoader() {
+        return classloader == null ? 
Thread.currentThread().getContextClassLoader() : classloader;
+    }
+
+    /**
+     * Sets an explicit ClassLoader that should be used when scanning for 
classes. If none
+     * is set then the context classloader will be used.
+     *
+     * @param classloader a ClassLoader to use when scanning for classes
+     */
+    public void setClassLoader(ClassLoader classloader) { this.classloader = 
classloader; }
+
+    /**
+     * Attempts to discover classes that are assignable to the type provided. 
In the case
+     * that an interface is provided this method will collect implementations. 
In the case
+     * of a non-interface class, subclasses will be collected.  Accumulated 
classes can be
+     * accessed by calling [EMAIL PROTECTED] #getClasses()}.
+     *
+     * @param parent the class of interface to find subclasses or 
implementations of
+     * @param packageNames one or more package names to scan (including 
subpackages) for classes
+     */
+    public void findImplementations(Class parent, String... packageNames) {
+        if (packageNames == null) return;
+
+        Test test = new IsA(parent);
+        for (String pkg : packageNames) {
+            find(test, pkg);
+        }
+    }
+
+    /**
+     * Attempts to discover classes that are annotated with to the annotation. 
Accumulated
+     * classes can be accessed by calling [EMAIL PROTECTED] #getClasses()}.
+     *
+     * @param annotation the annotation that should be present on matching 
classes
+     * @param packageNames one or more package names to scan (including 
subpackages) for classes
+     */
+    public void findAnnotated(Class<? extends Annotation> annotation, 
String... packageNames) {
+        if (packageNames == null) return;
+
+        Test test = new AnnotatedWith(annotation);
+        for (String pkg : packageNames) {
+            find(test, pkg);
+        }
+    }
+
+    /**
+     * Scans for classes starting at the package provided and descending into 
subpackages.
+     * Each class is offered up to the Test as it is discovered, and if the 
Test returns
+     * true the class is retained.  Accumulated classes can be fetched by 
calling
+     * [EMAIL PROTECTED] #getClasses()}.
+     *
+     * @param test an instance of [EMAIL PROTECTED] Test} that will be used to 
filter classes
+     * @param packageName the name of the package from which to start scanning 
for
+     *        classes, e.g. [EMAIL PROTECTED] net.sourceforge.stripes}
+     */
+    public void find(Test test, String packageName) {
+        packageName = packageName.replace('.', '/');
+        ClassLoader loader = getClassLoader();
+        Enumeration<URL> urls;
+
+        try {
+            urls = loader.getResources(packageName);
+        }
+        catch (IOException ioe) {
+            log.warn("Could not read package: " + packageName, ioe);
+            return;
+        }
+
+        while (urls.hasMoreElements()) {
+            try {
+                String urlPath = urls.nextElement().getFile();
+                urlPath = URLDecoder.decode(urlPath, "UTF-8");
+
+                // If it's a file in a directory, trim the stupid file: spec
+                if ( urlPath.startsWith("file:") ) {
+                    urlPath = urlPath.substring(5);
+                }
+
+                // Else it's in a JAR, grab the path to the jar
+                if (urlPath.indexOf('!') > 0) {
+                    urlPath = urlPath.substring(0, urlPath.indexOf('!'));
+                }
+
+                log.info("Scanning for classes in [" + urlPath + "] matching 
criteria: " + test);
+                File file = new File(urlPath);
+                if ( file.isDirectory() ) {
+                    loadImplementationsInDirectory(test, packageName, file);
+                }
+                else {
+                    loadImplementationsInJar(test, packageName, file);
+                }
+            }
+            catch (IOException ioe) {
+                log.warn("could not read entries", ioe);
+            }
+        }
+    }
+
+
+    /**
+     * Finds matches in a physical directory on a filesystem.  Examines all
+     * files within a directory - if the File object is not a directory, and 
ends with <i>.class</i>
+     * the file is loaded and tested to see if it is acceptable according to 
the Test.  Operates
+     * recursively to find classes within a folder structure matching the 
package structure.
+     *
+     * @param test a Test used to filter the classes that are discovered
+     * @param parent the package name up to this directory in the package 
hierarchy.  E.g. if
+     *        /classes is in the classpath and we wish to examine files in 
/classes/org/apache then
+     *        the values of <i>parent</i> would be <i>org/apache</i>
+     * @param location a File object representing a directory
+     */
+    private void loadImplementationsInDirectory(Test test, String parent, File 
location) {
+        File[] files = location.listFiles();
+        StringBuilder builder = null;
+
+        for (File file : files) {
+            builder = new StringBuilder(100);
+            builder.append(parent).append("/").append(file.getName());
+            String packageOrClass = ( parent == null ? file.getName() : 
builder.toString() );
+
+            if (file.isDirectory()) {
+                loadImplementationsInDirectory(test, packageOrClass, file);
+            }
+            else if (file.getName().endsWith(".class")) {
+                addIfMatching(test, packageOrClass);
+            }
+        }
+    }
+
+    /**
+     * Finds matching classes within a jar files that contains a folder 
structure
+     * matching the package structure.  If the File is not a JarFile or does 
not exist a warning
+     * will be logged, but no error will be raised.
+     *
+     * @param test a Test used to filter the classes that are discovered
+     * @param parent the parent package under which classes must be in order 
to be considered
+     * @param jarfile the jar file to be examined for classes
+     */
+    private void loadImplementationsInJar(Test test, String parent, File 
jarfile) {
+
+        try {
+            JarEntry entry;
+            JarInputStream jarStream = new JarInputStream(new 
FileInputStream(jarfile));
+
+            while ( (entry = jarStream.getNextJarEntry() ) != null) {
+                String name = entry.getName();
+                if (!entry.isDirectory() && name.startsWith(parent) && 
name.endsWith(".class")) {
+                    addIfMatching(test, name);
+                }
+            }
+        }
+        catch (IOException ioe) {
+            log.error("Could not search jar file '" + jarfile + "' for classes 
matching criteria: " +
+                      test + "due to an IOException: " + ioe.getMessage());
+        }
+    }
+
+    /**
+     * Add the class designated by the fully qualified class name provided to 
the set of
+     * resolved classes if and only if it is approved by the Test supplied.
+     *
+     * @param test the test used to determine if the class matches
+     * @param fqn the fully qualified name of a class
+     */
+    protected void addIfMatching(Test test, String fqn) {
+        try {
+            String externalName = fqn.substring(0, 
fqn.indexOf('.')).replace('/', '.');
+            ClassLoader loader = getClassLoader();
+            log.trace("Checking to see if class " + externalName + " matches 
criteria [" + test+ "]");
+
+            Class type = loader.loadClass(externalName);
+            if (test.matches(type) ) {
+                matches.add( (Class<T>) type);
+            }
+        }
+        catch (Throwable t) {
+            log.warn("Could not examine class '"+ fqn + "' due to a " +
+                     t.getClass().getName()+ " with message: " + 
t.getMessage());
+        }
+    }
+}
\ No newline at end of file

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/main/java/org/apache/servicemix/bean/support/ResolverUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java?view=auto&rev=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
 (added)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
 Fri Oct 20 03:12:26 2006
@@ -0,0 +1,100 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.bean;
+
+import org.apache.servicemix.tck.SpringTestSupport;
+import org.apache.servicemix.client.DefaultServiceMixClient;
+import org.apache.servicemix.jbi.resolver.URIResolver;
+import org.apache.servicemix.jbi.jaxp.StringSource;
+import org.apache.servicemix.jbi.jaxp.SourceTransformer;
+import org.apache.servicemix.bean.beans.PlainBean;
+import org.apache.xbean.spring.context.ClassPathXmlApplicationContext;
+import org.w3c.dom.DocumentFragment;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+
+import javax.jbi.servicedesc.ServiceEndpoint;
+import javax.jbi.messaging.InOnly;
+import javax.jbi.messaging.MessageExchange;
+import javax.jbi.messaging.NormalizedMessage;
+import javax.jbi.messaging.ExchangeStatus;
+import javax.xml.namespace.QName;
+
+public class AutoDeployedEndpointTest extends SpringTestSupport {
+
+    public void testSendingToDynamicEndpointForPlainBeanWithFooOperation() 
throws Exception {
+        // now lets make a request on this endpoint
+        DefaultServiceMixClient client = new DefaultServiceMixClient(jbi);
+
+        InOnly exchange = client.createInOnlyExchange();
+        exchange.setService(new QName("urn:test", "service"));
+        exchange.setOperation(new QName("foo"));
+        exchange.getInMessage().setContent(new 
StringSource("<hello>world</hello>"));
+        client.sendSync(exchange);
+
+        assertExchangeWorked(exchange);
+
+        /*
+        PlainBean bean = (PlainBean) getBean("plainBean");
+        MessageExchange answer = bean.getFoo();
+
+        log.info("Bean's foo() method has been invoked: " + answer);
+
+        assertNotNull("Bean's foo() method should bave been invoked", answer);
+        */
+    }
+
+    public void testSendingToDynamicEndpointForPlainBeanWithBarOperation() 
throws Exception {
+        // now lets make a request on this endpoint
+        DefaultServiceMixClient client = new DefaultServiceMixClient(jbi);
+
+        InOnly exchange = client.createInOnlyExchange();
+        exchange.setService(new QName("urn:test", "service"));
+        exchange.setOperation(new QName("bar"));
+        exchange.getInMessage().setContent(new 
StringSource("<hello>world</hello>"));
+        client.sendSync(exchange);
+
+        assertExchangeWorked(exchange);
+
+        /*
+        PlainBean bean = (PlainBean) getBean("plainBean");
+        MessageExchange bar = bean.getBar();
+        log.info("Bean's bar() method has been invoked: " + bar);
+
+        assertNotNull("Bean's bar() method should bave been invoked", bar);
+        */
+    }
+
+    protected void assertExchangeWorked(MessageExchange me) throws Exception {
+        if (me.getStatus() == ExchangeStatus.ERROR) {
+            if (me.getError() != null) {
+                throw me.getError();
+            }
+            else {
+                fail("Received ERROR status");
+            }
+        }
+        else if (me.getFault() != null) {
+            fail("Received fault: " + new 
SourceTransformer().toString(me.getFault().getContent()));
+        }
+    }
+
+    protected AbstractXmlApplicationContext createBeanFactory() {
+        return new 
ClassPathXmlApplicationContext("spring-no-endpoints-or-beans.xml");
+    }
+
+}

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/AutoDeployedEndpointTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java?view=auto&rev=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
 (added)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
 Fri Oct 20 03:12:26 2006
@@ -0,0 +1,51 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.bean.beans;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.bean.Property;
+import org.apache.servicemix.bean.XPath;
+import org.apache.servicemix.bean.Content;
+import org.apache.servicemix.bean.Endpoint;
+
+import javax.jbi.messaging.MessageExchange;
+
+/**
+ * A simple POJO which uses annotations to expose it on a JBI bus
+ *
+ * @version $Revision: $
+ */
[EMAIL PROTECTED](name="cheese", targetNamespace = "urn:test", serviceName = 
"service")
+public class AutoDeployedBean {
+
+    private static final Log log = LogFactory.getLog(AutoDeployedBean.class);
+
+    private MessageExchange foo;
+    private MessageExchange bar;
+
+    public void foo(MessageExchange messageExchange) {
+        this.foo = messageExchange;
+        log.info("foo() called with exchange: " + messageExchange);
+    }
+
+    public void bar(MessageExchange messageExchange) {
+        this.bar = messageExchange;
+        log.info("bar() called with exchange: " + messageExchange);
+    }
+}

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/AutoDeployedBean.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/ConsumerListener.java
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/ConsumerListener.java?view=diff&rev=466049&r1=466048&r2=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/ConsumerListener.java
 (original)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/test/java/org/apache/servicemix/bean/beans/ConsumerListener.java
 Fri Oct 20 03:12:26 2006
@@ -16,27 +16,33 @@
  */
 package org.apache.servicemix.bean.beans;
 
+import org.apache.servicemix.MessageExchangeListener;
+import org.apache.servicemix.jbi.util.MessageUtil;
+
 import javax.annotation.Resource;
 import javax.jbi.messaging.DeliveryChannel;
 import javax.jbi.messaging.InOut;
 import javax.jbi.messaging.MessageExchange;
 import javax.jbi.messaging.MessageExchangeFactory;
 import javax.jbi.messaging.MessagingException;
-import javax.jbi.messaging.NormalizedMessage;
-
-import org.apache.servicemix.MessageExchangeListener;
-import org.apache.servicemix.jbi.util.MessageUtil;
 
 public class ConsumerListener implements MessageExchangeListener {
 
     @Resource
     private DeliveryChannel channel;
-    
+
     public void onMessageExchange(MessageExchange exchange) throws 
MessagingException {
         MessageExchangeFactory factory = channel.createExchangeFactory();
         InOut io = factory.createInOutExchange();
-        NormalizedMessage nm = io.createMessage();
-        MessageUtil.transferInToIn(exchange, io);
+        try {
+            MessageUtil.transferInToIn(exchange, io);
+        }
+        catch (MessagingException e) {
+            throw e;
+        }
+        catch (Exception e) {
+            throw new MessagingException(e);
+        }
     }
 
 }

Added: 
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
URL: 
http://svn.apache.org/viewvc/incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml?view=auto&rev=466049
==============================================================================
--- 
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
 (added)
+++ 
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
 Fri Oct 20 03:12:26 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<beans xmlns:sm="http://servicemix.apache.org/config/1.0";
+       xmlns:bean="http://servicemix.apache.org/bean/1.0";
+       xmlns:test="urn:test">
+
+  <sm:container id="jbi" embedded="true" createMBeanServer="false">
+    <sm:activationSpecs>
+      <sm:activationSpec>
+        <sm:component>
+          <bean:component searchPackages="org.apache.servicemix.bean.beans"/>
+        </sm:component>
+      </sm:activationSpec>
+    </sm:activationSpecs>
+  </sm:container>
+
+</beans>

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
incubator/servicemix/trunk/servicemix-bean/src/test/resources/spring-no-endpoints-or-beans.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml


Reply via email to