Improvements and refinements to web site
[zxing.git] / zxingorg / src / com / google / zxing / web / DecodeServlet.java
1 /*
2  * Copyright 2008 Google Inc.
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 com.google.zxing.web;
18
19 import com.google.zxing.DecodeHintType;
20 import com.google.zxing.MultiFormatReader;
21 import com.google.zxing.Reader;
22 import com.google.zxing.ReaderException;
23 import com.google.zxing.Result;
24 import com.google.zxing.client.j2se.BufferedImageMonochromeBitmapSource;
25 import com.google.zxing.client.result.ParsedReaderResult;
26 import org.apache.commons.fileupload.FileItem;
27 import org.apache.commons.fileupload.FileUploadException;
28 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
29 import org.apache.commons.fileupload.servlet.ServletFileUpload;
30 import org.apache.http.Header;
31 import org.apache.http.HttpException;
32 import org.apache.http.HttpMessage;
33 import org.apache.http.HttpResponse;
34 import org.apache.http.HttpVersion;
35 import org.apache.http.client.HttpClient;
36 import org.apache.http.client.methods.HttpGet;
37 import org.apache.http.conn.PlainSocketFactory;
38 import org.apache.http.conn.Scheme;
39 import org.apache.http.conn.SchemeRegistry;
40 import org.apache.http.conn.ssl.SSLSocketFactory;
41 import org.apache.http.impl.client.DefaultHttpClient;
42 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
43 import org.apache.http.params.BasicHttpParams;
44 import org.apache.http.params.HttpParams;
45 import org.apache.http.params.HttpProtocolParams;
46
47 import javax.imageio.ImageIO;
48 import javax.mail.Authenticator;
49 import javax.servlet.ServletConfig;
50 import javax.servlet.ServletException;
51 import javax.servlet.http.HttpServlet;
52 import javax.servlet.http.HttpServletRequest;
53 import javax.servlet.http.HttpServletResponse;
54 import java.awt.image.BufferedImage;
55 import java.io.IOException;
56 import java.io.InputStream;
57 import java.io.OutputStreamWriter;
58 import java.io.Writer;
59 import java.net.URI;
60 import java.net.URISyntaxException;
61 import java.util.Hashtable;
62 import java.util.List;
63 import java.util.Timer;
64 import java.util.logging.Logger;
65
66 /**
67  * {@link HttpServlet} which decodes images containing barcodes. Given a URL, it will
68  * retrieve the image and decode it. It can also process image files uploaded via POST.
69  * 
70  * @author Sean Owen (srowen@google.com)
71  */
72 public final class DecodeServlet extends HttpServlet {
73
74   private static final long MAX_IMAGE_SIZE = 500000L;
75   private static final long EMAIL_CHECK_INTERVAL = 60000L;
76
77   private static final Logger log = Logger.getLogger(DecodeServlet.class.getName());
78
79   static final Hashtable<DecodeHintType, Object> HINTS;
80
81   static {
82     HINTS = new Hashtable<DecodeHintType, Object>(3);
83     HINTS.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
84   }
85
86   private HttpClient client;
87   private DiskFileItemFactory diskFileItemFactory;
88   private Timer emailTimer;
89
90   @Override
91   public void init(ServletConfig servletConfig) throws ServletException {
92
93     Logger logger = Logger.getLogger("com.google.zxing");
94     logger.addHandler(new ServletContextLogHandler(servletConfig.getServletContext()));
95
96     String emailAddress = servletConfig.getInitParameter("emailAddress");
97     String emailPassword = servletConfig.getInitParameter("emailPassword");
98     if (emailAddress == null || emailPassword == null) {
99       throw new ServletException("emailAddress or emailPassword not specified");
100     }
101
102     HttpParams params = new BasicHttpParams();
103     HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
104
105     SchemeRegistry registry = new SchemeRegistry();
106     registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
107     registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
108
109     client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, registry), params);
110
111     diskFileItemFactory = new DiskFileItemFactory();
112
113     Authenticator emailAuthenticator = new EmailAuthenticator(emailAddress, emailPassword);
114     emailTimer = new Timer("Email decoder timer", true);
115     emailTimer.schedule(new DecodeEmailTask(emailAddress, emailAuthenticator), 0L, EMAIL_CHECK_INTERVAL);
116
117     log.info("DecodeServlet configured");
118   }
119
120   @Override
121   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
122
123     String imageURIString = request.getParameter("u");
124     if (imageURIString == null || imageURIString.length() == 0) {
125       response.sendRedirect("badurl.jspx");
126       return;
127     }
128
129     if (!(imageURIString.startsWith("http://") || imageURIString.startsWith("https://"))) {
130       imageURIString = "http://" + imageURIString;
131     }
132
133     URI imageURI;
134     try {
135       imageURI = new URI(imageURIString);
136     } catch (URISyntaxException urise) {
137       response.sendRedirect("badurl.jspx");
138       return;
139     }
140
141     HttpGet getRequest = new HttpGet(imageURI);
142
143     try {
144       HttpResponse getResponse = client.execute(getRequest);
145       if (getResponse.getStatusLine().getStatusCode() != HttpServletResponse.SC_OK) {
146         response.sendRedirect("badurl.jspx");
147         return;
148       }
149       if (!isSizeOK(getResponse)) {
150         response.sendRedirect("badimage.jspx");
151         return;
152       }
153       log.info("Decoding " + imageURI);
154       InputStream is = getResponse.getEntity().getContent();
155       try {
156         processStream(is, request, response);
157       } finally {
158         is.close();
159       }
160     } catch (InterruptedException ie) {
161       getRequest.abort();
162       response.sendRedirect("badurl.jspx");
163     } catch (HttpException he) {
164       getRequest.abort();
165       response.sendRedirect("badurl.jspx");
166     }
167
168   }
169
170   @Override
171   protected void doPost(HttpServletRequest request, HttpServletResponse response)
172           throws ServletException, IOException {
173
174     if (!ServletFileUpload.isMultipartContent(request)) {
175       response.sendRedirect("badimage.jspx");
176       return;
177     }
178
179     ServletFileUpload upload = new ServletFileUpload(diskFileItemFactory);
180     upload.setFileSizeMax(MAX_IMAGE_SIZE);
181
182     // Parse the request
183     try {
184       for (FileItem item : (List<FileItem>) upload.parseRequest(request)) {
185         if (!item.isFormField()) {
186           if (item.getSize() <= MAX_IMAGE_SIZE) {
187             log.info("Decoding uploaded file");
188             InputStream is = item.getInputStream();
189             try {
190               processStream(is, request, response);
191             } finally {
192               is.close();
193             }
194           } else {
195             response.sendRedirect("badimage.jspx");
196           }
197           break;
198         }
199       }
200     } catch (FileUploadException fue) {
201       response.sendRedirect("badimage.jspx");
202     }
203
204   }
205
206   private static void processStream(InputStream is, HttpServletRequest request, HttpServletResponse response)
207       throws ServletException, IOException {
208     BufferedImage image = ImageIO.read(is);
209     if (image == null) {
210       response.sendRedirect("badimage.jspx");
211       return;
212     }
213
214     Reader reader = new MultiFormatReader();
215     Result result;
216     try {
217       result = reader.decode(new BufferedImageMonochromeBitmapSource(image), HINTS);
218     } catch (ReaderException re) {
219       log.info("DECODE FAILED: " + re.toString());
220       response.sendRedirect("notfound.jspx");
221       return;
222     }
223
224     if (request.getParameter("full") == null) {
225       response.setContentType("text/plain");
226       response.setCharacterEncoding("UTF-8");
227       Writer out = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
228       try {
229         out.write(result.getText());
230       } finally {
231         out.close();
232       }
233     } else {
234       request.setAttribute("result", result);
235       ParsedReaderResult parsedReaderResult = ParsedReaderResult.parseReaderResult(result);
236       request.setAttribute("parsedReaderResult", parsedReaderResult);
237       request.getRequestDispatcher("decoderesult.jspx").forward(request, response);
238     }
239   }
240
241   private static boolean isSizeOK(HttpMessage getResponse) {
242     Header lengthHeader = getResponse.getLastHeader("Content-Length");
243     if (lengthHeader != null) {
244       long length = Long.parseLong(lengthHeader.getValue());
245       if (length > MAX_IMAGE_SIZE) {
246         return false;
247       }
248     }
249     return true;
250   }
251
252   @Override
253   public void destroy() {
254     log.config("DecodeServlet shutting down...");
255     emailTimer.cancel();
256   }
257
258 }