/*
* Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: DatabaseContent.java 3918 2008-04-14 17:35:35Z gbevin $
*/
package com.uwyn.rife.cmf.dam.contentmanagers;
import com.uwyn.rife.database.*;
import com.uwyn.rife.database.queries.*;
import com.uwyn.rife.cmf.Content;
import com.uwyn.rife.cmf.ContentRepository;
import com.uwyn.rife.cmf.MimeType;
import com.uwyn.rife.cmf.dam.ContentDataUser;
import com.uwyn.rife.cmf.dam.ContentManager;
import com.uwyn.rife.cmf.dam.ContentStore;
import com.uwyn.rife.cmf.dam.contentmanagers.exceptions.InstallContentErrorException;
import com.uwyn.rife.cmf.dam.contentmanagers.exceptions.RemoveContentErrorException;
import com.uwyn.rife.cmf.dam.contentmanagers.exceptions.UnknownContentRepositoryException;
import com.uwyn.rife.cmf.dam.contentmanagers.exceptions.UnsupportedMimeTypeException;
import com.uwyn.rife.cmf.dam.contentstores.DatabaseImageStoreFactory;
import com.uwyn.rife.cmf.dam.contentstores.DatabaseRawStoreFactory;
import com.uwyn.rife.cmf.dam.contentstores.DatabaseTextStoreFactory;
import com.uwyn.rife.cmf.dam.exceptions.ContentManagerException;
import com.uwyn.rife.cmf.transform.ContentTransformer;
import com.uwyn.rife.database.exceptions.DatabaseException;
import com.uwyn.rife.datastructures.Pair;
import com.uwyn.rife.engine.ElementSupport;
import com.uwyn.rife.tools.exceptions.InnerClassException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
public abstract class DatabaseContent extends DbQueryManager implements ContentManager
{
protected ArrayList<ContentStore> mStores = null;
protected HashMap<MimeType, ContentStore> mMimeMapping = null;
public DatabaseContent(Datasource datasource)
{
super(datasource);
mStores = new ArrayList<ContentStore>();
mStores.add(DatabaseTextStoreFactory.getInstance(getDatasource()));
mStores.add(DatabaseImageStoreFactory.getInstance(getDatasource()));
mStores.add(DatabaseRawStoreFactory.getInstance(getDatasource()));
mMimeMapping = new HashMap<MimeType, ContentStore>();
for (ContentStore store : mStores)
{
for (MimeType mime_type : store.getSupportedMimeTypes())
{
mMimeMapping.put(mime_type, store);
}
}
}
public abstract DatabaseContentInfo getContentInfo(String location) throws ContentManagerException;
protected boolean _install(CreateSequence createSequenceContentRepository, CreateSequence createSequenceContentInfo,
CreateTable createTableContentRepository, CreateTable createTableContentInfo, CreateTable createTableContentAttribute, CreateTable createTableContentProperty)
throws ContentManagerException
{
assert createSequenceContentRepository != null;
assert createSequenceContentInfo != null;
assert createTableContentRepository != null;
assert createTableContentInfo != null;
assert createTableContentAttribute != null;
assert createTableContentProperty != null;
try
{
executeUpdate(createSequenceContentRepository);
executeUpdate(createSequenceContentInfo);
executeUpdate(createTableContentRepository);
executeUpdate(createTableContentInfo);
executeUpdate(createTableContentAttribute);
executeUpdate(createTableContentProperty);
createRepository(ContentRepository.DEFAULT);
for (ContentStore store : mStores)
{
store.install();
}
}
catch (DatabaseException e)
{
throw new InstallContentErrorException(e);
}
return true;
}
protected boolean _remove(DropSequence dropSequenceContentRepository, DropSequence dropSequenceContentInfo,
DropTable dropTableContentRepository, DropTable dropTableContentInfo, DropTable dropTableContentAttribute, DropTable dropTableContentProperty)
throws ContentManagerException
{
assert dropSequenceContentRepository != null;
assert dropSequenceContentInfo != null;
assert dropTableContentRepository != null;
assert dropTableContentInfo != null;
assert dropTableContentAttribute != null;
assert dropTableContentProperty != null;
try
{
for (ContentStore store : mStores)
{
store.remove();
}
executeUpdate(dropTableContentProperty);
executeUpdate(dropTableContentAttribute);
executeUpdate(dropTableContentInfo);
executeUpdate(dropTableContentRepository);
executeUpdate(dropSequenceContentInfo);
executeUpdate(dropSequenceContentRepository);
}
catch (DatabaseException e)
{
throw new RemoveContentErrorException(e);
}
return true;
}
protected String getValueColumnName()
{
return "value";
}
protected boolean _createRepository(final SequenceValue getContentRepositoryId, final Insert storeContentRepository, final String name)
throws ContentManagerException
{
if (null == name) throw new IllegalArgumentException("name can't be null");
if (0 == name.length()) throw new IllegalArgumentException("name can't be empty");
assert getContentRepositoryId != null;
assert storeContentRepository != null;
Boolean result = null;
try
{
result = inTransaction(new DbTransactionUser() {
public Boolean useTransaction() throws InnerClassException
{
// get new repository id
final int id = executeGetFirstInt(getContentRepositoryId);
// store the content
return executeUpdate(storeContentRepository, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("repositoryId", id)
.setString("name", name);
}
}) > 0;
}
});
}
catch (InnerClassException e)
{
throw (ContentManagerException)e.getCause();
}
return result != null && result.booleanValue();
}
protected boolean _containsRepository(final Select containsContentRepository, final String name)
throws ContentManagerException
{
if (null == name) throw new IllegalArgumentException("name can't be null");
assert containsContentRepository != null;
final String repository;
if (0 == name.length())
{
repository = ContentRepository.DEFAULT;
}
else
{
repository = name;
}
return executeGetFirstInt(containsContentRepository, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setString("name", repository);
}
}) > 0;
}
protected Pair<String, String> splitLocation(String location)
{
if (null == location) throw new IllegalArgumentException("location can't be null");
if (0 == location.length()) throw new IllegalArgumentException("location can't be empty");
int colon_index = location.indexOf(":");
String repository = null;
String path = null;
if (colon_index != -1)
{
repository = location.substring(0, colon_index);
path = location.substring(colon_index+1);
}
else
{
path = location;
}
if (null == repository ||
0 == repository.length())
{
repository = ContentRepository.DEFAULT;
}
if (0 == path.length()) throw new IllegalArgumentException("path can't be empty");
if (!path.startsWith("/")) throw new IllegalArgumentException("path needs to be absolute");
return new Pair<String, String>(repository, path);
}
protected boolean _storeContent(final SequenceValue getContentId, final Select getContentRepositoryId, final Insert storeContentInfo, final Insert storeContentAttribute, final Insert storeContentProperty, String location, final Content content, final ContentTransformer transformer)
throws ContentManagerException
{
if (null == content) throw new IllegalArgumentException("content can't be null");
final Pair<String, String> split_location = splitLocation(location);
assert getContentId != null;
assert getContentRepositoryId != null;
assert storeContentInfo != null;
assert storeContentAttribute != null;
assert storeContentProperty != null;
final ContentStore store = mMimeMapping.get(content.getMimeType());
if (null == store)
{
throw new UnsupportedMimeTypeException(content.getMimeType());
}
Boolean result = null;
try
{
result = inTransaction(new DbTransactionUser() {
public Boolean useTransaction() throws InnerClassException
{
// get new content id
final int id = executeGetFirstInt(getContentId);
// get repository id
final int repository_id = executeGetFirstInt(getContentRepositoryId, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setString("repository", split_location.getFirst());
}
});
// verify the existance of the repository
if (-1 == repository_id)
{
throwException(new UnknownContentRepositoryException(split_location.getFirst()));
}
// store the content
if (executeUpdate(storeContentInfo, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", id)
.setInt("repositoryId", repository_id)
.setString("path", split_location.getSecond())
.setString("mimeType", content.getMimeType().toString())
.setBoolean("fragment", content.isFragment());
if (content.hasName())
{
statement
.setString("name", content.getName());
}
else
{
statement
.setNull("name", Types.VARCHAR);
}
}
}) > 0)
{
// store the attributes if there are some
if (content.hasAttributes())
{
for (Map.Entry<String, String> attribute : content.getAttributes().entrySet())
{
final String name = attribute.getKey();
final String value = attribute.getValue();
executeUpdate(storeContentAttribute, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", id)
.setString("name", name)
.setString(getValueColumnName(), value);
}
});
}
}
// put the actual content data in the content store
try
{
if (!store.storeContentData(id, content, transformer))
{
rollback();
}
}
catch (ContentManagerException e)
{
throwException(e);
}
// store the content data properties if there are some
if (content.hasProperties())
{
for (Map.Entry<String, String> property : content.getProperties().entrySet())
{
final String name = property.getKey();
final String value = property.getValue();
executeUpdate(storeContentProperty, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", id)
.setString("name", name)
.setString(getValueColumnName(), value);
}
});
}
}
return true;
}
return false;
}
});
}
catch (InnerClassException e)
{
throw (ContentManagerException)e.getCause();
}
return result != null && result.booleanValue();
}
protected boolean _deleteContent(final Select getContentInfo, final Delete deleteContentInfo, final Delete deleteContentAttributes, final Delete deleteContentProperties, String location)
throws ContentManagerException
{
final Pair<String, String> split_location = splitLocation(location);
assert getContentInfo != null;
assert deleteContentInfo != null;
assert deleteContentAttributes != null;
assert deleteContentProperties != null;
Boolean result = null;
try
{
result = inTransaction(new DbTransactionUser() {
public Boolean useTransaction() throws InnerClassException
{
return executeFetchAll(getContentInfo, new DbRowProcessor() {
public boolean processRow(ResultSet resultSet)
throws SQLException
{
final int contentid = resultSet.getInt("contentId");
MimeType mimetype = MimeType.getMimeType(resultSet.getString("mimeType"));
ContentStore store = mMimeMapping.get(mimetype);
if (null == store)
{
throw new UnsupportedMimeTypeException(mimetype);
}
if (!store.deleteContentData(contentid))
{
rollback();
}
executeUpdate(deleteContentAttributes, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", contentid);
}
});
executeUpdate(deleteContentProperties, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", contentid);
}
});
if (0 == executeUpdate(deleteContentInfo, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", contentid);
}
}))
{
rollback();
}
return true;
}
}, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setString("repository", split_location.getFirst())
.setString("path", split_location.getSecond());
}
});
}
});
}
catch (InnerClassException e)
{
throw (ContentManagerException)e.getCause();
}
return result != null && result.booleanValue();
}
private Pair<String, String> splitPath(String path)
{
assert path != null;
int slash_index = path.lastIndexOf('/');
String path_part = path.substring(0, slash_index);
String name_part = path.substring(slash_index+1);
return new Pair<String, String>(path_part, name_part);
}
protected <ResultType> ResultType _useContentData(Select retrieveContent, String location, ContentDataUser user)
throws ContentManagerException
{
if (null == user) throw new IllegalArgumentException("user can't be null");
final Pair<String, String> split_location = splitLocation(location);
assert retrieveContent != null;
DatabaseContentInfo content_info = executeFetchFirstBean(retrieveContent, DatabaseContentInfo.class, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
Pair<String, String> path_parts = splitPath(split_location.getSecond());
statement
.setString("repository", split_location.getFirst())
.setString("path", split_location.getSecond())
.setString("pathpart", path_parts.getFirst())
.setString("namepart", path_parts.getSecond());
}
});
if (null == content_info)
{
return null;
}
MimeType mime_type = MimeType.getMimeType(content_info.getMimeType());
ContentStore store = mMimeMapping.get(mime_type);
if (null == store)
{
throw new UnsupportedMimeTypeException(mime_type);
}
return (ResultType)store.useContentData(content_info.getContentId(), user);
}
protected boolean _hasContentData(Select retrieveContent, String location)
throws ContentManagerException
{
final Pair<String, String> split_location = splitLocation(location);
assert retrieveContent != null;
DatabaseContentInfo content_info = executeFetchFirstBean(retrieveContent, DatabaseContentInfo.class, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
Pair<String, String> path_parts = splitPath(split_location.getSecond());
statement
.setString("repository", split_location.getFirst())
.setString("path", split_location.getSecond())
.setString("pathpart", path_parts.getFirst())
.setString("namepart", path_parts.getSecond());
}
});
if (null == content_info)
{
return false;
}
MimeType mime_type = MimeType.getMimeType(content_info.getMimeType());
ContentStore store = mMimeMapping.get(mime_type);
if (null == store)
{
throw new UnsupportedMimeTypeException(mime_type);
}
return store.hasContentData(content_info.getContentId());
}
protected DatabaseContentInfo _getContentInfo(Select getContentInfo, Select getContentAttributes, Select getContentProperties, String location)
throws ContentManagerException
{
final Pair<String, String> split_location = splitLocation(location);
assert getContentInfo != null;
assert getContentAttributes != null;
assert getContentProperties != null;
final DatabaseContentInfo content_info = executeFetchFirstBean(getContentInfo, DatabaseContentInfo.class, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
Pair<String, String> path_parts = splitPath(split_location.getSecond());
statement
.setString("repository", split_location.getFirst())
.setString("path", split_location.getSecond())
.setString("pathpart", path_parts.getFirst())
.setString("namepart", path_parts.getSecond());
}
});
if (content_info != null)
{
// get the content attributes
ContentAttributesProcessor processor_attributes = new ContentAttributesProcessor();
executeFetchAll(getContentAttributes, processor_attributes, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", content_info.getContentId());
}
}
);
content_info.setAttributes(processor_attributes.getAttributes());
// get the content data properties
ContentPropertiesProcessor processor_properties = new ContentPropertiesProcessor();
executeFetchAll(getContentProperties, processor_properties, new DbPreparedStatementHandler() {
public void setParameters(DbPreparedStatement statement)
{
statement
.setInt("contentId", content_info.getContentId());
}
}
);
content_info.setProperties(processor_properties.getProperties());
// retrieve the content store
MimeType mime_type = MimeType.getMimeType(content_info.getMimeType());
ContentStore store = mMimeMapping.get(mime_type);
if (null == store)
{
throw new UnsupportedMimeTypeException(mime_type);
}
// retrieve the content size
content_info.setSize(store.getSize(content_info.getContentId()));
}
return content_info;
}
protected void _serveContentData(ElementSupport element, final String location)
throws ContentManagerException
{
if (null == element) throw new IllegalArgumentException("element can't be null.");
try
{
splitLocation(location);
}
catch (IllegalArgumentException e)
{
element.defer();
return;
}
DatabaseContentInfo content_info = null;
try
{
content_info = getContentInfo(location);
}
catch (IllegalArgumentException e)
{
element.defer();
return;
}
if (null == content_info)
{
element.defer();
return;
}
// retrieve the content store
MimeType mime_type = MimeType.getMimeType(content_info.getMimeType());
ContentStore store = mMimeMapping.get(mime_type);
if (null == store)
{
throw new UnsupportedMimeTypeException(mime_type);
}
// set cache headers
long if_modified_since = element.getDateHeader("If-Modified-Since");
Timestamp last_modified = content_info.getCreated();
long last_modified_timestamp = (last_modified.getTime() /1000)*1000;
if (if_modified_since > 0 &&
if_modified_since >= last_modified_timestamp)
{
element.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
// set general headers
element.setContentType(store.getContentType(content_info));
if (content_info.hasName())
{
element.addHeader("Content-Disposition", "inline; filename="+content_info.getName());
}
element.addHeader("Cache-Control", "must-revalidate");
element.addDateHeader("Expires", System.currentTimeMillis()+60*60*1000);
element.addDateHeader("Last-Modified", last_modified_timestamp);
store.serveContentData(element, content_info.getContentId());
}
protected String _getContentForHtml(String location, ElementSupport element, String serveContentExitName)
throws ContentManagerException
{
DatabaseContentInfo content_info = null;
try
{
content_info = getContentInfo(location);
}
catch (IllegalArgumentException e)
{
return "";
}
if (null == content_info)
{
return "";
}
// retrieve the content store
MimeType mime_type = MimeType.getMimeType(content_info.getMimeType());
ContentStore store = mMimeMapping.get(mime_type);
if (null == store)
{
throw new UnsupportedMimeTypeException(mime_type);
}
return store.getContentForHtml(content_info.getContentId(), content_info, element, serveContentExitName);
}
private class ContentAttributesProcessor extends DbRowProcessor
{
private Map<String, String> mAttributes = null;
public boolean processRow(ResultSet result)
throws SQLException
{
if (null == mAttributes)
{
mAttributes = new HashMap<String, String>();
}
mAttributes.put(result.getString("name"), result.getString(getValueColumnName()));
return true;
}
public Map<String, String> getAttributes()
{
return mAttributes;
}
}
private class ContentPropertiesProcessor extends DbRowProcessor
{
private Map<String, String> mProperties = null;
public boolean processRow(ResultSet result)
throws SQLException
{
if (null == mProperties)
{
mProperties = new HashMap<String, String>();
}
mProperties.put(result.getString("name"), result.getString(getValueColumnName()));
return true;
}
public Map<String, String> getProperties()
{
return mProperties;
}
}
}