001: /*
002: * Copyright (c) 2001 - 2005 ivata limited.
003: * All rights reserved.
004: * ---------------------------------------------------------
005: * ivata groupware may be redistributed under the GNU General Public
006: * License as published by the Free Software Foundation;
007: * version 2 of the License.
008: *
009: * These programs are free software; you can redistribute them and/or
010: * modify them under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; version 2 of the License.
012: *
013: * These programs are distributed in the hope that they will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: *
017: * See the GNU General Public License in the file LICENSE.txt for more
018: * details.
019: *
020: * If you would like a copy of the GNU General Public License write to
021: *
022: * Free Software Foundation, Inc.
023: * 59 Temple Place - Suite 330
024: * Boston, MA 02111-1307, USA.
025: *
026: *
027: * To arrange commercial support and licensing, contact ivata at
028: * http://www.ivata.com/contact.jsp
029: * ---------------------------------------------------------
030: * $Log: HibernateSetupAction.java,v $
031: * Revision 1.5 2005/10/12 18:36:18 colinmacleod
032: * Standardized format of Logger declaration - to make it easier to find instances
033: * which are not both static and final.
034: *
035: * Revision 1.4 2005/10/11 18:57:17 colinmacleod
036: * Fixed some checkstyle and javadoc issues.
037: *
038: * Revision 1.3 2005/10/03 10:21:15 colinmacleod
039: * Fixed some style and javadoc issues.
040: *
041: * Revision 1.2 2005/10/02 14:08:59 colinmacleod
042: * Added/improved log4j logging.
043: *
044: * Revision 1.1 2005/09/29 13:06:06 colinmacleod
045: * First version of setting subproject.
046: * Existing classes restructured, new setting user interface created.
047: * Flexible XML UI configuration makes it easy to reuse the same web pages in
048: * other projects.
049: * Web files work as stand-alone webapp for testing.
050: *
051: * Revision 1.7 2005/09/15 10:23:26 colinmacleod
052: * Upgraded Maven to 1.1 (beta-2).
053: * Upgraded Hibernate to 3.0.5.
054: *
055: * Revision 1.6 2005/09/14 16:00:16 colinmacleod
056: * Made database paths more flexible, to suit ivata cms.
057: * Removed unused local and class variables.
058: * Added serialVersionUID.
059: *
060: * Revision 1.5 2005/05/10 20:07:14 colinmacleod
061: * Workaround for tomcat 5.0.x (creates
062: * copy of 'res' HSQLDB).
063: *
064: * Revision 1.4 2005/04/30 13:02:36 colinmacleod
065: * Added checking and removal of previous
066: * hibernate lock file.
067: *
068: * Revision 1.3 2005/04/28 18:47:06 colinmacleod
069: * Fixed XHMTL, styles and resin compatibility.
070: * Added support for URL rewriting.
071: *
072: * Revision 1.2 2005/04/22 09:34:21 colinmacleod
073: * Added setup action interface so that
074: * the hibernate action can reset/delete all
075: * actions when the container is reloaded.
076: *
077: * Revision 1.1 2005/04/11 10:03:43 colinmacleod
078: * Added setup feature.
079: *
080: * ---------------------------------------------------------
081: */
082: package com.ivata.groupware.admin.struts;
083:
084: import java.io.File;
085: import java.io.FileNotFoundException;
086: import java.io.FileOutputStream;
087: import java.io.FileReader;
088: import java.io.FileWriter;
089: import java.io.IOException;
090: import java.io.InputStream;
091: import java.io.OutputStream;
092: import java.sql.Connection;
093: import java.sql.DriverManager;
094: import java.sql.ResultSet;
095: import java.sql.Statement;
096: import java.util.Map;
097:
098: import javax.servlet.http.HttpServletRequest;
099: import javax.servlet.http.HttpServletResponse;
100: import javax.servlet.http.HttpSession;
101:
102: import org.apache.log4j.Logger;
103: import org.apache.struts.Globals;
104: import org.apache.struts.action.ActionErrors;
105: import org.apache.struts.action.ActionForm;
106: import org.apache.struts.action.ActionMapping;
107: import org.apache.struts.action.ActionMessage;
108: import org.apache.struts.action.ActionMessages;
109: import org.dom4j.Document;
110: import org.dom4j.DocumentException;
111: import org.dom4j.Element;
112: import org.dom4j.io.OutputFormat;
113: import org.dom4j.io.SAXReader;
114: import org.dom4j.io.XMLWriter;
115: import org.hibernate.Hibernate;
116: import org.hibernate.util.DTDEntityResolver;
117: import org.sourceforge.clientsession.ClientSession;
118:
119: import com.ivata.groupware.admin.security.Security;
120: import com.ivata.groupware.admin.security.server.SecuritySession;
121: import com.ivata.groupware.admin.security.struts.LoginGuestAction;
122: import com.ivata.groupware.admin.setting.Settings;
123: import com.ivata.groupware.container.PicoContainerFactory;
124: import com.ivata.groupware.container.persistence.hibernate.HibernateSetupConstants;
125: import com.ivata.mask.MaskFactory;
126: import com.ivata.mask.util.StringHandling;
127: import com.ivata.mask.util.SystemException;
128: import com.ivata.mask.util.ThrowableHandling;
129: import com.ivata.mask.web.struts.MaskAuthenticator;
130:
131: /**
132: * Setup the application to use a database.
133: *
134: * @since ivata groupware 0.11 (2005-03-25)
135: * @author Colin MacLeod
136: * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
137: * @version $Revision: 1.5 $
138: */
139: public class HibernateSetupAction extends LoginGuestAction {
140: /**
141: * Logger for this class.
142: */
143: private static final Logger logger = Logger
144: .getLogger(HibernateSetupAction.class);
145:
146: /**
147: * Split off from <code>onConfirm</code> to create a new start database.
148: *
149: * @param errorMessages Used to append messages to if there is any error
150: * creating the database.
151: * @param dbFile The file representing the path to the automatically
152: * generated <strong>HypersonicSQL</strong> database.
153: * @param propertiesFile File representing the path to the properties file
154: * for the automatically generated <strong>HypersonicSQL</strong> database.
155: * @param lockFile File representing path to the database lock file
156: * for the automatically generated <strong>HypersonicSQL</strong> database.
157: * @param scriptFile File representing the path to the script file
158: * for the automatically generated <strong>HypersonicSQL</strong> database.
159: * @param startDB The name of the initial database (e.g.
160: * "igwstart").
161: */
162: private static void copyStartDB(final ActionMessages errorMessages,
163: final File dbFile, final File propertiesFile,
164: final File scriptFile, final File lockFile,
165: final String startDB) {
166: if (logger.isDebugEnabled()) {
167: logger.debug("Creating database.");
168: }
169:
170: File dbDirectory = dbFile.getParentFile();
171: // try to create the directory, if it doesn't exist
172: if ((dbDirectory != null) && !dbDirectory.exists()) {
173: if (!dbDirectory.mkdirs()) {
174: logger.error("Could not create directory '"
175: + dbDirectory.getAbsolutePath() + "'.");
176: errorMessages.add(null, new ActionMessage(
177: "errors.setup.databaseDirectory", dbDirectory
178: .getAbsolutePath()));
179: }
180: }
181: // check the previous step didn't fail.
182: if (dbDirectory.exists()) {
183: // see if there is a previous lock file and remove it if so
184: if (lockFile.exists()) {
185: logger.warn("Removing previous lock file '"
186: + lockFile.getAbsolutePath() + "'.");
187: if (!lockFile.delete()) {
188: logger
189: .error("COULD NOT REMOVE previous lock file '"
190: + lockFile.getAbsolutePath() + "'.");
191: }
192: }
193:
194: // now we have the directory, and we know the user is cool with
195: // our writing the files out, so next try to extract the start
196: // db into the chosen path
197: try {
198: byte[] buffer = new byte[HibernateSetupConstants.READ_BUFFER];
199: int bytesRead;
200: InputStream inputStream = Hibernate.class
201: .getResourceAsStream(startDB
202: + HibernateSetupConstants.HSQLDB_PROPERTIES_SUFFIX);
203: if (inputStream == null) {
204: throw new FileNotFoundException(
205: "Could not "
206: + "locate HypersonicSQL properties file '"
207: + startDB
208: + HibernateSetupConstants.HSQLDB_PROPERTIES_SUFFIX
209: + "'");
210: }
211:
212: OutputStream outputStream = new FileOutputStream(
213: propertiesFile);
214: while ((bytesRead = inputStream.read(buffer, 0,
215: buffer.length)) != -1) {
216: outputStream.write(buffer, 0, bytesRead);
217: }
218: inputStream = Hibernate.class
219: .getResourceAsStream(startDB
220: + HibernateSetupConstants.HSQLDB_SCRIPT_SUFFIX);
221: if (inputStream == null) {
222: throw new FileNotFoundException(
223: "Could not "
224: + "locate HypersonicSQL script file '"
225: + startDB
226: + HibernateSetupConstants.HSQLDB_SCRIPT_SUFFIX
227: + "'");
228: }
229: outputStream = new FileOutputStream(scriptFile);
230: while ((bytesRead = inputStream.read(buffer, 0,
231: buffer.length)) != -1) {
232: outputStream.write(buffer, 0, bytesRead);
233: }
234: } catch (Exception e) {
235: logger.error("Could not copy database resources.", e);
236: errorMessages.add(null, new ActionMessage(
237: "errors.setup.databaseResources", e.getClass()
238: .getName(), e.getMessage()));
239: }
240: }
241:
242: if (logger.isDebugEnabled()) {
243: logger.debug("copyStartDB - end");
244: }
245: }
246:
247: /**
248: * <p>
249: * Create a <strong>HypersonicSQL</strong> database on the local file
250: * system.
251: * </p>
252: * <p>
253: * This is a copy of the start (memory) database shipped with <strong>ivata
254: * groupware</strong>. If the form exists already and the user has confirmed
255: * she doesn't want to overwrite, then the existing form is left as is.
256: * </p>
257: *
258: * @param errorMessages if there are any errors, they will be appended to
259: * this collection.
260: * @param databaseURL JDBC URL of the database to copy. This must start
261: * with {@link HibernateSetupConstants.AUTOMATIC_DATABASE_URL_START}.
262: * @param startDB The name of the initial database (e.g.
263: * "igwstart").
264: * @param confirmOverwriteString "true" if the user should be
265: * asked. All other values will overwrite any existing data without asking.
266: * @return <code>true</code> if all is well and the database has been crated
267: * or an existing one can be used, othwerise <code>false</code> if user
268: * confirmation is required before using/overwriting an existing DB.
269: */
270: private static boolean copyStartDB(
271: final ActionMessages errorMessages,
272: final String databaseURL, final String startDB,
273: final String confirmOverwriteString) {
274: if (logger.isDebugEnabled()) {
275: logger.debug("copyStartDB(ActionErrors errorMessages = "
276: + errorMessages + ", String databaseURL = "
277: + databaseURL + ", String startDB = " + startDB
278: + ", String confirmOverwriteString = "
279: + confirmOverwriteString + ") - start");
280: }
281:
282: String dbPath = databaseURL
283: .substring(HibernateSetupConstants.AUTOMATIC_DATABASE_URL_START
284: .length());
285: File dbFile = new File(dbPath);
286: if (logger.isDebugEnabled()) {
287: logger.debug("Database path is '" + dbPath + "', for URL '"
288: + databaseURL + "'");
289: }
290: File propertiesFile = new File(dbPath
291: + HibernateSetupConstants.HSQLDB_PROPERTIES_SUFFIX);
292: File scriptFile = new File(dbPath
293: + HibernateSetupConstants.HSQLDB_SCRIPT_SUFFIX);
294: File lockFile = new File(dbPath
295: + HibernateSetupConstants.HSQLDB_LOCK_SUFFIX);
296: // if either file exists, check the user confirmed we can
297: // overwrite them
298: boolean databaseExists = (propertiesFile.exists() || scriptFile
299: .exists());
300: boolean confirmOverwrite = "true"
301: .equals(confirmOverwriteString);
302: // if the database exists, and we have not asked the user for
303: // confirmation, then go to the user confirmation page
304: if ((confirmOverwriteString == null) && databaseExists) {
305: if (logger.isDebugEnabled()) {
306: logger
307: .debug("copyStartDB - end - return value = " + false);
308: }
309: return false;
310: }
311: if ((!databaseExists) || confirmOverwrite) {
312: copyStartDB(errorMessages, dbFile, propertiesFile,
313: scriptFile, lockFile, startDB);
314: }
315:
316: if (logger.isDebugEnabled()) {
317: logger.debug("copyStartDB - end - return value = " + true);
318: }
319: return true;
320: }
321:
322: /**
323: * Helper to get the appropriate XPath to locate a Hibernate property
324: * element in the config.
325: *
326: * @param propertyName name of the hibernate property.
327: * @return XPath expression for this property.
328: */
329: private static String getHibernateXPath(final String propertyName) {
330: if (logger.isDebugEnabled()) {
331: logger.debug("getHibernateXPath(String propertyName = "
332: + propertyName + ") - start");
333: }
334:
335: String returnString = "/hibernate-configuration/session-factory/property[@name='"
336: + propertyName + "']";
337: if (logger.isDebugEnabled()) {
338: logger
339: .debug("getHibernateXPath(String) - end - return value = "
340: + returnString);
341: }
342: return returnString;
343: }
344:
345: /**
346: * Stores the actions in the request processor, so they can be reset.
347: */
348: private Map actions;
349: /**
350: * <copyDoc>Refer to {@link HibernateSetupAction()}.</copyDoc>
351: */
352: private Settings settings;
353: /**
354: * Used to store the value of system setting "siteDefaultDB".
355: */
356: private String startDB;
357:
358: /**
359: * Constructor.
360: *
361: * @param securityParam System security object.
362: * @param settingsParam System settings object.
363: * @param maskFactoryParam Creates input and list masks.
364: * @param authenticatorParam Used to check user is auhtorized to see this
365: * page.
366: */
367: public HibernateSetupAction(final Security securityParam,
368: final Settings settingsParam,
369: final MaskFactory maskFactoryParam,
370: final MaskAuthenticator authenticatorParam) {
371: super (securityParam, settingsParam, maskFactoryParam,
372: authenticatorParam);
373: this .settings = settingsParam;
374: }
375:
376: /**
377: * Check the database connection specified in the form, and log any errors.
378: * @param setupForm the form from the current request, containing the
379: * database settings to check.
380: * @param errorMessages collection to append error messages to.
381: */
382: private void checkConnection(final HibernateSetupForm setupForm,
383: final ActionMessages errorMessages) {
384: if (logger.isDebugEnabled()) {
385: logger
386: .debug("checkConnection(HibernateSetupForm setupForm = "
387: + setupForm
388: + ", ActionErrors errorMessages = "
389: + errorMessages + ") - start");
390: }
391:
392: String databaseDriver = setupForm.getDatabaseDriver();
393: String databasePassword = setupForm.getDatabasePassword();
394: String databaseURL = setupForm.getDatabaseURL();
395: String databaseUserName = setupForm.getDatabaseUserName();
396: // first check we can connect to the chosen db
397: // does the database driver exist?
398: try {
399: Class.forName(databaseDriver);
400: Connection connection = null;
401: Statement statement = null;
402: try {
403: connection = DriverManager.getConnection(databaseURL,
404: databaseUserName, databasePassword);
405: statement = connection.createStatement();
406: // NOTE: this is about the only SQL in the whole app.
407: ResultSet allSettings = statement
408: .executeQuery("select name, value, type from setting where "
409: + "person_user is null");
410: if (!allSettings.next()) {
411: errorMessages.add(null, new ActionMessage(
412: "errors.setup.databaseSettings",
413: databaseURL, databaseUserName));
414: }
415: } catch (Exception e) {
416: // if we get in here it means we have a driver but could
417: // not connect
418: logger.error(e.getClass().getName()
419: + ": connecting to URL '"
420: + setupForm.getDatabaseURL() + "'", e);
421: Throwable cause = ThrowableHandling.getCause(e);
422: errorMessages.add(null, new ActionMessage(
423: "errors.setup.databaseConnection", databaseURL,
424: databaseUserName, cause.getClass().getName(),
425: StringHandling.getNotNull(cause.getMessage())));
426: } finally {
427: if (statement != null) {
428: try {
429: statement.close();
430: } catch (Exception e) {
431: logger.error("checkConnection - "
432: + e.getClass().getName()
433: + ": clsoing the statement.", e);
434:
435: }
436: }
437: if (connection != null) {
438: try {
439: connection.close();
440: } catch (Exception e) {
441: logger.error("checkConnection - "
442: + e.getClass().getName()
443: + ": clsoing the connection.", e);
444:
445: }
446: }
447: }
448: } catch (ClassNotFoundException e) {
449: // if we get in here it means the database driver was not
450: // found
451: logger.error(e.getClass().getName()
452: + ": looking for driver '"
453: + setupForm.getDatabaseDriver() + "'", e);
454: errorMessages.add(null, new ActionMessage(
455: "errors.setup.databaseDriver", databaseDriver));
456: }
457:
458: if (logger.isDebugEnabled()) {
459: logger.debug("checkConnection - end");
460: }
461: }
462:
463: /**
464: * Check the form is valid and, if not, return the action forward we should
465: * go to, to sort it out.
466: *
467: * @param mappingParam current action mapping from <em>Struts</em> config.
468: * @param formParam optional ActionForm bean for this request (if any).
469: * @return <code>null</code> if the action should continue, otherwise the
470: * name of a forward to pass control to.
471: */
472: protected String checkForm(final ActionMapping mappingParam,
473: final ActionForm formParam) {
474: if (logger.isDebugEnabled()) {
475: logger.debug("checkForm(ActionMapping mappingParam = "
476: + mappingParam + ", ActionForm formParam = "
477: + formParam + ") - start");
478: }
479:
480: if (!"setupForm".equals(mappingParam.getName())) {
481: if (logger.isDebugEnabled()) {
482: logger
483: .debug("checkForm - end - return value = setupAction");
484: }
485: return "setupAction";
486: }
487:
488: if (logger.isDebugEnabled()) {
489: logger.debug("checkForm - end - return value = " + null);
490: }
491: return null;
492: }
493:
494: /**
495: * Prepare the form for the first use of the setup page.
496: *
497: * @param mappingParam {@inheritDoc}
498: * @param formParam {@inheritDoc}
499: * @param requestParam {@inheritDoc}
500: * @param responseParam {@inheritDoc}
501: * @param sessionParam {@inheritDoc}
502: * @param clientSessionParam {@inheritDoc}
503: * @return "setup"
504: * @throws SystemException Not thrown here.
505: */
506: public String execute(final ActionMapping mappingParam,
507: final ActionForm formParam,
508: final HttpServletRequest requestParam,
509: final HttpServletResponse responseParam,
510: final HttpSession sessionParam,
511: final ClientSession clientSessionParam)
512: throws SystemException {
513: if (logger.isDebugEnabled()) {
514: logger.debug("execute(ActionMapping mappingParam = "
515: + mappingParam + ", ActionForm formParam = "
516: + formParam
517: + ", HttpServletRequest requestParam = "
518: + requestParam
519: + ", HttpServletResponse responseParam = "
520: + responseParam + ", HttpSession session = "
521: + sessionParam + ", ClientSession clientSession = "
522: + clientSessionParam + ") - start");
523: }
524:
525: // if there is no security session
526: SecuritySession securitySession = (SecuritySession) sessionParam
527: .getAttribute("securitySession");
528: startDB = settings.getStringSetting(securitySession,
529: "siteDefaultDB", null);
530: if (securitySession == null) {
531: super .execute(mappingParam, formParam, requestParam,
532: responseParam, sessionParam, clientSessionParam);
533: securitySession = (SecuritySession) sessionParam
534: .getAttribute("securitySession");
535: }
536: if (formParam == null) {
537: if (logger.isDebugEnabled()) {
538: logger.debug("execute - end - return value = setup");
539: }
540: return "setup";
541: }
542: HibernateSetupForm setupForm = (HibernateSetupForm) formParam;
543: // by default create database automatically, if we are using the memory
544: // db already
545: ActionErrors errorMessages = new ActionErrors();
546: Document document = readHibernateConfig(errorMessages);
547: String autoDB = HibernateSetupConstants.AUTOMATIC_DATABASE_MEMORY_URL
548: + settings.getStringSetting(securitySession,
549: "siteDefaultDB", null);
550: String uRL = autoDB;
551: if (errorMessages.isEmpty()) {
552: // note here - if there were errors, we will just default to create
553: // a db automatically below
554: Element element = (Element) document
555: .selectSingleNode(getHibernateXPath(HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_URL));
556: if (element != null) {
557: uRL = element.getTextTrim();
558: }
559: }
560: // if you are logged on as guest and the default (memory) URL is not
561: // used (i.e. this is a real, not test system), you need to log in first
562: if (securitySession.isGuest() && !autoDB.equals(uRL)) {
563: if (logger.isDebugEnabled()) {
564: logger
565: .debug("execute - end - return value = setupSecurity");
566: }
567: return "setupSecurity";
568: }
569: // by default, create the database automatically for the user, if she
570: // is still using the memory db
571: if (autoDB.equals(uRL)) {
572: setupForm.setCreateDatabaseAutomatically(true);
573: } else {
574: // default the form values from the hibernate config
575: setupForm.setCreateDatabaseAutomatically(false);
576: setupForm.setDatabaseURL(uRL);
577: Element element = (Element) document
578: .selectSingleNode(getHibernateXPath(HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_DIALECT));
579: setupForm.setDatabaseDialect(element.getTextTrim());
580: element = (Element) document
581: .selectSingleNode(getHibernateXPath(HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_DRIVER));
582: setupForm.setDatabaseDriver(element.getTextTrim());
583: element = (Element) document
584: .selectSingleNode(getHibernateXPath(HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_PASSWORD));
585: setupForm.setDatabasePassword(element.getTextTrim());
586: element = (Element) document
587: .selectSingleNode(getHibernateXPath(HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_USER_NAME));
588: setupForm.setDatabaseUserName(element.getTextTrim());
589: }
590:
591: if (logger.isDebugEnabled()) {
592: logger.debug("execute - end - return value = " + null);
593: }
594: return null;
595: }
596:
597: /**
598: * Called when the user clicks the 'OK' button to continue with the chosen
599: * setup options. If you chose hibernate settings, this will write them
600: * out.
601: * @param mappingParam {@inheritDoc}
602: * @param formParam {@inheritDoc}
603: * @param request {@inheritDoc}
604: * @param responseParam {@inheritDoc}
605: * @param session {@inheritDoc}
606: * @param clientSession {@inheritDoc}
607: * @param defaultForwardParam {@inheritDoc}
608: * @throws SystemException If the configuration cannot be written for any
609: * reason.
610: * @return "setup", if there is no form, "setupConfirm"
611: * if the user must confirm a DB overwrite, otherwise
612: * <code>defaultForwardParam</code>.
613: */
614: public String onConfirm(final ActionMapping mappingParam,
615: final ActionForm formParam,
616: final HttpServletRequest request,
617: final HttpServletResponse responseParam,
618: final HttpSession session,
619: final ClientSession clientSession,
620: final String defaultForwardParam) throws SystemException {
621: if (logger.isDebugEnabled()) {
622: logger.debug("onConfirm(ActionMapping mappingParam = "
623: + mappingParam + ", ActionForm formParam = "
624: + formParam + ", HttpServletRequest request = "
625: + request
626: + ", HttpServletResponse responseParam = "
627: + responseParam + ", HttpSession session = "
628: + session + ", ClientSession clientSession = "
629: + clientSession + ", String defaultForwardParam = "
630: + defaultForwardParam + ") - start");
631: }
632:
633: HibernateSetupForm setupForm = (HibernateSetupForm) formParam;
634: if (formParam == null) {
635: if (logger.isDebugEnabled()) {
636: logger.debug("onConfirm - end - return value = setup");
637: }
638: return "setup";
639: }
640: // if we should create the database automatically, do that first
641: ActionMessages errors = new ActionMessages();
642: if (setupForm.isCreateDatabaseAutomatically()) {
643: // the method fails if it needs user confirmation
644: String confirmOverwriteString = request
645: .getParameter("confirmOverwrite");
646: if (!copyStartDB(errors, setupForm.getDatabaseURL(),
647: startDB, confirmOverwriteString)) {
648: if (logger.isDebugEnabled()) {
649: logger.debug("onConfirm - end - return value = "
650: + "setupConfirm");
651: }
652: return "setupConfirm";
653: }
654: }
655:
656: // if there was no error till this point, that means any automatic
657: // process went smoothly. in that case, perform validation checks
658: // on the db
659: if (errors.isEmpty()) {
660: checkConnection(setupForm, errors);
661: saveErrors(request, errors);
662: saveToken(request);
663: }
664: // if there are no errors thus far, try writing out the new hibernate
665: // configuration
666: // first read in the existing hibernate config
667: Document document = null;
668: if (errors.isEmpty()) {
669: document = readHibernateConfig(errors);
670: }
671: // if that went well, update the settings in the config
672: if ((document != null) && errors.isEmpty()) {
673: updateHibernateConfig(document, setupForm, errors);
674: }
675: // finally, write out the changed Hibernate config
676: if ((document != null) && errors.isEmpty()) {
677: writeHibernateConfig(document, errors);
678: }
679:
680: // now, if all is well, reset the pico container factory to use
681: // the new settings database. then update the mail settings to the
682: // values shown
683: if (errors.isEmpty()) {
684: // login as guest to force a re-login after this process
685: SecuritySession securitySession = (SecuritySession) session
686: .getAttribute("securitySession");
687: if ((securitySession == null) || !securitySession.isGuest()) {
688: super .execute(mappingParam, formParam, request,
689: responseParam, session, clientSession);
690: }
691: // FIXME: this is dirty. I need a nicer way to trigger a reset of
692: // all cached actions in the request processor at this point
693: assert (actions != null);
694: actions.clear();
695: resetFactoryUpdateSettings(setupForm,
696: (SecuritySession) session
697: .getAttribute("securitySession"), session);
698: }
699:
700: // did we get error messages? if not, continue as normal to the results
701: // page
702: if (errors.isEmpty()) {
703: // confirm that the application is set-up, in case the memory
704: // database is still used. Note, in this case, the user will have to
705: // go thru' the setup again, the next time the app is restarted -
706: // "that's not a bug, it's a feature" ;-)
707: servlet.getServletContext().setAttribute(
708: HibernateSetupConstants.CONFIRM_ATTRIBUTE,
709: Boolean.TRUE);
710:
711: if (logger.isDebugEnabled()) {
712: logger.debug("onConfirm - end - return value = "
713: + defaultForwardParam);
714: }
715: return defaultForwardParam;
716: }
717:
718: // if there were errors, return to the setup input page
719: request.setAttribute(Globals.ERROR_KEY, errors);
720:
721: if (logger.isDebugEnabled()) {
722: logger.debug("onConfirm - end - return value = " + null);
723: }
724: return null;
725: }
726:
727: /**
728: * Read in the current hibernate config so we can update the changed
729: * <strong>Hibernate</strong> configuration.
730: * @param errorMessages any error will be added to this collection.
731: * @return <strong>DOM4J</strong> document containing the
732: * <strong>Hibernate</strong> configuration.
733: */
734: private Document readHibernateConfig(
735: final ActionMessages errorMessages) {
736: if (logger.isDebugEnabled()) {
737: logger
738: .debug("readHibernateConfig(ActionErrors errorMessages = "
739: + errorMessages + ") - start");
740: }
741:
742: String hibernateConfigPath = servlet.getServletContext()
743: .getRealPath(HibernateSetupConstants.HIBERNATE_CONFIG);
744: SAXReader reader = new SAXReader();
745: Document document = null;
746: // first read in the existing Hibernate config
747: try {
748: // use an entity resolver so the DTD is found even if you have
749: // no internet
750: reader.setEntityResolver(new DTDEntityResolver());
751: // without the FileReader, it fails on resin
752: document = reader.read(new FileReader(hibernateConfigPath));
753: } catch (FileNotFoundException e) {
754: logger.error(e.getClass().getName()
755: + ": reading the hibernate config '"
756: + HibernateSetupConstants.HIBERNATE_CONFIG
757: + "', real path '" + hibernateConfigPath + "'", e);
758: errorMessages.add(null, new ActionMessage(
759: "errors.setup.hibernateConfigRead", e.getClass()
760: .getName(),
761: HibernateSetupConstants.HIBERNATE_CONFIG,
762: hibernateConfigPath, e.getMessage()));
763: } catch (DocumentException e) {
764: logger.error(e.getClass().getName()
765: + ": reading the hibernate config '"
766: + HibernateSetupConstants.HIBERNATE_CONFIG
767: + "', real path '" + hibernateConfigPath + "'", e);
768: errorMessages.add(null, new ActionMessage(
769: "errors.setup.hibernateConfigRead", e.getClass()
770: .getName(),
771: HibernateSetupConstants.HIBERNATE_CONFIG,
772: hibernateConfigPath, e.getMessage()));
773: }
774:
775: if (logger.isDebugEnabled()) {
776: logger.debug("readHibernateConfig - end - return value = "
777: + document);
778: }
779: return document;
780: }
781:
782: /**
783: * Reset the pico container factory. Override this method toupdate settings
784: * in the new factory.
785: * @param hibernateSetupForm the form from the current request.
786: * @param securitySession guest security session, used to access settings.
787: * @param session current HTTP session.
788: * @throws SystemException if any setting cannot be set.
789: */
790: protected void resetFactoryUpdateSettings(
791: final HibernateSetupForm hibernateSetupForm,
792: final SecuritySession securitySession,
793: final HttpSession session) throws SystemException {
794: if (logger.isDebugEnabled()) {
795: logger.debug("resetFactoryUpdateSettings"
796: + "(HibernateSetupForm hibernateSetupForm = "
797: + hibernateSetupForm
798: + ", SecuritySession securitySession = "
799: + securitySession + ", HttpSession session = "
800: + session + ") - start");
801: }
802:
803: if (logger.isDebugEnabled()) {
804: logger.debug("resetFactoryUpdateSettings - end");
805: }
806: }
807:
808: /**
809: * This method should only ever be called by the request processor,
810: * to set the actions so they can be reset at the appropriate time.
811: *
812: * @param actionsParam map of all action instances.
813: */
814: public void setActions(final Map actionsParam) {
815: if (logger.isDebugEnabled()) {
816: logger.debug("setActions(Map actionsParam = "
817: + actionsParam + ") - start");
818: }
819:
820: this .actions = actionsParam;
821:
822: if (logger.isDebugEnabled()) {
823: logger.debug("setActions(Map) - end");
824: }
825: }
826:
827: /**
828: * Helper to select a single property node from the hibernate config
829: * file, set its text and handle any error.
830: * @param propertyName name of the property to search for.
831: * @param text new value of the text for this element, if found.
832: * @param document hibernate config document.
833: * @param errorMessages any error will be added to this collection.
834: */
835: private void setHibernateProperty(final String propertyName,
836: final String text, final Document document,
837: final ActionMessages errorMessages) {
838: if (logger.isDebugEnabled()) {
839: logger.debug("setHibernateProperty(String propertyName = "
840: + propertyName + ", String text = " + text
841: + ", Document document = " + document
842: + ", ActionErrors errorMessages = " + errorMessages
843: + ") - start");
844: }
845:
846: // change the config to match the new settings
847: String xPath = getHibernateXPath(propertyName);
848: Element element = (Element) document.selectSingleNode(xPath);
849: if (element == null) {
850: errorMessages.add(null, new ActionMessage(
851: "errors.setup.hibernateConfigElement",
852: HibernateSetupConstants.HIBERNATE_CONFIG, xPath));
853: } else {
854: element.setText(text);
855: }
856:
857: if (logger.isDebugEnabled()) {
858: logger.debug("setHibernateProperty - end");
859: }
860: }
861:
862: /**
863: * Update the entries in the <strong>DOM4J</strong> document to represent
864: * the new <strong>Hibernate</strong> config values.
865: *
866: * @param document <strong>DOM4J</strong> document representing the
867: * <strong>Hibernate</strong> config values.
868: * @param setupForm current form we are processing from the request.
869: * @param errorMessages any error will be added to this collection.
870: */
871: private void updateHibernateConfig(final Document document,
872: final HibernateSetupForm setupForm,
873: final ActionMessages errorMessages) {
874: if (logger.isDebugEnabled()) {
875: logger.debug("updateHibernateConfig(Document document = "
876: + document + ", HibernateSetupForm setupForm = "
877: + setupForm + ", ActionErrors errorMessages = "
878: + errorMessages + ") - start");
879: }
880:
881: setHibernateProperty(
882: HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_DRIVER,
883: setupForm.getDatabaseDriver(), document, errorMessages);
884: setHibernateProperty(
885: HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_URL,
886: setupForm.getDatabaseURL(), document, errorMessages);
887: setHibernateProperty(
888: HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_DIALECT,
889: setupForm.getDatabaseDialect(), document, errorMessages);
890: setHibernateProperty(
891: HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_USER_NAME,
892: setupForm.getDatabaseUserName(), document,
893: errorMessages);
894: setHibernateProperty(
895: HibernateSetupConstants.HIBERNATE_PROPERTY_DATABASE_PASSWORD,
896: setupForm.getDatabasePassword(), document,
897: errorMessages);
898:
899: if (logger.isDebugEnabled()) {
900: logger.debug("updateHibernateConfig - end");
901: }
902: }
903:
904: /**
905: * Write out the hibernate config to the file.
906: * @param document the current <strong>DOM4J</strong>, with updated
907: * settings.
908: * @param errorMessages if the file cannot be written, a message is logged
909: * here.
910: * @throws SystemException If we fail to retrieve an instance of the
911: * <code>PicoContainerFactory</code>.
912: */
913: private void writeHibernateConfig(final Document document,
914: final ActionMessages errorMessages) throws SystemException {
915: if (logger.isDebugEnabled()) {
916: logger.debug("writeHibernateConfig(Document document = "
917: + document + ", ActionErrors errorMessages = "
918: + errorMessages + ") - start");
919: }
920:
921: // store the filename so we read the same config in next time
922: String hibernateConfigPath = servlet.getServletContext()
923: .getRealPath(HibernateSetupConstants.HIBERNATE_CONFIG);
924: PicoContainerFactory.getInstance().setHibernateConfigFileName(
925: hibernateConfigPath);
926: // now this is the bit where we write the config out
927: try {
928: XMLWriter writer = new XMLWriter(new FileWriter(
929: hibernateConfigPath), new OutputFormat(" ", true));
930: writer.write(document);
931: writer.close();
932: } catch (IOException e) {
933: logger.error(e.getClass().getName()
934: + ": writing the hibernate config '"
935: + HibernateSetupConstants.HIBERNATE_CONFIG
936: + "', real path '" + hibernateConfigPath + "'", e);
937: errorMessages.add(null, new ActionMessage(
938: "errors.setup.hibernateConfigRead", e.getClass()
939: .getName(),
940: HibernateSetupConstants.HIBERNATE_CONFIG,
941: hibernateConfigPath, e.getMessage()));
942: }
943:
944: if (logger.isDebugEnabled()) {
945: logger
946: .debug("writeHibernateConfig(Document, ActionErrors) - end");
947: }
948: }
949: }
|