/*
* Copyright 2002-2005 the original author or 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 info.jtrac.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Properties;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.context.ServletContextAware;
/**
* <p>
* Custom extension of the Spring PropertyPlaceholderConfigurer that
* sets up the jtrac.home System property (creates if required) and also creates
* a default jtrac.properties file for HSQLDB - useful for those who want
* to quickly evaluate JTrac. Just dropping the war into a servlet container
* would work without the need to even configure a datasource.
* </p>
* <p>
* This class would effectively do nothing if a <code>jtrac.properties</code> file exists in jtrac.home
* </p>
* <ol>
* <li>A "jtrac.home" property is looked for in <code>/WEB-INF/classes/jtrac-init.properties</code></li>
* <li>if not found, then a <code>jtrac.home</code> system property is checked for</li>
* <li>then a servlet context init-parameter called <code>jtrac.home</code> is looked for</li>
* <li>last resort, a <code>.jtrac</code> folder is created in the <code>user.home</code> and used as <code>jtrac.home</code></li>
* </ol>
*
* <p>
* Other tasks
* </p>
* <ul>
* <li>initialize the "test" query for checking idle database connections</li>
* <li>initialize list of available locales based on the properties files available</li>
* </ul>
*
* <p>
* Also playing an important role during startup are the following factory beans:
* </p>
* <ul>
* <li>DataSourceFactoryBean:</li>
* <ul>
* <li>switches between embedded HSQLDB or Apache DBCP (connection pool)</li>
* <li>performs graceful shutdown of database if embedded HSQLDB</li>
* </ul>
* <li>ProviderManagerFactoryBean</li>
* <ul>
* <li>conditionally includes LDAP authentication if requested</li>
* </ul>
* </ul>
*
* <p>
* Note that later on during startup, the HibernateJtracDao would check if
* database tables exist, and if they don't, would proceed to create them.
* </p>
*/
public class JtracConfigurer extends PropertyPlaceholderConfigurer implements ServletContextAware {
private ServletContext servletContext;
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// do our custom configuration before spring gets a chance to
try {
configureJtrac();
} catch(Exception e) {
throw new BeanCreationException("JtracConfigurer failed", e);
}
super.postProcessBeanFactory(beanFactory);
}
private void configureJtrac() throws Exception {
String jtracHome = null;
ClassPathResource jtracInitResource = new ClassPathResource("jtrac-init.properties");
// jtrac-init.properties assumed to exist
Properties props = loadProps(jtracInitResource.getFile());
logger.info("found 'jtrac-init.properties' on classpath, processing...");
jtracHome = props.getProperty("jtrac.home");
if (jtracHome != null) {
logger.info("'jtrac.home' property initialized from 'jtrac-init.properties' as '" + jtracHome + "'");
}
//======================================================================
FilenameFilter ff = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith("messages_") && name.endsWith(".properties");
}
};
File[] messagePropsFiles = jtracInitResource.getFile().getParentFile().listFiles(ff);
String locales = "en";
for(File f : messagePropsFiles) {
int endIndex = f.getName().indexOf('.');
String localeCode = f.getName().substring(9, endIndex);
locales += "," + localeCode;
}
logger.info("locales available configured are '" + locales + "'");
props.setProperty("jtrac.locales", locales);
//======================================================================
if (jtracHome == null) {
logger.info("valid 'jtrac.home' property not available in 'jtrac-init.properties', trying system properties.");
jtracHome = System.getProperty("jtrac.home");
if (jtracHome != null) {
logger.info("'jtrac.home' property initialized from system properties as '" + jtracHome + "'");
}
}
if (jtracHome == null) {
logger.info("valid 'jtrac.home' property not available in system properties, trying servlet init paramters.");
jtracHome = servletContext.getInitParameter("jtrac.home");
if (jtracHome != null) {
logger.info("Servlet init parameter 'jtrac.home' exists: '" + jtracHome + "'");
}
}
if (jtracHome == null) {
jtracHome = System.getProperty("user.home") + "/.jtrac";
logger.warn("Servlet init paramter 'jtrac.home' does not exist. Will use 'user.home' directory '" + jtracHome + "'");
}
//======================================================================
File homeFile = new File(jtracHome);
if (!homeFile.exists()) {
homeFile.mkdir();
logger.info("directory does not exist, created '" + homeFile.getPath() + "'");
if (!homeFile.exists()) {
String message = "invalid path '" + homeFile.getAbsolutePath() + "', try creating this directory first. Aborting.";
logger.error(message);
throw new Exception(message);
}
} else {
logger.info("directory already exists: '" + homeFile.getPath() + "'");
}
props.setProperty("jtrac.home", homeFile.getAbsolutePath());
//======================================================================
File attachmentsFile = new File(jtracHome + "/attachments");
if (!attachmentsFile.exists()) {
attachmentsFile.mkdir();
logger.info("directory does not exist, created '" + attachmentsFile.getPath() + "'");
} else {
logger.info("directory already exists: '" + attachmentsFile.getPath() + "'");
}
File indexesFile = new File(jtracHome + "/indexes");
if (!indexesFile.exists()) {
indexesFile.mkdir();
logger.info("directory does not exist, created '" + indexesFile.getPath() + "'");
} else {
logger.info("directory already exists: '" + indexesFile.getPath() + "'");
}
//======================================================================
File propsFile = new File(homeFile.getPath() + "/jtrac.properties");
if (!propsFile.exists()) {
propsFile.createNewFile();
logger.info("properties file does not exist, created '" + propsFile.getPath() + "'");
OutputStream os = new FileOutputStream(propsFile);
Writer out = new PrintWriter(os);
try {
out.write("database.driver=org.hsqldb.jdbcDriver\n");
out.write("database.url=jdbc:hsqldb:file:${jtrac.home}/db/jtrac\n");
out.write("database.username=sa\n");
out.write("database.password=\n");
out.write("hibernate.dialect=org.hibernate.dialect.HSQLDialect\n");
out.write("hibernate.show_sql=false\n");
} finally {
out.close();
os.close();
}
logger.info("HSQLDB will be used. Finished creating '" + propsFile.getPath() + "'");
} else {
logger.info("'jtrac.properties' file exists: '" + propsFile.getPath() + "'");
}
//======================================================================
String version = "0.0.0";
String timestamp = "0000";
ClassPathResource versionResource = new ClassPathResource("jtrac-version.properties");
if(versionResource.exists()) {
logger.info("found 'jtrac-version.properties' on classpath, processing...");
Properties versionProps = loadProps(versionResource.getFile());
version = versionProps.getProperty("version");
timestamp = versionProps.getProperty("timestamp");
} else {
logger.info("did not find 'jtrac-version.properties' on classpath");
}
logger.info("jtrac.version = '" + version + "'");
logger.info("jtrac.timestamp = '" + timestamp + "'");
props.setProperty("jtrac.version", version);
props.setProperty("jtrac.timestamp", timestamp);
/*
* TODO: A better way (default value) to check the database should be used for Apache DBCP.
* The current "SELECT...FROM DUAL" only works on Oracle (and MySQL).
* Other databases also support "SELECT 1+1" as query
* (e.g. PostgreSQL, Hypersonic 2 (H2), MySQL, etc.).
*/
props.setProperty("database.validationQuery", "SELECT 1 FROM DUAL");
props.setProperty("ldap.url", "");
props.setProperty("ldap.activeDirectoryDomain", "");
props.setProperty("ldap.searchBase", "");
props.setProperty("database.datasource.jndiname", "");
// set default properties that can be overridden by user if required
setProperties(props);
// finally set the property that spring is expecting, manually
FileSystemResource fsr = new FileSystemResource(propsFile);
setLocation(fsr);
}
private Properties loadProps(File file) throws Exception {
InputStream is = null;
Properties props = new Properties();
try {
is = new FileInputStream(file);
props.load(is);
} finally {
is.close();
}
return props;
}
}