001/*
002 * Copyright 2002-2018 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.net.InetSocketAddress;
020import java.util.List;
021import javax.jws.WebService;
022import javax.xml.ws.Endpoint;
023import javax.xml.ws.WebServiceProvider;
024
025import com.sun.net.httpserver.Authenticator;
026import com.sun.net.httpserver.Filter;
027import com.sun.net.httpserver.HttpContext;
028import com.sun.net.httpserver.HttpServer;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031
032import org.springframework.lang.UsesSunHttpServer;
033
034/**
035 * Simple exporter for JAX-WS services, autodetecting annotated service beans
036 * (through the JAX-WS {@link javax.jws.WebService} annotation) and exporting
037 * them through the HTTP server included in Sun's JDK 1.6. The full address
038 * for each service will consist of the server's base address with the
039 * service name appended (e.g. "http://localhost:8080/OrderService").
040 *
041 * <p>Note that this exporter will only work on Sun's JDK 1.6 or higher, as well
042 * as on JDKs that ship Sun's entire class library as included in the Sun JDK.
043 * For a portable JAX-WS exporter, have a look at {@link SimpleJaxWsServiceExporter}.
044 *
045 * @author Juergen Hoeller
046 * @since 2.5.5
047 * @see javax.jws.WebService
048 * @see javax.xml.ws.Endpoint#publish(Object)
049 * @see SimpleJaxWsServiceExporter
050 */
051@UsesSunHttpServer
052public class SimpleHttpServerJaxWsServiceExporter extends AbstractJaxWsServiceExporter {
053
054        protected final Log logger = LogFactory.getLog(getClass());
055
056        private HttpServer server;
057
058        private int port = 8080;
059
060        private String hostname;
061
062        private int backlog = -1;
063
064        private int shutdownDelay = 0;
065
066        private String basePath = "/";
067
068        private List<Filter> filters;
069
070        private Authenticator authenticator;
071
072        private boolean localServer = false;
073
074
075        /**
076         * Specify an existing HTTP server to register the web service contexts
077         * with. This will typically be a server managed by the general Spring
078         * {@link org.springframework.remoting.support.SimpleHttpServerFactoryBean}.
079         * <p>Alternatively, configure a local HTTP server through the
080         * {@link #setPort "port"}, {@link #setHostname "hostname"} and
081         * {@link #setBacklog "backlog"} properties (or rely on the defaults there).
082         */
083        public void setServer(HttpServer server) {
084                this.server = server;
085        }
086
087        /**
088         * Specify the HTTP server's port. Default is 8080.
089         * <p>Only applicable for a locally configured HTTP server.
090         * Ignored when the {@link #setServer "server"} property has been specified.
091         */
092        public void setPort(int port) {
093                this.port = port;
094        }
095
096        /**
097         * Specify the HTTP server's hostname to bind to. Default is localhost;
098         * can be overridden with a specific network address to bind to.
099         * <p>Only applicable for a locally configured HTTP server.
100         * Ignored when the {@link #setServer "server"} property has been specified.
101         */
102        public void setHostname(String hostname) {
103                this.hostname = hostname;
104        }
105
106        /**
107         * Specify the HTTP server's TCP backlog. Default is -1,
108         * indicating the system's default value.
109         * <p>Only applicable for a locally configured HTTP server.
110         * Ignored when the {@link #setServer "server"} property has been specified.
111         */
112        public void setBacklog(int backlog) {
113                this.backlog = backlog;
114        }
115
116        /**
117         * Specify the number of seconds to wait until HTTP exchanges have
118         * completed when shutting down the HTTP server. Default is 0.
119         * <p>Only applicable for a locally configured HTTP server.
120         * Ignored when the {@link #setServer "server"} property has been specified.
121         */
122        public void setShutdownDelay(int shutdownDelay) {
123                this.shutdownDelay = shutdownDelay;
124        }
125
126        /**
127         * Set the base path for context publication. Default is "/".
128         * <p>For each context publication path, the service name will be
129         * appended to this base address. E.g. service name "OrderService"
130         * -> "/OrderService".
131         * @see javax.xml.ws.Endpoint#publish(Object)
132         * @see javax.jws.WebService#serviceName()
133         */
134        public void setBasePath(String basePath) {
135                this.basePath = basePath;
136        }
137
138        /**
139         * Register common {@link com.sun.net.httpserver.Filter Filters} to be
140         * applied to all detected {@link javax.jws.WebService} annotated beans.
141         */
142        public void setFilters(List<Filter> filters) {
143                this.filters = filters;
144        }
145
146        /**
147         * Register a common {@link com.sun.net.httpserver.Authenticator} to be
148         * applied to all detected {@link javax.jws.WebService} annotated beans.
149         */
150        public void setAuthenticator(Authenticator authenticator) {
151                this.authenticator = authenticator;
152        }
153
154
155        @Override
156        public void afterPropertiesSet() throws Exception {
157                if (this.server == null) {
158                        InetSocketAddress address = (this.hostname != null ?
159                                        new InetSocketAddress(this.hostname, this.port) : new InetSocketAddress(this.port));
160                        HttpServer server = HttpServer.create(address, this.backlog);
161                        if (logger.isInfoEnabled()) {
162                                logger.info("Starting HttpServer at address " + address);
163                        }
164                        server.start();
165                        this.server = server;
166                        this.localServer = true;
167                }
168                super.afterPropertiesSet();
169        }
170
171        @Override
172        protected void publishEndpoint(Endpoint endpoint, WebService annotation) {
173                endpoint.publish(buildHttpContext(endpoint, annotation.serviceName()));
174        }
175
176        @Override
177        protected void publishEndpoint(Endpoint endpoint, WebServiceProvider annotation) {
178                endpoint.publish(buildHttpContext(endpoint, annotation.serviceName()));
179        }
180
181        /**
182         * Build the HttpContext for the given endpoint.
183         * @param endpoint the JAX-WS Provider Endpoint object
184         * @param serviceName the given service name
185         * @return the fully populated HttpContext
186         */
187        protected HttpContext buildHttpContext(Endpoint endpoint, String serviceName) {
188                String fullPath = calculateEndpointPath(endpoint, serviceName);
189                HttpContext httpContext = this.server.createContext(fullPath);
190                if (this.filters != null) {
191                        httpContext.getFilters().addAll(this.filters);
192                }
193                if (this.authenticator != null) {
194                        httpContext.setAuthenticator(this.authenticator);
195                }
196                return httpContext;
197        }
198
199        /**
200         * Calculate the full endpoint path for the given endpoint.
201         * @param endpoint the JAX-WS Provider Endpoint object
202         * @param serviceName the given service name
203         * @return the full endpoint path
204         */
205        protected String calculateEndpointPath(Endpoint endpoint, String serviceName) {
206                return this.basePath + serviceName;
207        }
208
209
210        @Override
211        public void destroy() {
212                super.destroy();
213                if (this.localServer) {
214                        logger.info("Stopping HttpServer");
215                        this.server.stop(this.shutdownDelay);
216                }
217        }
218
219}