1 package org.jmage.mapper;
2
3 import org.apache.log4j.Logger;
4 import org.apache.log4j.Priority;
5 import org.jmage.ApplicationContext;
6 import org.jmage.ImageRequest;
7 import org.jmage.util.ExceptionUtil;
8
9 import javax.servlet.ServletException;
10 import javax.servlet.http.HttpServlet;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13 import java.io.IOException;
14 import java.net.URI;
15 import java.net.URISyntaxException;
16 import java.util.Enumeration;
17 import java.util.Properties;
18
19 /***
20 * Maps ImageRequests based on queryString params.
21 */
22 public class ServletMapper extends HttpServlet {
23 public static final String CHAINPARAM_DELIMITER = "^.*__.*$";
24 public static final String CHAIN_DELIMITER = "^.*--.*$";
25
26 protected static Logger log = Logger.getLogger(ServletMapper.class.getName());
27 protected static ApplicationContext context;
28
29 private static final String CHAIN_REGEX = "[Cc][Hh][Aa][Ii][Nn]";
30 private static final String IMAGE_REGEX = "[Ii][Mm][Aa][Gg][Ee]";
31 private static final String IMAGE_URI_REGEX = "[Ii][Mm][Aa][Gg][Ee]_[Uu][Rr][Ii]";
32 private static final String SRC_REGEX = "[Ss][Rr][Cc]";
33 private static final String URI_REGEX = "^.*[Uu][Rr][Ii]$";
34 private static final String CHAINURISCHEME = "chain:";
35 private static final String ENCODE = "encode";
36 private static final String SERVLET_CONTEXT = "SERVLET_CONTEXT";
37 private static final String SLASH = "/";
38 private static final String DOT = ".";
39 private static final String COLON = ":";
40
41 private static final String CONTENT_DISPOSITION = "Content-disposition";
42 private static final String FILENAME = "filename=image_";
43 private static final String MAP_ERROR = "unable to map image request, cause: ";
44 private static final String SOCKET_RESET_ERROR = "discarding request, connection reset by peer, cause: ";
45 private static final String FILE = "file";
46
47 protected static final String TOMCAT_CLIENTABORT = "ClientAbortException";
48
49 protected ThreadLocalServletImageRequestMap requestMap;
50 private static final String EMPTY_STRING = "";
51
52 protected String chainParamDelimiter;
53 protected String chainDelimiter;
54
55 public ServletMapper() {
56 super();
57 requestMap = new ThreadLocalServletImageRequestMap();
58 }
59
60 public void init() throws ServletException {
61 if (context == null) {
62 context = ApplicationContext.getContext();
63 }
64 if (context.get(SERVLET_CONTEXT) == null) {
65 context.put(SERVLET_CONTEXT, this.getServletContext());
66 }
67 chainParamDelimiter = context.getProperty("chainParamDelimiter", CHAINPARAM_DELIMITER);
68 chainDelimiter = context.getProperty("chainDelimiter", CHAIN_DELIMITER);
69 }
70
71 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
72 try {
73
74
75 synchronized (this) {
76 requestMap.setRequest(request);
77 requestMap.setResponse(response);
78
79 ImageRequestMapper imageRequestMapper = this.getImageRequestMapper();
80 ImageRequest imageRequest = this.populateImageRequestFrom(request);
81 imageRequestMapper.setImageRequest(imageRequest);
82 requestMap.setMapper(imageRequestMapper);
83 }
84
85 requestMap.getResponse().setContentType(this.getServletContext().getMimeType(DOT +
86 requestMap.getThreadLocalImageRequest().getEncodingFormat()));
87 requestMap.getResponse().setHeader(CONTENT_DISPOSITION, FILENAME +
88 requestMap.getThreadLocalImageRequest().hashCode() +
89 DOT + requestMap.getThreadLocalImageRequest().getEncodingFormat());
90 requestMap.getResponse().getOutputStream().write(requestMap.getMapper().processRequest());
91 requestMap.getResponse().getOutputStream().flush();
92 } catch (Exception e) {
93 String message = MAP_ERROR + e.getMessage();
94
95 if (new ExceptionUtil().hasMessage(e, TOMCAT_CLIENTABORT)) {
96 if (log.isEnabledFor(Priority.DEBUG)) log.debug(SOCKET_RESET_ERROR + message);
97 } else {
98 if (log.isEnabledFor(Priority.ERROR)) log.error(message);
99 }
100 } finally {
101
102 synchronized (this) {
103 requestMap.removeRequest();
104 requestMap.removeResponse();
105 requestMap.removeMapper();
106 }
107 }
108 }
109
110 public ImageRequestMapper getImageRequestMapper() {
111 return new ImageRequestMapper();
112 }
113
114 protected ImageRequest populateImageRequestFrom(HttpServletRequest request) throws URISyntaxException {
115 ImageRequest imageRequest = new ImageRequest();
116 Properties filterChainProperties = new Properties();
117
118 Enumeration paramNames = request.getParameterNames();
119 while (paramNames.hasMoreElements()) {
120 String key = ((String) paramNames.nextElement());
121 String value = (String) request.getParameter(key);
122
123
124 if(key.matches(URI_REGEX) || key.matches(IMAGE_REGEX) || key.matches(IMAGE_URI_REGEX) || key.matches(SRC_REGEX)) {
125 String scheme = URI.create(value).getScheme();
126 if (scheme==null || scheme.length()==0) {
127 value = this.completeUri(value).toString();
128 }
129 }
130
131
132 if (key.matches(CHAIN_REGEX)) {
133 this.fillChainParam(key, value, imageRequest);
134 continue;
135 }
136
137
138 if (key.matches(IMAGE_REGEX) || key.matches(IMAGE_URI_REGEX) || key.matches(SRC_REGEX)) {
139 this.fillImageParam(key, value, imageRequest);
140 continue;
141 }
142
143
144 if (key.matches(chainParamDelimiter)) {
145 this.fillFilterChainProperties(key, value, filterChainProperties);
146 } else {
147 filterChainProperties.setProperty(key.toUpperCase(), value);
148 }
149 }
150
151
152 String encode = request.getParameter(ENCODE);
153 if (encode != null) {
154 imageRequest.setEncodingFormat(encode);
155 }
156
157 imageRequest.setFilterChainProperties(filterChainProperties);
158 return imageRequest;
159 }
160
161 /***
162 * Extract per filterchain only properties. put them into their own properties and store them in global hashmap with
163 * filterchain name as lookup key.
164 *
165 * @param key the per filterchain property key
166 * @param value the per filterchain property value
167 * @param filterChainProperties per image request properties
168 */
169 protected void fillFilterChainProperties(String key, String value, Properties filterChainProperties) {
170 String[] perFilterChainProperty = key.split("__");
171 Object perFilterChainProps = filterChainProperties.get(perFilterChainProperty[0] != null ? perFilterChainProperty[0] : EMPTY_STRING);
172 if (perFilterChainProps == null) {
173 perFilterChainProps = new Properties();
174 }
175 ((Properties) perFilterChainProps).setProperty(
176 perFilterChainProperty[1] != null ? perFilterChainProperty[1].toUpperCase() : EMPTY_STRING, value);
177 filterChainProperties.put(perFilterChainProperty[0] != null ? perFilterChainProperty[0] : EMPTY_STRING, perFilterChainProps);
178 }
179
180 /***
181 * Set the image param on the ImageRequest
182 *
183 * @param key
184 * @param imageRequest
185 * @throws URISyntaxException
186 */
187 protected void fillImageParam(String key, String image, ImageRequest imageRequest) throws URISyntaxException {
188 imageRequest.setImageURI(new URI(image));
189 imageRequest.setEncodingFormat(determineContentType(image));
190 }
191
192 /***
193 * Set the chain param on the ImageRequest
194 *
195 * @param key
196 * @param value
197 * @param imageRequest
198 * @throws URISyntaxException
199 */
200 protected void fillChainParam(String key, String value, ImageRequest imageRequest) throws URISyntaxException {
201 String[] chains = value.split("--");
202 URI[] filterChainURIs = new URI[chains.length];
203 for (int i = 0; i < chains.length; i++) {
204 String chain = chains[i];
205 if (!chain.startsWith(CHAINURISCHEME)) {
206 chain = CHAINURISCHEME + chain;
207 }
208 filterChainURIs[i] = new URI(chain);
209 }
210 imageRequest.setFilterChainURI(filterChainURIs);
211 }
212
213 /***
214 * Fix partial URIs
215 *
216 * @param resource
217 * @return
218 * @throws URISyntaxException
219 */
220 protected URI completeUri(String resource) throws URISyntaxException {
221 URI uri;
222 StringBuffer buffer = new StringBuffer();
223 buffer.append(FILE);
224 buffer.append(COLON);
225 buffer.append(SLASH);
226 buffer.append(SLASH);
227 buffer.append(resource.startsWith(SLASH) ? resource : SLASH + resource);
228 uri = new URI(buffer.toString());
229 return uri;
230 }
231
232 /***
233 * What content type are we looking for?
234 *
235 * @param imagePath
236 * @return file extension
237 */
238 protected String determineContentType(String imagePath) {
239 return imagePath.substring(imagePath.lastIndexOf(DOT) + 1).toLowerCase();
240 }
241 }