1
18
19 package io.undertow.servlet.core;
20
21 import java.nio.file.Path;
22 import java.nio.file.Paths;
23 import java.util.Date;
24 import java.util.List;
25 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
26
27 import javax.servlet.MultipartConfigElement;
28 import javax.servlet.Servlet;
29 import javax.servlet.ServletException;
30 import javax.servlet.SingleThreadModel;
31 import javax.servlet.UnavailableException;
32
33 import io.undertow.server.handlers.form.FormEncodedDataDefinition;
34 import io.undertow.server.handlers.form.FormParserFactory;
35 import io.undertow.server.handlers.form.MultiPartParserDefinition;
36 import io.undertow.server.handlers.resource.ResourceChangeListener;
37 import io.undertow.server.handlers.resource.ResourceManager;
38 import io.undertow.servlet.UndertowServletLogger;
39 import io.undertow.servlet.UndertowServletMessages;
40 import io.undertow.servlet.api.DeploymentInfo;
41 import io.undertow.servlet.api.DeploymentManager;
42 import io.undertow.servlet.api.InstanceFactory;
43 import io.undertow.servlet.api.InstanceHandle;
44 import io.undertow.servlet.api.LifecycleInterceptor;
45 import io.undertow.servlet.api.ServletInfo;
46 import io.undertow.servlet.spec.ServletConfigImpl;
47 import io.undertow.servlet.spec.ServletContextImpl;
48
49
54 public class ManagedServlet implements Lifecycle {
55
56 private final ServletInfo servletInfo;
57 private final ServletContextImpl servletContext;
58
59 private volatile boolean started = false;
60 private final InstanceStrategy instanceStrategy;
61 private volatile boolean permanentlyUnavailable = false;
62
63 private long maxRequestSize;
64 private FormParserFactory formParserFactory;
65 private MultipartConfigElement multipartConfig;
66
67 private static final AtomicLongFieldUpdater<ManagedServlet> unavailableUntilUpdater = AtomicLongFieldUpdater.newUpdater(ManagedServlet.class, "unavailableUntil");
68
69 @SuppressWarnings("unused")
70 private volatile long unavailableUntil = 0;
71
72 public ManagedServlet(final ServletInfo servletInfo, final ServletContextImpl servletContext) {
73 this.servletInfo = servletInfo;
74 this.servletContext = servletContext;
75 if (SingleThreadModel.class.isAssignableFrom(servletInfo.getServletClass())) {
76 instanceStrategy = new SingleThreadModelPoolStrategy(servletInfo.getInstanceFactory(), servletInfo, servletContext);
77 } else {
78 instanceStrategy = new DefaultInstanceStrategy(servletInfo.getInstanceFactory(), servletInfo, servletContext);
79 }
80 setupMultipart(servletContext);
81 }
82
83 public void setupMultipart(ServletContextImpl servletContext) {
84 FormEncodedDataDefinition formDataParser = new FormEncodedDataDefinition()
85 .setDefaultEncoding(servletContext.getDeployment().getDefaultRequestCharset().name());
86 MultipartConfigElement multipartConfig = servletInfo.getMultipartConfig();
87 if(multipartConfig == null) {
88 multipartConfig = servletContext.getDeployment().getDeploymentInfo().getDefaultMultipartConfig();
89 }
90 this.multipartConfig = multipartConfig;
91 if (multipartConfig != null) {
92
93 MultipartConfigElement config = multipartConfig;
94 if (config.getMaxRequestSize() != -1) {
95 maxRequestSize = config.getMaxRequestSize();
96 } else {
97 maxRequestSize = -1;
98 }
99 final Path tempDir;
100 if(config.getLocation() == null || config.getLocation().isEmpty()) {
101 tempDir = servletContext.getDeployment().getDeploymentInfo().getTempPath();
102 } else {
103 String location = config.getLocation();
104 Path locFile = Paths.get(location);
105 if(locFile.isAbsolute()) {
106 tempDir = locFile;
107 } else {
108 final DeploymentInfo deploymentInfo = servletContext.getDeployment().getDeploymentInfo();
109 tempDir = deploymentInfo.requireTempPath().resolve(location);
110 }
111 }
112
113 MultiPartParserDefinition multiPartParserDefinition = new MultiPartParserDefinition(tempDir);
114 if(config.getMaxFileSize() > 0) {
115 multiPartParserDefinition.setMaxIndividualFileSize(config.getMaxFileSize());
116 }
117 if (config.getFileSizeThreshold() > 0) {
118 multiPartParserDefinition.setFileSizeThreshold(config.getFileSizeThreshold());
119 }
120 multiPartParserDefinition.setDefaultEncoding(servletContext.getDeployment().getDefaultRequestCharset().name());
121
122 formParserFactory = FormParserFactory.builder(false)
123 .addParser(formDataParser)
124 .addParser(multiPartParserDefinition)
125 .build();
126
127 } else {
128
129 formParserFactory = FormParserFactory.builder(false).addParser(formDataParser).build();
130 maxRequestSize = -1;
131 }
132 }
133
134
135 public synchronized void start() throws ServletException {
136
137 }
138
139 public void createServlet() throws ServletException {
140 if (permanentlyUnavailable) {
141 return;
142 }
143 try {
144 if (!started && servletInfo.getLoadOnStartup() != null && servletInfo.getLoadOnStartup() >= 0) {
145 instanceStrategy.start();
146 started = true;
147 }
148 } catch (UnavailableException e) {
149 if (e.isPermanent()) {
150 permanentlyUnavailable = true;
151 stop();
152 }
153 }
154 }
155
156 public synchronized void stop() {
157 if (started) {
158 instanceStrategy.stop();
159 }
160 started = false;
161 }
162
163 @Override
164 public boolean isStarted() {
165 return started;
166 }
167
168 public boolean isPermanentlyUnavailable() {
169 return permanentlyUnavailable;
170 }
171
172 public boolean isTemporarilyUnavailable() {
173 long until = unavailableUntil;
174 if (until != 0) {
175 if (System.currentTimeMillis() < until) {
176 return true;
177 } else {
178 unavailableUntilUpdater.compareAndSet(this, until, 0);
179 }
180 }
181 return false;
182 }
183
184 public void setPermanentlyUnavailable(final boolean permanentlyUnavailable) {
185 this.permanentlyUnavailable = permanentlyUnavailable;
186 }
187
188 public InstanceHandle<? extends Servlet> getServlet() throws ServletException {
189 if(servletContext.getDeployment().getDeploymentState() != DeploymentManager.State.STARTED) {
190 throw UndertowServletMessages.MESSAGES.deploymentStopped(servletContext.getDeployment().getDeploymentInfo().getDeploymentName());
191 }
192 if (!started) {
193 synchronized (this) {
194 if (!started) {
195 instanceStrategy.start();
196 started = true;
197 }
198 }
199 }
200 return instanceStrategy.getServlet();
201 }
202
203
204 public void forceInit() throws ServletException {
205 if (!started) {
206 if(servletContext.getDeployment().getDeploymentState() != DeploymentManager.State.STARTED) {
207 throw UndertowServletMessages.MESSAGES.deploymentStopped(servletContext.getDeployment().getDeploymentInfo().getDeploymentName());
208 }
209 synchronized (this) {
210 if (!started) {
211 try {
212 instanceStrategy.start();
213 } catch (UnavailableException e) {
214 handleUnavailableException(e);
215 }
216 started = true;
217 }
218 }
219 }
220 }
221
222 public void handleUnavailableException(UnavailableException e) {
223 if (e.isPermanent()) {
224 UndertowServletLogger.REQUEST_LOGGER.stoppingServletDueToPermanentUnavailability(getServletInfo().getName(), e);
225 stop();
226 setPermanentlyUnavailable(true);
227 } else {
228 long until = System.currentTimeMillis() + e.getUnavailableSeconds() * 1000;
229 unavailableUntilUpdater.set(this, until);
230 UndertowServletLogger.REQUEST_LOGGER.stoppingServletUntilDueToTemporaryUnavailability(getServletInfo().getName(), new Date(until), e);
231 }
232 }
233
234 public ServletInfo getServletInfo() {
235 return servletInfo;
236 }
237
238 public long getMaxRequestSize() {
239 return maxRequestSize;
240 }
241
242 public FormParserFactory getFormParserFactory() {
243 return formParserFactory;
244 }
245
246 public MultipartConfigElement getMultipartConfig() {
247 return multipartConfig;
248 }
249
250 @Override
251 public String toString() {
252 return "ManagedServlet{" +
253 "servletInfo=" + servletInfo +
254 '}';
255 }
256
257
260 interface InstanceStrategy {
261 void start() throws ServletException;
262
263 void stop();
264
265 InstanceHandle<? extends Servlet> getServlet() throws ServletException;
266 }
267
268
269
272 private static class DefaultInstanceStrategy implements InstanceStrategy {
273
274 private final InstanceFactory<? extends Servlet> factory;
275 private final ServletInfo servletInfo;
276 private final ServletContextImpl servletContext;
277 private volatile InstanceHandle<? extends Servlet> handle;
278 private volatile Servlet instance;
279 private ResourceChangeListener changeListener;
280 private final InstanceHandle<Servlet> instanceHandle = new InstanceHandle<Servlet>() {
281 @Override
282 public Servlet getInstance() {
283 return instance;
284 }
285
286 @Override
287 public void release() {
288
289 }
290 };
291
292 DefaultInstanceStrategy(final InstanceFactory<? extends Servlet> factory, final ServletInfo servletInfo, final ServletContextImpl servletContext) {
293 this.factory = factory;
294 this.servletInfo = servletInfo;
295 this.servletContext = servletContext;
296 }
297
298 public synchronized void start() throws ServletException {
299 try {
300 handle = factory.createInstance();
301 } catch (Exception e) {
302 throw UndertowServletMessages.MESSAGES.couldNotInstantiateComponent(servletInfo.getName(), e);
303 }
304 instance = handle.getInstance();
305 new LifecyleInterceptorInvocation(servletContext.getDeployment().getDeploymentInfo().getLifecycleInterceptors(), servletInfo, instance, new ServletConfigImpl(servletInfo, servletContext)).proceed();
306
307
308 final ResourceManager resourceManager = servletContext.getDeployment().getDeploymentInfo().getResourceManager();
309 if(instance instanceof ResourceChangeListener && resourceManager.isResourceChangeListenerSupported()) {
310 resourceManager.registerResourceChangeListener(changeListener = (ResourceChangeListener) instance);
311 }
312 }
313
314 public synchronized void stop() {
315 if (handle != null) {
316 final ResourceManager resourceManager = servletContext.getDeployment().getDeploymentInfo().getResourceManager();
317 if(changeListener != null) {
318 resourceManager.removeResourceChangeListener(changeListener);
319 }
320 invokeDestroy();
321 handle.release();
322 }
323 }
324
325 private void invokeDestroy() {
326 List<LifecycleInterceptor> interceptors = servletContext.getDeployment().getDeploymentInfo().getLifecycleInterceptors();
327 try {
328 new LifecyleInterceptorInvocation(interceptors, servletInfo, instance).proceed();
329 } catch (Exception e) {
330 UndertowServletLogger.ROOT_LOGGER.failedToDestroy(servletInfo, e);
331 }
332 }
333
334 public InstanceHandle<? extends Servlet> getServlet() {
335 return instanceHandle;
336 }
337 }
338
339
342 private static class SingleThreadModelPoolStrategy implements InstanceStrategy {
343
344
345 private final InstanceFactory<? extends Servlet> factory;
346 private final ServletInfo servletInfo;
347 private final ServletContextImpl servletContext;
348
349 private SingleThreadModelPoolStrategy(final InstanceFactory<? extends Servlet> factory, final ServletInfo servletInfo, final ServletContextImpl servletContext) {
350 this.factory = factory;
351 this.servletInfo = servletInfo;
352 this.servletContext = servletContext;
353 }
354
355 @Override
356 public void start() throws ServletException {
357 if(servletInfo.getLoadOnStartup() != null) {
358
359 getServlet().release();
360 }
361 }
362
363 @Override
364 public void stop() {
365
366 }
367
368 @Override
369 public InstanceHandle<? extends Servlet> getServlet() throws ServletException {
370 final InstanceHandle<? extends Servlet> instanceHandle;
371 final Servlet instance;
372
373 try {
374 instanceHandle = factory.createInstance();
375 } catch (Exception e) {
376 throw UndertowServletMessages.MESSAGES.couldNotInstantiateComponent(servletInfo.getName(), e);
377 }
378 instance = instanceHandle.getInstance();
379 new LifecyleInterceptorInvocation(servletContext.getDeployment().getDeploymentInfo().getLifecycleInterceptors(), servletInfo, instance, new ServletConfigImpl(servletInfo, servletContext)).proceed();
380
381 return new InstanceHandle<Servlet>() {
382 @Override
383 public Servlet getInstance() {
384 return instance;
385 }
386
387 @Override
388 public void release() {
389 try {
390 instance.destroy();
391 } catch (Throwable t) {
392 UndertowServletLogger.REQUEST_LOGGER.failedToDestroy(instance, t);
393 }
394 instanceHandle.release();
395 }
396 };
397
398 }
399 }
400
401
402 }
403