package marubinotto.piggydb.ui; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import marubinotto.piggydb.extension.ExtensionDeployer; import marubinotto.piggydb.impl.db.DatabaseSchema; import marubinotto.piggydb.impl.db.SequenceAdjusterList; import marubinotto.piggydb.ui.util.ModifiedClickContext; import marubinotto.util.procedure.Procedure; import marubinotto.util.procedure.Transaction; import net.sf.click.Context; import net.sf.click.extras.spring.SpringClickServlet; import net.sf.click.util.ClickLogger; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class PiggydbServlet extends SpringClickServlet { private static Log log = LogFactory.getLog(PiggydbServlet.class); private static final String BEAN_TRANSACTION = "transaction"; private static final String BEAN_DATABASE_SCHEMA = "databaseSchemaForInit"; private static final String BEAN_SEQUENCE_ADJUSTER_LIST = "sequenceAdjusterList"; @Override public void init() throws ServletException { log.info("ServerInfo: " + getServletContext().getServerInfo()); log.info("ContextName: " + getServletContext().getServletContextName()); // log.info("ContextPath: " + getServletContext().getContextPath()); // Servlet 2.5 // Deploy the resources of extensions // // This should be done before init of Click in order for it to recognize the resources try { // Extension.testClassLoaderResources(); ExtensionDeployer.deployWebappFiles(getServletContext()); } catch (IOException e) { throw new ServletException(e); } // Initialize Click Framework and applicationContext super.init(); // Suppress click logger this.logger.setLevel(ClickLogger.ERROR_ID + 1); // Update database schema // // The transaction doesn't make any sense because H2 doesn't support transactional DDL // (an implicit 'commit' occurs after a DDL statement is executed). So database schema // consistency cannot be assured. Transaction transaction = (Transaction)this.applicationContext.getBean(BEAN_TRANSACTION); final DatabaseSchema schema = (DatabaseSchema) this.applicationContext.getBean(BEAN_DATABASE_SCHEMA); try { transaction.execute(new Procedure() { public Object execute(Object input) throws Exception { schema.update(); return null; } }); } catch (Exception e) { // Just log the error to avoid a situation in which the server cannot be started // for database inconsistency. When the database is found to be inconsistent, // the user should export their data and delete the H2 database files, then // restart the server to re-create a database and restore it with the exported data. // // FIXME It's possible that a user cannot export the database because of an exception // for inconsistent schema. log.error(e); } log.info("Schema updated to: " + schema.getVersion()); // Workaround for H2 sequence problem after restart // // sequences are incremented not by one but by 32 on the file system. // Internally (in memory) they are not of course, // but after a system crash the value read // from the disk will be at most 32 too high (average 16). SequenceAdjusterList adjusterList = (SequenceAdjusterList) this.applicationContext.getBean(BEAN_SEQUENCE_ADJUSTER_LIST); try { adjusterList.adjust(); } catch (Exception e) { throw new ServletException(e); } // Initialize extensions try { ExtensionDeployer.initAll(getServletContext(), this.applicationContext); } catch (IOException e) { throw new ServletException(e); } } @Override protected Context createContext( HttpServletRequest request, HttpServletResponse response, boolean isPost) { return new ModifiedClickContext( getServletContext(), getServletConfig(), request, response, isPost, this); } }