You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by bi...@apache.org on 2009/10/17 02:34:01 UTC

svn commit: r826144 - in /cxf/trunk: parent/ rt/frontend/jaxws/ rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/ systests/jaxws/

Author: bimargulies
Date: Sat Oct 17 00:34:01 2009
New Revision: 826144

URL: http://svn.apache.org/viewvc?rev=826144&view=rev
Log:
Initial cut at code to scan spring context for things with @WebServices.

Added:
    cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Jsr181HandlerMapping.java   (with props)
    cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties   (with props)
Modified:
    cxf/trunk/parent/pom.xml
    cxf/trunk/rt/frontend/jaxws/pom.xml
    cxf/trunk/systests/jaxws/pom.xml

Modified: cxf/trunk/parent/pom.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/parent/pom.xml?rev=826144&r1=826143&r2=826144&view=diff
==============================================================================
--- cxf/trunk/parent/pom.xml (original)
+++ cxf/trunk/parent/pom.xml Sat Oct 17 00:34:01 2009
@@ -774,6 +774,52 @@
                 </exclusions>
             </dependency>
             <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-webmvc</artifactId>
+                <version>${spring.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>log4j</groupId>
+                        <artifactId>log4j</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>javax.servlet</groupId>
+                        <artifactId>servlet-api</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>logkit</groupId>
+                        <artifactId>logkit</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>avalon-framework</groupId>
+                        <artifactId>avalon-framework</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-aop</artifactId>
+                <version>${spring.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>log4j</groupId>
+                        <artifactId>log4j</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>javax.servlet</groupId>
+                        <artifactId>servlet-api</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>logkit</groupId>
+                        <artifactId>logkit</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>avalon-framework</groupId>
+                        <artifactId>avalon-framework</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
                 <groupId>commons-httpclient</groupId>
                 <artifactId>commons-httpclient</artifactId>
                 <version>3.1</version>

Modified: cxf/trunk/rt/frontend/jaxws/pom.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxws/pom.xml?rev=826144&r1=826143&r2=826144&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxws/pom.xml (original)
+++ cxf/trunk/rt/frontend/jaxws/pom.xml Sat Oct 17 00:34:01 2009
@@ -118,7 +118,37 @@
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
-            <scope>test</scope>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aop</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <scope>provided</scope>
             <version>${spring.version}</version>
         </dependency>
         <dependency>

Added: cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Jsr181HandlerMapping.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Jsr181HandlerMapping.java?rev=826144&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Jsr181HandlerMapping.java (added)
+++ cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Jsr181HandlerMapping.java Sat Oct 17 00:34:01 2009
@@ -0,0 +1,282 @@
+/**
+ * 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.cxf.jaxws.spring;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.logging.Logger;
+
+import javax.jws.WebService;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.i18n.Message;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.databinding.DataBinding;
+import org.apache.cxf.frontend.ServerFactoryBean;
+import org.apache.cxf.jaxb.JAXBDataBinding;
+import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
+import org.apache.cxf.service.factory.ReflectionServiceFactoryBean;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.web.context.ServletConfigAware;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
+import org.springframework.web.servlet.mvc.Controller;
+
+// All tests for this are in systests, since there's no place else to assemble all the necessary dependencies.
+
+/**
+ * Bean to scan context for potential web services. This scans the beans for classes that
+ * are annotated with @WebService. Excepting those already declared via the JAX-WS Spring
+ * schema, it launches each as an endpoint.
+ * 
+ * By default, it sets up a default JaxWsServiceFactory and JAX-B data binding,
+ * and then creates a URL under /services/ based on the service name. Properties of the bean
+ * permit you to configure this; if you set prototypeServiceFactoryBeanName, the code
+ * will fetch that bean. It must be a prototype, since service factory object can't be used
+ * for more than one endpoint. Similiarly, prototypeDataBindingBeanName can be used to 
+ * control the data binding.
+ * 
+ * Note that this class uses {@link org.apache.cxf.transport.servlet#CXFServlet} from the 
+ * cxf-rt-transports-http-jetty library, which is not part of the standard dependencies of the JAX-WS front
+ * end.
+ * 
+ */
+public class Jsr181HandlerMapping extends AbstractUrlHandlerMapping implements BeanPostProcessor,
+    ServletConfigAware, BeanFactoryAware {
+    
+    private static final Logger LOG = LogUtils.getL7dLogger(Jsr181HandlerMapping.class);
+    
+    private static final String CXF_SERVLET_CLASS_NAME = "org.apache.cxf.transport.servlet";
+    private static final String AOP_UTILS_CLASS_NAME = "org.springframework.aop.support.AopUtils";
+    private Class<?> servletClass;
+    private Method servletGetBusMethod;
+    private Method aopUtilsGetTargetClassMethod;
+
+    private String urlPrefix = "/services/";
+    private Servlet shadowCxfServlet;
+    private String prototypeDataBindingBeanName;
+    private String prototypeServiceFactoryBeanName;
+    private BeanFactory beanFactory;
+    
+    public Jsr181HandlerMapping() throws SecurityException, NoSuchMethodException, ClassNotFoundException {
+        try {
+            servletClass = ClassLoaderUtils.loadClass(CXF_SERVLET_CLASS_NAME, getClass());
+        } catch (ClassNotFoundException e) {
+            Message message = new Message("SERVLET_CLASS_MISSING", LOG, CXF_SERVLET_CLASS_NAME);
+            LOG.severe(message.toString());
+            throw e;
+        }
+        servletGetBusMethod = servletClass.getMethod("getBus");
+        try {
+            Class<?> aopUtilsClass = ClassLoaderUtils.loadClass(AOP_UTILS_CLASS_NAME, getClass());
+            aopUtilsGetTargetClassMethod = aopUtilsClass.getMethod("getTargetClass", Object.class);
+        } catch (Exception e) {
+            Message message = new Message("AOP_GET_TARGET_CLASS_MISSING", LOG);
+            LOG.severe(message.toString());
+            
+        }
+    }
+    
+    private Bus getServletBus() {
+        try {
+            return (Bus) servletGetBusMethod.invoke(shadowCxfServlet);
+        } catch (Exception e) {
+            // CXF internally inconsistent? 
+            throw new RuntimeException(e);
+        }
+    }
+
+  
+    /**
+     * Set the prefix for the generated endpoint URLs.
+     * @param urlPrefix
+     */
+    public void setUrlPrefix(String urlPrefix) {
+        this.urlPrefix = urlPrefix;
+    }
+
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        return bean;
+    }
+    
+    private Class<?> aopUtilsGetTargetClass(Object possiblyProxiedObject) {
+        if (aopUtilsGetTargetClassMethod != null && possiblyProxiedObject != null) {
+            try {
+                return (Class<?>)aopUtilsGetTargetClassMethod.invoke(null, possiblyProxiedObject);
+            } catch (IllegalArgumentException e) {
+                return possiblyProxiedObject.getClass();
+            } catch (IllegalAccessException e) {
+                return possiblyProxiedObject.getClass();
+            } catch (InvocationTargetException e) {
+                return possiblyProxiedObject.getClass();
+            }
+        }
+        return possiblyProxiedObject.getClass();
+    }
+
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        Class<?> clazz = aopUtilsGetTargetClass(bean);
+
+        if (clazz.isAnnotationPresent(WebService.class)) {
+            WebService ws = (WebService)clazz.getAnnotation(WebService.class);
+            String url = urlPrefix + ws.serviceName();
+            Message message = new Message("SELECTED_SERVICE", LOG, beanName,
+                                          clazz.getName(),
+                                          url);
+            LOG.info(message.toString());
+
+            createAndPublishEndpoint(url, bean);
+            registerHandler(url, new ServletAdapter(shadowCxfServlet));
+        } else {
+            if (logger.isDebugEnabled()) {
+                Message message = new Message("REJECTED_NO_ANNOTATION", LOG, beanName,
+                                              clazz.getName());
+                LOG.fine(message.toString());
+            }
+        }
+
+        return bean;
+    }
+    
+    private void createAndPublishEndpoint(String url, Object implementor) {
+        ReflectionServiceFactoryBean serviceFactory = null;
+        if (prototypeServiceFactoryBeanName != null) {
+            if (!beanFactory.isPrototype(prototypeServiceFactoryBeanName)) {
+                throw 
+                    new IllegalArgumentException(
+                        "prototypeServiceFactoryBeanName must indicate a scope='prototype' bean");
+            }
+            serviceFactory = (ReflectionServiceFactoryBean)
+                             beanFactory.getBean(prototypeServiceFactoryBeanName, 
+                                                 ReflectionServiceFactoryBean.class); 
+        } else {
+            serviceFactory = new JaxWsServiceFactoryBean();
+        }
+
+        ServerFactoryBean serverFactoryBean = new ServerFactoryBean();
+        serverFactoryBean.setServiceBean(implementor);
+        serverFactoryBean.setServiceClass(aopUtilsGetTargetClass(implementor));
+        serverFactoryBean.setAddress(url);
+        
+        DataBinding dataBinding = null;
+        if (prototypeDataBindingBeanName != null) {
+            if (!beanFactory.isPrototype(prototypeDataBindingBeanName)) {
+                throw 
+                    new IllegalArgumentException(
+                        "prototypeDataBindingBeanName must indicate a scope='prototype' bean");
+            }
+            dataBinding = (DataBinding)
+                             beanFactory.getBean(prototypeDataBindingBeanName, 
+                                                 DataBinding.class); 
+        } else {
+            dataBinding = new JAXBDataBinding();
+        }
+        
+        serverFactoryBean.setDataBinding(dataBinding);
+        serverFactoryBean.setServiceFactory(serviceFactory);
+        serverFactoryBean.setBus(getServletBus());
+        serverFactoryBean.create();
+    }
+
+    public void setServletConfig(ServletConfig servletConfig) {
+
+        try {
+            shadowCxfServlet = (Servlet)servletClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        try {
+            shadowCxfServlet.init(servletConfig);
+        } catch (ServletException ex) {
+            throw new RuntimeException(ex.getMessage(), ex);
+        }
+    }
+
+    public static class ServletAdapter implements Controller {
+
+        private Servlet controller;
+
+        public ServletAdapter(Servlet controller) {
+            this.controller = controller;
+        }
+
+        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
+            throws Exception {
+            controller.service(request, response);
+            return null;
+        }
+    }
+
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        this.beanFactory = beanFactory; 
+        
+    }
+    
+    public String getPrototypeServiceFactoryBeanName() {
+        return prototypeServiceFactoryBeanName;
+    }
+
+    /**
+     * Set the service factory for all services launched by this bean. This must be the name of a 
+     * scope='prototype' bean that implements 
+     * {@link org.apache.cxf.service.factory#ReflectionServiceFactoryBean}.
+     * @param prototypeServiceFactoryBeanName
+     */
+    public void setPrototypeServiceFactoryBeanName(String prototypeServiceFactoryBeanName) {
+        this.prototypeServiceFactoryBeanName = prototypeServiceFactoryBeanName;
+    }
+    
+    public String getPrototypeDataBindingBeanName() {
+        return prototypeDataBindingBeanName;
+    }
+
+    /**
+     * Set the data binding for all services launched by this bean. This must be the name of a 
+     * scope='prototype' bean that implements {@link org.apache.cxf.databinding#DataBinding}.
+     * @param prototypeDataBindingBeanName
+     */
+    public void setPrototypeDataBindingBeanName(String prototypeDataBindingBeanName) {
+        this.prototypeDataBindingBeanName = prototypeDataBindingBeanName;
+    }
+ 
+    /**
+     * For unit testing ONLY. Disable use of Spring-AOP method to unwrap target classes, to
+     * ensure that this code works when AOP is not there.
+     * @param b
+     */
+    public void setSimulateNoAopForUnitTest(boolean b) {
+        if (b) {
+            aopUtilsGetTargetClassMethod = null;
+        }
+    }
+    
+}

