View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.socket;
18  
19  import java.io.IOException;
20  import javax.servlet.DispatcherType;
21  import javax.servlet.Filter;
22  import javax.servlet.Servlet;
23  import javax.servlet.ServletException;
24  
25  import io.undertow.Undertow;
26  import io.undertow.server.HttpHandler;
27  import io.undertow.servlet.api.DeploymentInfo;
28  import io.undertow.servlet.api.DeploymentManager;
29  import io.undertow.servlet.api.FilterInfo;
30  import io.undertow.servlet.api.InstanceFactory;
31  import io.undertow.servlet.api.InstanceHandle;
32  import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
33  import org.xnio.ByteBufferSlicePool;
34  import org.xnio.OptionMap;
35  import org.xnio.Xnio;
36  
37  import org.springframework.util.Assert;
38  import org.springframework.util.SocketUtils;
39  import org.springframework.web.context.WebApplicationContext;
40  import org.springframework.web.servlet.DispatcherServlet;
41  
42  import static io.undertow.servlet.Servlets.*;
43  
44  /**
45   * Undertow-based {@link WebSocketTestServer}.
46   *
47   * @author Rossen Stoyanchev
48   */
49  public class UndertowTestServer implements WebSocketTestServer {
50  
51  	private int port = -1;
52  
53  	private Undertow server;
54  
55  	private DeploymentManager manager;
56  
57  
58  	@Override
59  	public void setup() {
60  		this.port = SocketUtils.findAvailableTcpPort();
61  	}
62  
63  	@Override
64  	public int getPort() {
65  		return this.port;
66  	}
67  
68  	@Override
69  	public void deployConfig(WebApplicationContext cxt, Filter... filters) {
70  		Assert.state(this.port != -1, "setup() was never called");
71  		DispatcherServletInstanceFactory servletFactory = new DispatcherServletInstanceFactory(cxt);
72  		// manually building WebSocketDeploymentInfo in order to avoid class cast exceptions
73  		// with tomcat's implementation when using undertow 1.1.0+
74  		WebSocketDeploymentInfo info = new WebSocketDeploymentInfo();
75  		try {
76  			info.setWorker(Xnio.getInstance().createWorker(OptionMap.EMPTY));
77  			info.setBuffers(new ByteBufferSlicePool(1024,1024));
78  		}
79  		catch (IOException ex) {
80  			throw new IllegalStateException(ex);
81  		}
82  
83  		DeploymentInfo servletBuilder = deployment()
84  				.setClassLoader(UndertowTestServer.class.getClassLoader())
85  				.setDeploymentName("undertow-websocket-test")
86  				.setContextPath("/")
87  				.addServlet(servlet("DispatcherServlet", DispatcherServlet.class, servletFactory).addMapping("/").setAsyncSupported(true))
88  				.addServletContextAttribute(WebSocketDeploymentInfo.ATTRIBUTE_NAME, info);
89  		for (final Filter filter : filters) {
90  			String filterName = filter.getClass().getName();
91  			servletBuilder.addFilter(new FilterInfo(filterName, filter.getClass(), new FilterInstanceFactory(filter)).setAsyncSupported(true));
92  			for (DispatcherType type : DispatcherType.values()) {
93  				servletBuilder.addFilterUrlMapping(filterName, "/*", type);
94  			}
95  		}
96  		try {
97  			this.manager = defaultContainer().addDeployment(servletBuilder);
98  			this.manager.deploy();
99  			HttpHandler httpHandler = this.manager.start();
100 			this.server = Undertow.builder().addHttpListener(this.port, "localhost").setHandler(httpHandler).build();
101 		}
102 		catch (ServletException ex) {
103 			throw new IllegalStateException(ex);
104 		}
105 	}
106 
107 	@Override
108 	public void undeployConfig() {
109 		this.manager.undeploy();
110 	}
111 
112 	@Override
113 	public void start() throws Exception {
114 		this.server.start();
115 	}
116 
117 	@Override
118 	public void stop() throws Exception {
119 		this.server.stop();
120 	}
121 
122 
123 	private static class DispatcherServletInstanceFactory implements InstanceFactory<Servlet> {
124 
125 		private final WebApplicationContext wac;
126 
127 		public DispatcherServletInstanceFactory(WebApplicationContext wac) {
128 			this.wac = wac;
129 		}
130 
131 		@Override
132 		public InstanceHandle<Servlet> createInstance() throws InstantiationException {
133 			return new InstanceHandle<Servlet>() {
134 				@Override
135 				public Servlet getInstance() {
136 					return new DispatcherServlet(wac);
137 				}
138 				@Override
139 				public void release() {
140 				}
141 			};
142 		}
143 	}
144 
145 	private static class FilterInstanceFactory implements InstanceFactory<Filter> {
146 
147 		private final Filter filter;
148 
149 		private FilterInstanceFactory(Filter filter) {
150 			this.filter = filter;
151 		}
152 
153 		@Override
154 		public InstanceHandle<Filter> createInstance() throws InstantiationException {
155 			return new InstanceHandle<Filter>() {
156 				@Override
157 				public Filter getInstance() {
158 					return filter;
159 				}
160 				@Override
161 				public void release() {}
162 			};
163 		}
164 	}
165 
166 }