View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/servlets/Controller.java,v 1.40 2006/03/18 16:01:07 hkollmann Exp $
3    * $Revision: 1.40 $
4    * $Date: 2006/03/18 16:01:07 $
5    *
6    * DbForms - a Rapid Application Development Framework
7    * Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation; either
12   * version 2.1 of the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22   */
23  
24  package org.dbforms.servlets;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import org.dbforms.servlets.base.AbstractServletBase;
30  
31  import org.dbforms.config.DbFormsConfig;
32  import org.dbforms.config.DbFormsConfigRegistry;
33  import org.dbforms.config.MultipleValidationException;
34  import org.dbforms.config.Table;
35  
36  import org.dbforms.event.AbstractDatabaseEvent;
37  import org.dbforms.event.EventEngine;
38  import org.dbforms.event.AbstractWebEvent;
39  
40  import org.dbforms.util.ParseUtil;
41  import org.dbforms.util.SqlUtil;
42  import org.dbforms.util.Util;
43  
44  import org.dbforms.validation.ValidatorConstants;
45  
46  import java.io.IOException;
47  
48  import java.sql.Connection;
49  import java.sql.SQLException;
50  
51  import java.util.Enumeration;
52  import java.util.Hashtable;
53  import java.util.Vector;
54  
55  import javax.servlet.ServletException;
56  import javax.servlet.http.HttpServletRequest;
57  import javax.servlet.http.HttpServletResponse;
58  import javax.servlet.http.HttpSession;
59  
60  
61  
62  /***
63   * This servlets is the Controller component in the Model-View-Controller -
64   * architecture used by the dbforms-framework. Every request goes through this
65   * component and its event dispatching facilities.
66   *
67   * @author Joe Peer
68   */
69  public class Controller extends AbstractServletBase {
70  
71     /*** logging category for this class */
72     private static Log logCat        = LogFactory.getLog(Controller.class.getName());
73  
74  
75     /***
76      * Initialize this servlet.
77      *
78      * @exception ServletException if the initialization fails
79      */
80     public void init() throws ServletException {
81        super.init();
82     }
83  
84  
85     /***
86      * Gets the connection object. Holds a list of all used connections. So they
87      * stay open between calls an can be reused
88      *
89      * @param request the request object
90      * @param tableId the table identifier
91      * @param connectionsTable the connections hash table
92      *
93      * @return The connection object
94      */
95     private Connection getConnection(DbFormsConfig      config,
96                                      HttpServletRequest request,
97                                      int                tableId,
98                                      Hashtable          connectionsTable)
99                               throws SQLException {
100       String     connectionName = null;
101       Connection con = null;
102 
103       // get the connection name from the request;
104       if (tableId != -1) {
105          connectionName = ParseUtil.getParameter(request, "invname_" + tableId);
106       }
107 
108       connectionName = Util.isNull(connectionName) ? "default"
109                                                    : connectionName;
110 
111       if ((con = (Connection) connectionsTable.get(connectionName)) == null) {
112          con = config.getConnection(connectionName);
113          connectionsTable.put(connectionName, con);
114       }
115 
116       return con;
117    }
118 
119 
120    /***
121     * In our development, we sometimes set the connection object to  autoCommit
122     * = false in the interceptor (Pre... methods).  This allows us to have
123     * dbForms do part of the required transaction (other parts are done via
124     * jdbc calls). If the database throws an exception, then we need to make
125     * sure that the connection is reinitialized (rollbacked) before it is sent
126     * back into the connection pool.  Grunikiewicz.philip&at;hydro.qc.ca
127     * 2001-10-29
128     *
129     * @param con the connection object
130     */
131    private void cleanUpConnectionAfterException(Connection con) {
132       try {
133          // Do only if autoCommit is disabled
134          if ((con != null) && (!con.getAutoCommit())) {
135             con.rollback();
136             con.setAutoCommit(true);
137          }
138       } catch (java.sql.SQLException e) {
139          SqlUtil.logSqlException(e);
140       }
141    }
142 
143 
144    /***
145     * Close all the connections stored into the the connections HashTable.
146     *
147     * @param connectionsTable the connections HashTable
148     */
149    private void closeConnections(Hashtable connectionsTable) {
150       Enumeration cons = connectionsTable.keys();
151 
152       while (cons.hasMoreElements()) {
153          String     dbConnectionName = (String) cons.nextElement();
154          Connection con = (Connection) connectionsTable.get(dbConnectionName);
155 
156          try {
157             // Do only if autoCommit is disabled
158             if ((con != null) && (!con.getAutoCommit())) {
159                con.commit();
160                con.setAutoCommit(true);
161             }
162          } catch (java.sql.SQLException e) {
163             SqlUtil.logSqlException(e);
164          }
165 
166          SqlUtil.closeConnection(con);
167       }
168    }
169 
170 
171    /***
172     * Process the incoming requests.
173     *
174     * @param request the request object
175     * @param response the response object
176     *
177     * @throws IOException
178     * @throws ServletException
179     */
180    protected void process(HttpServletRequest  request,
181                         HttpServletResponse response)
182                  throws IOException, ServletException {
183 
184       HttpSession session = request.getSession(true);
185       logCat.debug("session timeout: " + session.getMaxInactiveInterval());
186       
187       
188       // take Config-Object from application context - this object should have been
189       // initalized by Config-Servlet on Webapp/server-startup!
190       DbFormsConfig config = null;
191 
192       try {
193          config = DbFormsConfigRegistry.instance()
194                                        .lookup();
195       } catch (Exception e) {
196          logCat.error(e);
197          throw new ServletException(e);
198       }
199 
200 
201       Hashtable connections = new Hashtable();
202       Connection con    = null;
203       AbstractWebEvent   e      = null;
204       Vector     errors = new Vector();
205 
206       try {
207          request.setAttribute("errors", errors);
208 
209          EventEngine engine = new EventEngine(request, config);
210          e = engine.generatePrimaryEvent();
211 
212          // send as info to dbForms (=> Taglib)
213          if (e != null) {
214             request.setAttribute("webEvent", e);
215          }
216 
217          try {
218             con = getConnection(config, request,
219                                 ((e == null) || (e.getTable() == null)) ? (-1)
220                                                                         : e.getTable().getId(),
221                                 connections);
222 
223             // primary event can be any kind of event (database, navigation...)
224             if (e instanceof AbstractDatabaseEvent) {
225                try {
226                   // if hidden formValidatorName exist and it's an Update or Insert event,
227                   // doValidation with Commons-Validator
228                   String formValidatorName = request.getParameter(ValidatorConstants.FORM_VALIDATOR_NAME
229                                                                   + "_"
230                                                                   + e.getTable().getId());
231 
232                   if (formValidatorName != null) {
233                      ((AbstractDatabaseEvent) e).doValidation(formValidatorName,
234                                                       getServletContext());
235                   }
236 
237                   ((AbstractDatabaseEvent) e).processEvent(con);
238                } catch (SQLException sqle) {
239                   logCat.error("::process - SQLException:", sqle);
240                   errors.addElement(sqle);
241                   cleanUpConnectionAfterException(con);
242                } catch (MultipleValidationException mve) {
243                   processMultipleValidationException(con, errors, mve);
244                }
245             }
246 
247             //else
248             //{
249             // currently, we support db events ONLY
250             // but in future there may be events with processEvent() method which do not need a jdbc con!
251             // (you may think: "what about navigation events?" - well they are created by the
252             // controller but they get executed in the referncing "DbFormTag" at the jsp -- that's why we
253             // do not any further operations on them right here...we just put them into the request)
254             //}
255             // secondary Events are always database events
256             // (in fact, they all are SQL UPDATEs)
257             if (engine.getInvolvedTables() != null) {
258                processInvolvedTables(config, request, connections, e, errors,
259                                      engine);
260             }
261          } catch (SQLException exc) {
262             throw new ServletException(exc);
263          }
264       } finally {
265          // close all the connections stored into the connections hash table;
266          closeConnections(connections);
267 
268          if (e != null) {
269             // PG  - if form contained errors, use followupOnError (if available!)
270             String followUp = null;
271 
272             if (errors.size() != 0) {
273                followUp = e.getFollowUpOnError();
274             }
275 
276             if (Util.isNull(followUp)) {
277                followUp = e.getFollowUp();
278             }
279 
280             logCat.info("*** e = " + e + "*** e.getFollowUp() = "
281                         + e.getFollowUp());
282 
283             if (!Util.isNull(followUp)) {
284                request.getRequestDispatcher(followUp)
285                       .forward(request, response);
286             }
287          }
288       }
289    }
290 
291 
292    /***
293     * PRIVATE METHODS here
294     *
295     * @param request DOCUMENT ME!
296     * @param connections DOCUMENT ME!
297     * @param e DOCUMENT ME!
298     * @param errors DOCUMENT ME!
299     * @param engine DOCUMENT ME!
300     */
301    /***
302     * Process tables related to the main event table. <br>
303     * For every table related to the parent one, generate secundary (update)
304     * events  for that table and execute them.
305     *
306     * @param request the request object
307     * @param connections the connections hashTable
308     * @param e the main webEvent object
309     * @param errors the errors vector
310     * @param engine the eventEngine reference
311     */
312    private void processInvolvedTables(DbFormsConfig      config,
313                                       HttpServletRequest request,
314                                       Hashtable          connections,
315                                       AbstractWebEvent           e,
316                                       Vector             errors,
317                                       EventEngine        engine)
318                                throws SQLException {
319       Connection con;
320 
321       // may be null if empty form!
322       Enumeration tableEnum = engine.getInvolvedTables()
323                                     .elements();
324 
325       while (tableEnum.hasMoreElements()) {
326          Table       t         = (Table) tableEnum.nextElement();
327          Enumeration eventEnum = engine.generateSecundaryEvents(t, e);
328 
329          // scan all the secundary events for the current secundary table;
330          while (eventEnum.hasMoreElements()) {
331             AbstractDatabaseEvent dbE = (AbstractDatabaseEvent) eventEnum.nextElement();
332 
333             // 2003-02-03 HKK: do not do the work twice - without this every event 
334             // would be generated for each table and event
335             if (t.getId() == dbE.getTable()
336                                       .getId()) {
337                con = getConnection(config, request, dbE.getTable().getId(),
338                                    connections);
339 
340                // 2003-02-03 HKK: do not do the work twice!!!
341                String formValidatorName = request.getParameter(ValidatorConstants.FORM_VALIDATOR_NAME
342                                                                + "_"
343                                                                + dbE.getTable().getId());
344 
345                try {
346                   // if hidden formValidatorName exist and it's an Update or Insert event,
347                   // doValidation with Commons-Validator
348                   if (formValidatorName != null) {
349                      dbE.doValidation(formValidatorName, getServletContext());
350                   }
351 
352                   dbE.processEvent(con);
353                } catch (SQLException sqle2) {
354                   SqlUtil.logSqlException(sqle2,
355                                           "::process - exception while process secundary events");
356                   errors.addElement(sqle2);
357                   cleanUpConnectionAfterException(con);
358                } catch (MultipleValidationException mve) {
359                   processMultipleValidationException(con, errors, mve);
360                }
361             }
362          }
363       }
364    }
365 
366 
367 
368 
369    /***
370     * Process the input MultipleValidationException object.
371     *
372     * @param con the connection object to close
373     * @param errors the errors vector to fill
374     * @param mve the MultipleValidationException to process
375     */
376    private void processMultipleValidationException(Connection                  con,
377                                                    Vector                      errors,
378                                                    MultipleValidationException mve) {
379       java.util.Vector v = null;
380 
381       logCat.error("::processMultipleValidationException - exception", mve);
382 
383       if ((v = mve.getMessages()) != null) {
384          Enumeration e = v.elements();
385 
386          while (e.hasMoreElements()) {
387             errors.addElement(e.nextElement());
388          }
389       }
390 
391       cleanUpConnectionAfterException(con);
392    }
393 
394 
395 }