Propchange: cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Jsr181HandlerMapping.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties?rev=826144&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties (added)
+++ cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties Sat Oct 17 00:34:01 2009
@@ -0,0 +1,24 @@
+#
+#
+#    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.
+#
+#
+SERVLET_CLASS_MISSING=The CXF Servlet class {0} is not available in the classpath. 
+AOP_GET_TARGET_CLASS_MISSING=The Spring method AopUtils.getTargetClass() is missing. Check Spring dependencies.
+SELECTED_SERVICE=Publishing bean {0} class {1} as a service at {1}.
+REJECTED_NO_ANNOTATION=Rejected bean {0} class {1}; no @WebService annotation.

Propchange: cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/spring/Messages.properties
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: cxf/trunk/systests/jaxws/pom.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxws/pom.xml?rev=826144&r1=826143&r2=826144&view=diff
==============================================================================
--- cxf/trunk/systests/jaxws/pom.xml (original)
+++ cxf/trunk/systests/jaxws/pom.xml Sat Oct 17 00:34:01 2009
@@ -201,6 +201,43 @@
           <artifactId>cxf-rt-testsupport</artifactId>
           <version>${project.version}</version>
         </dependency>
+        
+         <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aop</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <scope>provided</scope>
+            <version>${spring.version}</version>
+        </dependency>
 
     </dependencies>
     <properties>