001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.remoting.jaxws;
018
019import java.util.Arrays;
020import java.util.LinkedHashSet;
021import java.util.Map;
022import java.util.Set;
023import java.util.concurrent.Executor;
024import javax.jws.WebService;
025import javax.xml.ws.Endpoint;
026import javax.xml.ws.WebServiceFeature;
027import javax.xml.ws.WebServiceProvider;
028
029import org.springframework.beans.BeanUtils;
030import org.springframework.beans.factory.BeanFactory;
031import org.springframework.beans.factory.BeanFactoryAware;
032import org.springframework.beans.factory.CannotLoadBeanClassException;
033import org.springframework.beans.factory.DisposableBean;
034import org.springframework.beans.factory.InitializingBean;
035import org.springframework.beans.factory.ListableBeanFactory;
036import org.springframework.beans.factory.config.ConfigurableBeanFactory;
037import org.springframework.lang.UsesJava7;
038import org.springframework.util.Assert;
039import org.springframework.util.ClassUtils;
040
041/**
042 * Abstract exporter for JAX-WS services, autodetecting annotated service beans
043 * (through the JAX-WS {@link javax.jws.WebService} annotation). Compatible with
044 * JAX-WS 2.1 and 2.2, as included in JDK 6 update 4+ and Java 7/8.
045 *
046 * <p>Subclasses need to implement the {@link #publishEndpoint} template methods
047 * for actual endpoint exposure.
048 *
049 * @author Juergen Hoeller
050 * @since 2.5.5
051 * @see javax.jws.WebService
052 * @see javax.xml.ws.Endpoint
053 * @see SimpleJaxWsServiceExporter
054 * @see SimpleHttpServerJaxWsServiceExporter
055 */
056public abstract class AbstractJaxWsServiceExporter implements BeanFactoryAware, InitializingBean, DisposableBean {
057
058        private Map<String, Object> endpointProperties;
059
060        private Executor executor;
061
062        private String bindingType;
063
064        private WebServiceFeature[] endpointFeatures;
065
066        private Object[] webServiceFeatures;
067
068        private ListableBeanFactory beanFactory;
069
070        private final Set<Endpoint> publishedEndpoints = new LinkedHashSet<Endpoint>();
071
072
073        /**
074         * Set the property bag for the endpoint, including properties such as
075         * "javax.xml.ws.wsdl.service" or "javax.xml.ws.wsdl.port".
076         * @see javax.xml.ws.Endpoint#setProperties
077         * @see javax.xml.ws.Endpoint#WSDL_SERVICE
078         * @see javax.xml.ws.Endpoint#WSDL_PORT
079         */
080        public void setEndpointProperties(Map<String, Object> endpointProperties) {
081                this.endpointProperties = endpointProperties;
082        }
083
084        /**
085         * Set the JDK concurrent executor to use for dispatching incoming requests
086         * to exported service instances.
087         * @see javax.xml.ws.Endpoint#setExecutor
088         */
089        public void setExecutor(Executor executor) {
090                this.executor = executor;
091        }
092
093        /**
094         * Specify the binding type to use, overriding the value of
095         * the JAX-WS {@link javax.xml.ws.BindingType} annotation.
096         */
097        public void setBindingType(String bindingType) {
098                this.bindingType = bindingType;
099        }
100
101        /**
102         * Specify WebServiceFeature objects (e.g. as inner bean definitions)
103         * to apply to JAX-WS endpoint creation.
104         * @since 4.0
105         */
106        public void setEndpointFeatures(WebServiceFeature... endpointFeatures) {
107                this.endpointFeatures = endpointFeatures;
108        }
109
110        /**
111         * Allows for providing JAX-WS 2.2 WebServiceFeature specifications:
112         * in the form of actual {@link javax.xml.ws.WebServiceFeature} objects,
113         * WebServiceFeature Class references, or WebServiceFeature class names.
114         * <p>As of Spring 4.0, this is effectively just an alternative way of
115         * specifying {@link #setEndpointFeatures "endpointFeatures"}. Do not specify
116         * both properties at the same time; prefer "endpointFeatures" moving forward.
117         * @deprecated as of Spring 4.0, in favor of {@link #setEndpointFeatures}
118         */
119        @Deprecated
120        public void setWebServiceFeatures(Object[] webServiceFeatures) {
121                this.webServiceFeatures = webServiceFeatures;
122        }
123
124        /**
125         * Obtains all web service beans and publishes them as JAX-WS endpoints.
126         */
127        @Override
128        public void setBeanFactory(BeanFactory beanFactory) {
129                if (!(beanFactory instanceof ListableBeanFactory)) {
130                        throw new IllegalStateException(getClass().getSimpleName() + " requires a ListableBeanFactory");
131                }
132                this.beanFactory = (ListableBeanFactory) beanFactory;
133        }
134
135
136        /**
137         * Immediately publish all endpoints when fully configured.
138         * @see #publishEndpoints()
139         */
140        @Override
141        public void afterPropertiesSet() throws Exception {
142                publishEndpoints();
143        }
144
145        /**
146         * Publish all {@link javax.jws.WebService} annotated beans in the
147         * containing BeanFactory.
148         * @see #publishEndpoint
149         */
150        public void publishEndpoints() {
151                Set<String> beanNames = new LinkedHashSet<String>(this.beanFactory.getBeanDefinitionCount());
152                beanNames.addAll(Arrays.asList(this.beanFactory.getBeanDefinitionNames()));
153                if (this.beanFactory instanceof ConfigurableBeanFactory) {
154                        beanNames.addAll(Arrays.asList(((ConfigurableBeanFactory) this.beanFactory).getSingletonNames()));
155                }
156                for (String beanName : beanNames) {
157                        try {
158                                Class<?> type = this.beanFactory.getType(beanName);
159                                if (type != null && !type.isInterface()) {
160                                        WebService wsAnnotation = type.getAnnotation(WebService.class);
161                                        WebServiceProvider wsProviderAnnotation = type.getAnnotation(WebServiceProvider.class);
162                                        if (wsAnnotation != null || wsProviderAnnotation != null) {
163                                                Endpoint endpoint = createEndpoint(this.beanFactory.getBean(beanName));
164                                                if (this.endpointProperties != null) {
165                                                        endpoint.setProperties(this.endpointProperties);
166                                                }
167                                                if (this.executor != null) {
168                                                        endpoint.setExecutor(this.executor);
169                                                }
170                                                if (wsAnnotation != null) {
171                                                        publishEndpoint(endpoint, wsAnnotation);
172                                                }
173                                                else {
174                                                        publishEndpoint(endpoint, wsProviderAnnotation);
175                                                }
176                                                this.publishedEndpoints.add(endpoint);
177                                        }
178                                }
179                        }
180                        catch (CannotLoadBeanClassException ex) {
181                                // ignore beans where the class is not resolvable
182                        }
183                }
184        }
185
186        /**
187         * Create the actual Endpoint instance.
188         * @param bean the service object to wrap
189         * @return the Endpoint instance
190         * @see Endpoint#create(Object)
191         * @see Endpoint#create(String, Object)
192         */
193        @UsesJava7  // optional use of Endpoint#create with WebServiceFeature[]
194        protected Endpoint createEndpoint(Object bean) {
195                if (this.endpointFeatures != null || this.webServiceFeatures != null) {
196                        WebServiceFeature[] endpointFeaturesToUse = this.endpointFeatures;
197                        if (endpointFeaturesToUse == null) {
198                                endpointFeaturesToUse = new WebServiceFeature[this.webServiceFeatures.length];
199                                for (int i = 0; i < this.webServiceFeatures.length; i++) {
200                                        endpointFeaturesToUse[i] = convertWebServiceFeature(this.webServiceFeatures[i]);
201                                }
202                        }
203                        return Endpoint.create(this.bindingType, bean, endpointFeaturesToUse);
204                }
205                else {
206                        return Endpoint.create(this.bindingType, bean);
207                }
208        }
209
210        private WebServiceFeature convertWebServiceFeature(Object feature) {
211                Assert.notNull(feature, "WebServiceFeature specification object must not be null");
212                if (feature instanceof WebServiceFeature) {
213                        return (WebServiceFeature) feature;
214                }
215                else if (feature instanceof Class) {
216                        return (WebServiceFeature) BeanUtils.instantiate((Class<?>) feature);
217                }
218                else if (feature instanceof String) {
219                        try {
220                                Class<?> featureClass = getBeanClassLoader().loadClass((String) feature);
221                                return (WebServiceFeature) BeanUtils.instantiate(featureClass);
222                        }
223                        catch (ClassNotFoundException ex) {
224                                throw new IllegalArgumentException("Could not load WebServiceFeature class [" + feature + "]");
225                        }
226                }
227                else {
228                        throw new IllegalArgumentException("Unknown WebServiceFeature specification type: " + feature.getClass());
229                }
230        }
231
232        private ClassLoader getBeanClassLoader() {
233                return (beanFactory instanceof ConfigurableBeanFactory ?
234                                ((ConfigurableBeanFactory) beanFactory).getBeanClassLoader() : ClassUtils.getDefaultClassLoader());
235        }
236
237
238        /**
239         * Actually publish the given endpoint. To be implemented by subclasses.
240         * @param endpoint the JAX-WS Endpoint object
241         * @param annotation the service bean's WebService annotation
242         */
243        protected abstract void publishEndpoint(Endpoint endpoint, WebService annotation);
244
245        /**
246         * Actually publish the given provider endpoint. To be implemented by subclasses.
247         * @param endpoint the JAX-WS Provider Endpoint object
248         * @param annotation the service bean's WebServiceProvider annotation
249         */
250        protected abstract void publishEndpoint(Endpoint endpoint, WebServiceProvider annotation);
251
252
253        /**
254         * Stops all published endpoints, taking the web services offline.
255         */
256        @Override
257        public void destroy() {
258                for (Endpoint endpoint : this.publishedEndpoints) {
259                        endpoint.stop();
260                }
261        }
262
263}