org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader Maven / Gradle / Ivy
package org.apache.velocity.runtime.resource.loader;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.util.ExceptionUtils;
import org.apache.velocity.util.StringUtils;
/**
* This is a simple template file loader that loads templates
* from a DataSource instead of plain files.
*
*
It can be configured with a datasource name, a table name,
* id column (name), content column (the template body) and a
* datetime column (for last modification info).
*
*
* Example configuration snippet for velocity.properties:
*
*
* resource.loader = file, ds
*
* ds.resource.loader.public.name = DataSource
* ds.resource.loader.description = Velocity DataSource Resource Loader
* ds.resource.loader.class = org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader
* ds.resource.loader.resource.datasource = java:comp/env/jdbc/Velocity
* ds.resource.loader.resource.table = tb_velocity_template
* ds.resource.loader.resource.keycolumn = id_template
* ds.resource.loader.resource.templatecolumn = template_definition
* ds.resource.loader.resource.timestampcolumn = template_timestamp
* ds.resource.loader.cache = false
* ds.resource.loader.modificationCheckInterval = 60
*
*
Optionally, the developer can instantiate the DataSourceResourceLoader and set the DataSource via code in
* a manner similar to the following:
*
*
* DataSourceResourceLoader ds = new DataSourceResourceLoader();
* ds.setDataSource(DATASOURCE);
* Velocity.setProperty("ds.resource.loader.instance",ds);
*
The property ds.resource.loader.class
should be left out, otherwise all the other
* properties in velocity.properties would remain the same.
*
*
*
* Example WEB-INF/web.xml:
*
*
* Velocity template DataSource
* jdbc/Velocity
* javax.sql.DataSource
* Container
*
*
*
* and Tomcat 4 server.xml file:
* [...]
*
* [...]
*
*
* driverClassName
* org.hsql.jdbcDriver
*
*
* driverName
* jdbc:HypersonicSQL:database
*
*
* user
* database_username
*
*
* password
* database_password
*
*
* [...]
*
* [...]
*
* Example sql script:
* CREATE TABLE tb_velocity_template (
* id_template varchar (40) NOT NULL ,
* template_definition text (16) NOT NULL ,
* template_timestamp datetime NOT NULL
* )
*
* @author Will Glass-Husain
* @author Matt Raible
* @author David Kinnvall
* @author Paulo Gaspar
* @author Sylwester Lachiewicz
* @author Henning P. Schmiedehausen
* @version $Id: DataSourceResourceLoader.java 687177 2008-08-19 22:00:32Z nbubna $
* @since 1.5
*/
public class DataSourceResourceLoader extends ResourceLoader
{
private String dataSourceName;
private String tableName;
private String keyColumn;
private String templateColumn;
private String timestampColumn;
private InitialContext ctx;
private DataSource dataSource;
/**
* @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(org.apache.commons.collections.ExtendedProperties)
*/
public void init(ExtendedProperties configuration)
{
dataSourceName = StringUtils.nullTrim(configuration.getString("resource.datasource"));
tableName = StringUtils.nullTrim(configuration.getString("resource.table"));
keyColumn = StringUtils.nullTrim(configuration.getString("resource.keycolumn"));
templateColumn = StringUtils.nullTrim(configuration.getString("resource.templatecolumn"));
timestampColumn = StringUtils.nullTrim(configuration.getString("resource.timestampcolumn"));
if (dataSource != null)
{
if (log.isDebugEnabled())
{
log.debug("DataSourceResourceLoader: using dataSource instance with table \""
+ tableName + "\"");
log.debug("DataSourceResourceLoader: using columns \""
+ keyColumn + "\", \"" + templateColumn + "\" and \""
+ timestampColumn + "\"");
}
log.trace("DataSourceResourceLoader initialized.");
}
else if (dataSourceName != null)
{
if (log.isDebugEnabled())
{
log.debug("DataSourceResourceLoader: using \"" + dataSourceName
+ "\" datasource with table \"" + tableName + "\"");
log.debug("DataSourceResourceLoader: using columns \""
+ keyColumn + "\", \"" + templateColumn + "\" and \""
+ timestampColumn + "\"");
}
log.trace("DataSourceResourceLoader initialized.");
}
else
{
String msg = "DataSourceResourceLoader not properly initialized. No DataSource was identified.";
log.error(msg);
throw new RuntimeException(msg);
}
}
/**
* Set the DataSource used by this resource loader. Call this as an alternative to
* specifying the data source name via properties.
* @param dataSource The data source for this ResourceLoader.
*/
public void setDataSource(final DataSource dataSource)
{
this.dataSource = dataSource;
}
/**
* @see org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified(org.apache.velocity.runtime.resource.Resource)
*/
public boolean isSourceModified(final Resource resource)
{
return (resource.getLastModified() !=
readLastModified(resource, "checking timestamp"));
}
/**
* @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(org.apache.velocity.runtime.resource.Resource)
*/
public long getLastModified(final Resource resource)
{
return readLastModified(resource, "getting timestamp");
}
/**
* Get an InputStream so that the Runtime can build a
* template with it.
*
* @param name name of template
* @return InputStream containing template
* @throws ResourceNotFoundException
*/
public synchronized InputStream getResourceStream(final String name)
throws ResourceNotFoundException
{
if (org.apache.commons.lang.StringUtils.isEmpty(name))
{
throw new ResourceNotFoundException("DataSourceResourceLoader: Template name was empty or null");
}
Connection conn = null;
ResultSet rs = null;
try
{
conn = openDbConnection();
rs = readData(conn, templateColumn, name);
if (rs.next())
{
InputStream stream = rs.getBinaryStream(templateColumn);
if (stream == null)
{
throw new ResourceNotFoundException("DataSourceResourceLoader: "
+ "template column for '"
+ name + "' is null");
}
return new BufferedInputStream(stream);
}
else
{
throw new ResourceNotFoundException("DataSourceResourceLoader: "
+ "could not find resource '"
+ name + "'");
}
}
catch (SQLException sqle)
{
String msg = "DataSourceResourceLoader: database problem while getting resource '"
+ name + "': ";
log.error(msg, sqle);
throw new ResourceNotFoundException(msg);
}
catch (NamingException ne)
{
String msg = "DataSourceResourceLoader: database problem while getting resource '"
+ name + "': ";
log.error(msg, ne);
throw new ResourceNotFoundException(msg);
}
finally
{
closeResultSet(rs);
closeDbConnection(conn);
}
}
/**
* Fetches the last modification time of the resource
*
* @param resource Resource object we are finding timestamp of
* @param operation string for logging, indicating caller's intention
*
* @return timestamp as long
*/
private long readLastModified(final Resource resource, final String operation)
{
long timeStamp = 0;
/* get the template name from the resource */
String name = resource.getName();
if (name == null || name.length() == 0)
{
String msg = "DataSourceResourceLoader: Template name was empty or null";
log.error(msg);
throw new NullPointerException(msg);
}
else
{
Connection conn = null;
ResultSet rs = null;
try
{
conn = openDbConnection();
rs = readData(conn, timestampColumn, name);
if (rs.next())
{
Timestamp ts = rs.getTimestamp(timestampColumn);
timeStamp = ts != null ? ts.getTime() : 0;
}
else
{
String msg = "DataSourceResourceLoader: could not find resource "
+ name + " while " + operation;
log.error(msg);
throw new ResourceNotFoundException(msg);
}
}
catch (SQLException sqle)
{
String msg = "DataSourceResourceLoader: database problem while "
+ operation + " of '" + name + "': ";
log.error(msg, sqle);
throw ExceptionUtils.createRuntimeException(msg, sqle);
}
catch (NamingException ne)
{
String msg = "DataSourceResourceLoader: database problem while "
+ operation + " of '" + name + "': ";
log.error(msg, ne);
throw ExceptionUtils.createRuntimeException(msg, ne);
}
finally
{
closeResultSet(rs);
closeDbConnection(conn);
}
}
return timeStamp;
}
/**
* Gets connection to the datasource specified through the configuration
* parameters.
*
* @return connection
*/
private Connection openDbConnection() throws NamingException, SQLException
{
if (dataSource != null)
{
return dataSource.getConnection();
}
if (ctx == null)
{
ctx = new InitialContext();
}
dataSource = (DataSource) ctx.lookup(dataSourceName);
return dataSource.getConnection();
}
/**
* Closes connection to the datasource
*/
private void closeDbConnection(final Connection conn)
{
if (conn != null)
{
try
{
conn.close();
}
catch (RuntimeException re)
{
throw re;
}
catch (Exception e)
{
String msg = "DataSourceResourceLoader: problem when closing connection";
log.error(msg, e);
throw new VelocityException(msg, e);
}
}
}
/**
* Closes the result set.
*/
private void closeResultSet(final ResultSet rs)
{
if (rs != null)
{
try
{
rs.close();
}
catch (RuntimeException re)
{
throw re;
}
catch (Exception e)
{
String msg = "DataSourceResourceLoader: problem when closing result set";
log.error(msg, e);
throw new VelocityException(msg, e);
}
}
}
/**
* Reads the data from the datasource. It simply does the following query :
*
* SELECT columnNames FROM tableName WHERE keyColumn
* = 'templateName'
*
* where keyColumn is a class member set in init()
*
* @param conn connection to datasource
* @param columnNames columns to fetch from datasource
* @param templateName name of template to fetch
* @return result set from query
*/
private ResultSet readData(final Connection conn,
final String columnNames,
final String templateName) throws SQLException
{
PreparedStatement ps = conn.prepareStatement("SELECT " + columnNames + " FROM "+ tableName + " WHERE " + keyColumn + " = ?");
ps.setString(1, templateName);
return ps.executeQuery();
}
}