View Javadoc

1   /*
2    * Copyright (C) 2007 ETH Zurich
3    *
4    * This file is part of Fosstrak (www.fosstrak.org).
5    *
6    * Fosstrak is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License version 2.1, as published by the Free Software Foundation.
9    *
10   * Fosstrak is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with Fosstrak; if not, write to the Free
17   * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18   * Boston, MA  02110-1301  USA
19   */
20  
21  package org.fosstrak.epcis.repository.capture;
22  
23  import java.io.FileInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.PrintWriter;
27  import java.sql.SQLException;
28  import java.util.Properties;
29  
30  import javax.servlet.RequestDispatcher;
31  import javax.servlet.ServletConfig;
32  import javax.servlet.ServletContext;
33  import javax.servlet.ServletException;
34  import javax.servlet.http.HttpServlet;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.fosstrak.epcis.repository.InvalidFormatException;
41  import org.hibernate.HibernateException;
42  import org.hibernate.SessionFactory;
43  import org.hibernate.cfg.Configuration;
44  import org.xml.sax.SAXException;
45  
46  /**
47   * This CaptureOperationsServlet accepts and analyzes HTTP POST requests and
48   * delegates them to the appropriate handler methods in the
49   * CaptureOperationsModule. This servlet also initializes the
50   * CaptureOperationsModule properly and returns a simple information page upon
51   * GET requests.
52   * 
53   * @author Marco Steybe
54   */
55  public class CaptureOperationsServlet extends HttpServlet {
56  
57      private static final long serialVersionUID = -5765052834995535731L;
58  
59      private static final String APP_CONFIG_LOCATION = "appConfigLocation";
60      private static final String PROP_INSERT_MISSING_VOC = "insertMissingVoc";
61      private static final String PROP_DB_RESET_ALLOWED = "dbResetAllowed";
62      private static final String PROP_DB_RESET_SCRIPT = "dbResetScript";
63      private static final String PROP_EPCIS_SCHEMA_FILE = "epcisSchemaFile";
64      private static final String PROP_EPCIS_MASTER_DATA_SCHEMA_FILE = "epcisMasterDataSchemaFile";
65  
66      private static final String PAGE_CAPTURE_INTERFACE = "/WEB-INF/jsp/capture.jsp";
67      private static final String PAGE_CAPTURE_FORM = "/WEB-INF/jsp/captureForm.jsp";
68  
69      private static final Log LOG = LogFactory.getLog(CaptureOperationsServlet.class);
70  
71      private CaptureOperationsModule captureOperationsModule;
72  
73      /**
74       * {@inheritDoc}
75       */
76      public void init() {
77          LOG.debug("Fetching capture operations module from servlet context ...");
78          CaptureOperationsModule captureOperationsModule = (CaptureOperationsModule) getServletContext().getAttribute(
79                  "captureOperationsModule");
80          if (captureOperationsModule == null) {
81              LOG.debug("Capture operations module not found - initializing manually");
82              captureOperationsModule = new CaptureOperationsModule();
83  
84              ServletConfig servletConfig = getServletConfig();
85              Properties props;
86              if (servletConfig == null) {
87                  props = loadApplicationProperties();
88              } else {
89                  props = loadApplicationProperties(servletConfig);
90              }
91              SessionFactory hibernateSessionFactory = initHibernate();
92              captureOperationsModule.setSessionFactory(hibernateSessionFactory);
93              captureOperationsModule.setInsertMissingVoc(Boolean.parseBoolean(props.getProperty(PROP_INSERT_MISSING_VOC,
94                      "true")));
95              captureOperationsModule.setDbResetAllowed(Boolean.parseBoolean(props.getProperty(PROP_DB_RESET_ALLOWED,
96                      "false")));
97              captureOperationsModule.setDbResetScript(props.getProperty(PROP_DB_RESET_SCRIPT));
98              captureOperationsModule.setEpcisSchemaFile(props.getProperty(PROP_EPCIS_SCHEMA_FILE));
99              captureOperationsModule.setEpcisMasterdataSchemaFile(props.getProperty(PROP_EPCIS_MASTER_DATA_SCHEMA_FILE));        
100         } else {
101             LOG.debug("Capture operations module found");
102         }
103         setCaptureOperationsModule(captureOperationsModule);
104     }
105 
106     /**
107      * Loads the application properties and populates a java.util.Properties
108      * instance.
109      * 
110      * @param servletConfig
111      *            The ServletConfig used to locate the application property
112      *            file.
113      * @return The application properties.
114      */
115     private Properties loadApplicationProperties(ServletConfig servletConfig) {
116         // read application properties from servlet context
117         ServletContext ctx = servletConfig.getServletContext();
118         String path = ctx.getRealPath("/");
119         String appConfigFile = ctx.getInitParameter(APP_CONFIG_LOCATION);
120         Properties properties = new Properties();
121         try {
122             InputStream is = new FileInputStream(path + appConfigFile);
123             properties.load(is);
124             is.close();
125             LOG.info("Loaded application properties from " + path + appConfigFile);
126         } catch (IOException e) {
127             LOG.error("Unable to load application properties from " + path + appConfigFile, e);
128         }
129         return properties;
130     }
131 
132     /**
133      * Loads the application properties from classpath and populates a
134      * java.util.Properties instance.
135      * 
136      * @param servletConfig
137      *            The ServletConfig used to locate the application property
138      *            file.
139      * @return The application properties.
140      */
141     private Properties loadApplicationProperties() {
142         // read application properties from classpath
143         String resource = "/application.properties";
144         InputStream is = this.getClass().getResourceAsStream(resource);
145         Properties properties = new Properties();
146         try {
147             properties.load(is);
148             is.close();
149             LOG.info("Loaded application properties from classpath:" + resource + " ("
150                     + this.getClass().getResource(resource) + ")");
151         } catch (IOException e) {
152             LOG.error("Unable to load application properties from classpath:" + resource + " ("
153                     + this.getClass().getResource(resource) + ")", e);
154         }
155         return properties;
156     }
157 
158     /**
159      * Initializes Hibernate. Reads the configuration from hibernate.cfg.xml
160      * located on the classpath (WEB-INF/classes/)
161      * 
162      * @return The Hibernate SessionFactory.
163      * @throws ServletException
164      */
165     private SessionFactory initHibernate() throws HibernateException {
166         LOG.info("Manually initializing Hibernate");
167         Configuration c = new Configuration();
168         c.configure(); // from WEB-INF/classes/hibernate.cfg.xml
169         return c.buildSessionFactory();
170     }
171 
172     /**
173      * Handles HTTP GET requests by dispatching to simple information pages.
174      * 
175      * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
176      *      javax.servlet.http.HttpServletResponse)
177      * @param req
178      *            The servlet request.
179      * @param rsp
180      *            The servlet response.
181      * @throws IOException
182      *             If an error occurred while writing the response.
183      */
184     public void doGet(final HttpServletRequest req, final HttpServletResponse rsp) throws ServletException, IOException {
185         RequestDispatcher dispatcher;
186         String dbReset = req.getParameter("dbReset");
187         if (dbReset != null && dbReset.equalsIgnoreCase("true")) {
188             doDbReset(rsp);
189         } else {
190 	        String showCaptureForm = req.getParameter("showCaptureForm");
191 	        if (showCaptureForm != null && "true".equals(showCaptureForm)) {
192 	            req.setAttribute("responseMsg", "");
193 	            req.setAttribute("detailedMsg", "");
194 	            dispatcher = getServletContext().getRequestDispatcher(PAGE_CAPTURE_FORM);
195 	        } else {
196 	            dispatcher = getServletContext().getRequestDispatcher(PAGE_CAPTURE_INTERFACE);
197 	        }
198 	        dispatcher.forward(req, rsp);
199         }
200     }
201 
202     /**
203      * Implements the EPCIS capture operation. Takes HTTP POST request, extracts
204      * the payload into an XML document, validates the document against the
205      * EPCIS schema, and captures the EPCIS events given in the document. Errors
206      * are caught and returned as simple plaintext messages via HTTP.
207      * 
208      * @param req
209      *            The HttpServletRequest.
210      * @param rsp
211      *            The HttpServletResponse.
212      * @throws IOException
213      *             If an error occurred while validating the request or writing
214      *             the response.
215      */
216     public void doPost(final HttpServletRequest req, final HttpServletResponse rsp) throws ServletException, IOException {
217         LOG.info("EPCIS Capture Interface invoked.");
218         InputStream is = null;
219         
220         // check if we have a POST request with form parameters
221         if ("application/x-www-form-urlencoded".equalsIgnoreCase(req.getContentType())) {
222             rsp.setContentType("text/plain");
223             PrintWriter out = rsp.getWriter();
224             // check if the 'event' or 'dbReset' form parameter are given
225             String event = req.getParameter("event");
226             String dbReset = req.getParameter("dbReset");
227             if (event != null) {
228                 LOG.info("Found deprecated 'event=' parameter. Refusing to process request.");
229                 String msg = "Starting from version 0.2.2, the EPCIS repository does not accept the EPCISDocument in the HTTP POST form parameter 'event' anymore. Please provide the EPCISDocument as HTTP POST payload instead.";
230                 rsp.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
231                 out.println(msg);
232             } else if (dbReset != null && dbReset.equalsIgnoreCase("true")) {
233                 doDbReset(rsp);
234             }
235             out.flush();
236             out.close();
237             return;
238         } else {
239             is = req.getInputStream();
240         }
241         
242         // do the capture operation and handle exceptions
243         String responseMsg = "";
244         String detailedMsg = "";
245         try {
246             captureOperationsModule.doCapture(is, req.getUserPrincipal());
247             rsp.setStatus(HttpServletResponse.SC_OK);
248             responseMsg = "EPCIS capture request succeeded.";
249         } catch (SAXException e) {
250             responseMsg = "An error processing the XML document occurred.";
251             detailedMsg = "Unable to parse incoming XML due to error: " + e.getMessage();
252             LOG.info(detailedMsg);
253             rsp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
254         } catch (InvalidFormatException e) {
255             responseMsg = "An error parsing the XML contents occurred.";
256             detailedMsg = "Unable to parse incoming EPCISDocument due to error: " + e.getMessage();
257             LOG.info(detailedMsg);
258             rsp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
259         } catch (final Exception e) {
260             responseMsg = "An unexpected error occurred.";
261             detailedMsg = "The repository is unable to handle the request due to an internal error.";
262             LOG.error(responseMsg, e);
263             rsp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
264         }
265 
266         // dispatch the response
267         req.setAttribute("responseMsg", responseMsg);
268         req.setAttribute("detailedMsg", detailedMsg);
269         RequestDispatcher dispatcher;
270         String showCaptureForm = (String) req.getAttribute("showCaptureForm");
271         if (showCaptureForm != null && "true".equals(showCaptureForm)) {
272             dispatcher = getServletContext().getRequestDispatcher(PAGE_CAPTURE_FORM);
273         } else {
274             dispatcher = getServletContext().getRequestDispatcher(PAGE_CAPTURE_INTERFACE);
275         }
276         dispatcher.forward(req, rsp);
277     }
278 
279     private void doDbReset(final HttpServletResponse rsp) throws IOException {
280         LOG.debug("Found 'dbReset' parameter set to 'true'.");
281         rsp.setContentType("text/plain");
282         final PrintWriter out = rsp.getWriter();
283         try {
284             captureOperationsModule.doDbReset();
285             String msg = "db reset successfull";
286             LOG.info(msg);
287             rsp.setStatus(HttpServletResponse.SC_OK);
288             out.println(msg);
289         } catch (SQLException e) {
290             String msg = "An error involving the database occurred";
291             LOG.error(msg, e);
292             rsp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
293             out.println(msg);
294         } catch (IOException e) {
295             String msg = "An unexpected error occurred";
296             LOG.error(msg, e);
297             rsp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
298             out.println(msg);
299         } catch (UnsupportedOperationException e) {
300             String msg = "'dbReset' operation not allowed!";
301             LOG.info(msg);
302             rsp.setStatus(HttpServletResponse.SC_FORBIDDEN);
303             out.println(msg);
304         }
305     }
306 
307     public void setCaptureOperationsModule(CaptureOperationsModule captureOperationsModule) {
308         this.captureOperationsModule = captureOperationsModule;
309     }
310 }