Java tutorial
/* * Copyright 2008 The Tongue-Tied Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.tonguetied.web.servlet; import static org.tonguetied.utils.database.Constants.KEY_HIBERNATE_DIALECT; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.TreeSet; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import org.tonguetied.administration.AdministrationService; import org.tonguetied.administration.ServerData; import org.tonguetied.utils.database.EmbeddedDatabaseServer; /** * Listener that initializes application wide settings when the HTTP server is * first started and tidies up any resources when the servletContext is * destroyed. * * @author bsion * */ public class ServletContextInitializer implements ServletContextListener { private static final String DIR_WEB_INF = "/WEB-INF"; private static final String DIR_SQL = DIR_WEB_INF + "/sql"; // private static final String DIR_SQL_UPDATE = DIR_SQL + "/update"; private static final String BUILD_DATE_FORMAT = "yyyy/MM/dd hh:mm"; private static final String KEY_SUPPORTED_LANGUAGES = "supportedLanguages"; private static final String LANGUAGE_PROPERTIES = "language"; private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(BUILD_DATE_FORMAT); private static final Logger logger = Logger.getLogger(ServletContextInitializer.class); /** * Perform a graceful shutdown of the server servlets. If the server is * running an embedded database, then this is shut down here. * * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) */ public void contextDestroyed(ServletContextEvent event) { Properties props = loadProperties(event.getServletContext(), DIR_WEB_INF + "/jdbc.properties"); final String dialect = props.getProperty(KEY_HIBERNATE_DIALECT); // shutdown the embedded database if it is running if (EmbeddedDatabaseServer.isEmbeddable(dialect)) EmbeddedDatabaseServer.stopDatabase(); } /** * Perform server servlet initialization. When the servlet is first * initialized, the server system properties are set. These server * properties are used in the domain layer, but are not known until the web * application is deployed and started. Also loads application wide * variables including: * <ul> * <li>the list of supported languages</li> * </ul> * * This method also performs database management. It determines if this * server should run an embedded database, ie it is in demo mode, and * starts the database. It also creates the database, the first time it * connects to the server. * * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ public void contextInitialized(ServletContextEvent event) { loadSupportedLanguages(event.getServletContext()); WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(event.getServletContext()); AdministrationService administrationService = (AdministrationService) applicationContext .getBean("administrationService"); Properties props = loadProperties(event.getServletContext(), DIR_WEB_INF + "/jdbc.properties"); final String dialect = props.getProperty(KEY_HIBERNATE_DIALECT); if (EmbeddedDatabaseServer.isEmbeddable(dialect)) EmbeddedDatabaseServer.startDatabase(props); ServerData serverData = administrationService.getLatestData(); // If server data does not exist then assume the database has not been // created if (serverData == null) { if (logger.isDebugEnabled()) logger.debug("attempting to create database"); final String[] schemas = loadSchemas(event.getServletContext(), dialect, administrationService); administrationService.createDatabase(schemas); serverData = createServerData(event.getServletContext()); if (serverData != null) administrationService.saveOrUpdate(serverData); } // check if we need to update else { if (logger.isDebugEnabled()) logger.debug("attempting to update database"); ServerData curServerData = createServerData(event.getServletContext()); if (curServerData.compareTo(serverData) > 0) { // final String[] schemas = loadSchemas(event.getServletContext(), dialect, curServerData.getVersion()); // administrationService.createDatabase(schemas); administrationService.saveOrUpdate(curServerData); } } if (logger.isInfoEnabled()) { logger.info("Charset for this JVM: " + Charset.defaultCharset()); } } /** * Create the list of supported languages used by the web front end and add * as a application scope variable. * * @param context the {@linkplain ServletContext} to add the list of * languages */ private void loadSupportedLanguages(ServletContext context) { // read language.properties if (logger.isInfoEnabled()) logger.info("loading resources from file: " + LANGUAGE_PROPERTIES); final ResourceBundle bundle = ResourceBundle.getBundle(LANGUAGE_PROPERTIES); Set<String> languageKeys = new TreeSet<String>(Collections.list(bundle.getKeys())); if (languageKeys.isEmpty()) { logger.warn("Resource file " + LANGUAGE_PROPERTIES + ".properties contains no entries"); languageKeys.add(Locale.ENGLISH.getLanguage()); } context.setAttribute(KEY_SUPPORTED_LANGUAGES, languageKeys); } /** * Create an instance of a {@link ServerData} object instantiating it from * the system properties. * * @param servletContext * @return an instantiated {@link ServerData} object */ private ServerData createServerData(ServletContext servletContext) { ServerData serverData; try { final ResourceBundle bundle = ResourceBundle.getBundle("buildNumber"); serverData = new ServerData(bundle.getString("version"), bundle.getString("build.number"), DATE_FORMAT.parse(bundle.getString("build.date"))); } catch (ParseException pe) { logger.error("failed to parse date", pe); throw new IllegalStateException("failed to parse date", pe); } return serverData; } /** * Load the SQL schema files used to create the database. * * @param servletContext * @param dialect the SQL dialect * @param administrationService the administration service * @return a string representation of each SQL file */ private String[] loadSchemas(ServletContext servletContext, final String dialect, AdministrationService administrationService) { InputStream is = null; try { List<String> schemas = new ArrayList<String>(); final String schemaFile = DIR_SQL + "/" + administrationService.getSchemaFileName(dialect); is = servletContext.getResourceAsStream(schemaFile); schemas.add(IOUtils.toString(is)); is = servletContext.getResourceAsStream(DIR_SQL + "/initial-data.sql"); schemas.add(IOUtils.toString(is)); return schemas.toArray(new String[schemas.size()]); } catch (IOException ioe) { logger.error("failed to load file", ioe); throw new IllegalStateException("failed to load schemas", ioe); } finally { IOUtils.closeQuietly(is); } } // /** // * // * @param dialectStr the string name of the SQL dialect // * @return // * TODO this seems like the wrong place for this. There really should not // * be a dependency on Hibernate here // */ // private String getUpdateSchema(final String dialectStr, final String version) // { // String schemaFile = null; // final Dialect dialect = DialectFactory.buildDialect(dialectStr); // if (dialect instanceof HSQLDialect) // { // schemaFile = DIR_SQL_UPDATE+"/"+version+"/hsql-update.sql"; // } // else if (dialect instanceof MySQLDialect) // { // schemaFile = DIR_SQL_UPDATE+"/"+version+"/mysql-update.sql"; // } // else if (dialect instanceof PostgreSQLDialect) // { // schemaFile = DIR_SQL_UPDATE+"/"+version+"/postgresql-update.sql"; // } // // return schemaFile; // } /** * Load the jdbc properties for communication with the database. * * @param servletContext * @param fileName the file name and location of the resource to load * * @return the jdbc properties */ private Properties loadProperties(ServletContext servletContext, final String fileName) { InputStream is = null; Properties props = new Properties(); try { is = servletContext.getResourceAsStream(fileName); props.load(is); } catch (IOException ioe) { logger.error("failed to load properties file", ioe); } finally { IOUtils.closeQuietly(is); } return props; } }