1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.imaging;
35
36 import info.magnolia.context.MgnlContext;
37 import info.magnolia.imaging.util.PathSplitter;
38 import info.magnolia.init.MagnoliaConfigurationProperties;
39 import info.magnolia.objectfactory.Components;
40
41 import java.io.IOException;
42 import java.io.OutputStream;
43 import java.util.concurrent.ExecutorService;
44 import java.util.concurrent.Executors;
45 import java.util.concurrent.TimeUnit;
46
47 import javax.inject.Inject;
48 import javax.inject.Provider;
49 import javax.servlet.AsyncContext;
50 import javax.servlet.ServletException;
51 import javax.servlet.ServletResponse;
52 import javax.servlet.http.HttpServlet;
53 import javax.servlet.http.HttpServletRequest;
54 import javax.servlet.http.HttpServletResponse;
55
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 import com.google.common.net.MediaType;
60
61
62
63
64 public class ImagingServlet extends HttpServlet {
65 private static final Logger log = LoggerFactory.getLogger(ImagingServlet.class);
66 private final Imaging imaging;
67 private final Provider<ImagingModule> imagingModuleProvider;
68 private ExecutorService workers;
69
70
71 @Inject
72 public ImagingServlet(Imaging imaging, Provider<ImagingModule> imagingModuleProvider, MagnoliaConfigurationProperties mcp) {
73 this.imaging = imaging;
74 this.imagingModuleProvider = imagingModuleProvider;
75 int workersCount = 4;
76 if (mcp.hasProperty("imaging.workers.count")) {
77 try {
78 workersCount = Integer.parseInt(mcp.getProperty("imaging.workers.count"));
79 } catch (NumberFormatException e) {
80 log.error("Failed to parse the number of imaging workers, falling back to 4");
81 }
82 }
83 this.workers = Executors.newFixedThreadPool(workersCount);
84 }
85
86
87
88
89 @Deprecated
90 public ImagingServlet(Imaging imaging, Provider<ImagingModule> imagingModuleProvider) {
91 this(imaging, imagingModuleProvider, Components.getComponent(MagnoliaConfigurationProperties.class));
92 }
93
94
95
96
97 @Deprecated
98 public ImagingServlet(final ImagingModule imagingModule) {
99 this.imaging = Components.getComponent(Imaging.class);
100 this.imagingModuleProvider = new Provider<ImagingModule>() {
101 @Override
102 public ImagingModule get() {
103 return Components.getComponent(ImagingModule.class);
104 }
105 };
106 }
107
108
109 @Override
110 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
111 AsyncContext asyncContext = request.startAsync();
112 log.debug("Requesting {}", request.getRequestURI());
113 response.setStatus(HttpServletResponse.SC_ACCEPTED);
114
115 workers.submit(() -> {
116 MgnlContext.doInSystemContext(new MgnlContext.VoidOp() {
117
118 @Override
119 public void doExec() {
120 try {
121 String generatorName = getImageGeneratorName(request);
122 ServletImageResponse imageResponse = new ServletImageResponse(asyncContext.getResponse());
123 imaging.generate(generatorName, request, imageResponse);
124 log.debug("Generated image for {}", request.getRequestURI());
125 asyncContext.getResponse().flushBuffer();
126 } catch (IllegalArgumentException | ImagingRuntimeException | ImagingException e) {
127 log.warn("Because of incorrect arguments the image couldn't be found", e);
128 sendError(asyncContext, HttpServletResponse.SC_NOT_FOUND, "");
129 } catch (IOException e) {
130
131
132 log.debug("Unable to spool resource due to a {} exception", e.getClass().getName());
133 if (!asyncContext.getResponse().isCommitted()) {
134 sendError(asyncContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "");
135 }
136 } finally {
137 asyncContext.complete();
138 }
139 }
140 });
141 });
142 }
143
144 @Override
145 public void destroy() {
146 workers.shutdown();
147 boolean terminated = false;
148 try {
149 terminated = workers.awaitTermination(1, TimeUnit.SECONDS);
150 } catch (InterruptedException e) {
151 log.error("Exception during monitor termination", e);
152 }
153
154 if (terminated) {
155 log.info("{} terminated.", getClass().getSimpleName());
156 } else {
157 log.warn("{} not terminated, some tasks are still running.", getClass().getSimpleName());
158 }
159 }
160
161 private void sendError(AsyncContext asyncContext, int statusCode, String message) {
162 if(asyncContext.getResponse() instanceof HttpServletResponse)
163 try {
164 ((HttpServletResponse) asyncContext.getResponse()).sendError(statusCode, message);
165 } catch (IOException e) {
166 log.error("Failed to send [{}] error from async context", statusCode, e);
167 }
168 }
169
170
171
172
173
174 protected String getImageGeneratorName(HttpServletRequest request) {
175 final String pathInfo = request.getPathInfo();
176 return new PathSplitter(pathInfo).skipTo(0);
177 }
178
179 protected ImageGenerator getGenerator(String generatorName) {
180 final ImagingModule module = getImagingModule();
181 return module.getGenerators().get(generatorName);
182 }
183
184 protected ImagingModule getImagingModule() {
185 return imagingModuleProvider.get();
186 }
187
188 private static class ServletImageResponse implements ImageResponse {
189 private final ServletResponse response;
190
191 public ServletImageResponse(ServletResponse response) {
192 this.response = response;
193 }
194
195 @Override
196 public void setMediaType(MediaType mediaType) throws IOException {
197 response.setContentType(mediaType.toString());
198 }
199
200 @Override
201 public OutputStream getOutputStream() throws IOException {
202 return response.getOutputStream();
203 }
204 }
205 }