[New] Initial commit of the XML Persistence implementation

This is a rudimentary implementation which works in that objects can be written to the DB, read back and helper methods like keySet, size are also implemented
This commit is contained in:
Robert von Burg 2012-06-08 19:04:35 +02:00
parent b8ece51dbe
commit 4956925329
13 changed files with 1693 additions and 0 deletions

3
.gitignore vendored
View File

@ -4,3 +4,6 @@
*.jar
*.war
*.ear
# Project files
tmp

12
config/log4j.properties Normal file
View File

@ -0,0 +1,12 @@
log4j.rootLogger = info, stdout, file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] %C{1} %M - %m%n
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=${user.dir}/logs/app.log
log4j.appender.file.MaxFileSize=10000KB
log4j.appender.file.MaxBackupIndex=0
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %5p [%t] %C{1} %M - %m%n

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2010 - 2011
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
package ch.eitchnet.xmlpers;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.ContentHandler;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public interface XmlDao<T> {
/**
* @param object
* @return
*/
public String getType(T object);
/**
* @param object
* @return
*/
public String getSubType(T object);
/**
* @param object
* @return
*/
public String getId(T object);
/**
*
* @param object
* @param domImplementation
* @return
*/
// XXX think about returning a document, instead of an element, or use document as input
public Document serializeToDom(T object, DOMImplementation domImplementation);
/**
* @param element
* @return
*/
public T parseFromDom(Element element);
/**
* @param object
* @param contentHandler
*/
// XXX Use the XMLSerializer object for serializing to SAX...
public void serializeToSax(T object, ContentHandler contentHandler);
// XXX parse from SAX is missing...
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2010 - 2011
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
package ch.eitchnet.xmlpers;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public interface XmlDaoFactory {
public <T> XmlDao<T> getDao(String type);
}

View File

@ -0,0 +1,428 @@
package ch.eitchnet.xmlpers;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import ch.eitchnet.utils.helper.FileHelper;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
/**
*@author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class XmlFilePersister {
//
private static final String XML_DEFAULT_ENCODING = "UTF-8";
private static final Logger logger = Logger.getLogger(XmlFilePersister.class);
private boolean verbose;
private XmlPersistencePathBuilder xmlPathHelper;
/**
* @param xmlPathHelper
* @param verbose
*/
public XmlFilePersister(XmlPersistencePathBuilder xmlPathHelper, boolean verbose) {
this.xmlPathHelper = xmlPathHelper;
this.verbose = verbose;
}
/**
* @param type
* @param subType
* @param id
* @param document
*/
public void saveOrUpdate(String type, String subType, String id, Document document) {
File pathF;
if (subType != null)
pathF = xmlPathHelper.getPathF(type, subType, id);
else
pathF = xmlPathHelper.getPathF(type, id);
// if this is a new file, then check create parents, if the don't exist
if (!pathF.exists()) {
File parentFile = pathF.getParentFile();
if (!parentFile.exists() && !parentFile.mkdirs()) {
throw new XmlPersistenceExecption("Could not create path for " + type + " / " + subType + " / " + id
+ " at " + pathF.getAbsolutePath());
}
}
if (verbose)
logger.info("Persisting " + type + " / " + subType + " / " + id + " to " + pathF.getAbsolutePath() + "...");
BufferedOutputStream outStream = null;
try {
outStream = new BufferedOutputStream(new FileOutputStream(pathF));
OutputFormat outputFormat = new OutputFormat("XML", XML_DEFAULT_ENCODING, true);
outputFormat.setIndent(1);
outputFormat.setIndenting(true);
//of.setDoctype(null, null);
XMLSerializer serializer = new XMLSerializer(outStream, outputFormat);
serializer.asDOMSerializer();
serializer.serialize(document);
outStream.flush();
} catch (Exception e) {
throw new XmlPersistenceExecption("Could not persist object " + type + " / " + subType + " / " + id
+ " to " + pathF.getAbsolutePath(), e);
} finally {
if (outStream != null) {
try {
outStream.close();
} catch (IOException e) {
logger.error(e, e);
}
}
}
if (verbose)
logger.info("Done.");
}
/**
* @param type
* @param subType
* @param id
*/
public void remove(String type, String subType, String id) {
File pathF;
if (subType != null)
pathF = xmlPathHelper.getPathF(type, subType, id);
else
pathF = xmlPathHelper.getPathF(type, id);
if (verbose)
logger.info("Remove persistence file for " + type + " / " + subType + " / " + id + " from "
+ pathF.getAbsolutePath() + "...");
if (!pathF.exists()) {
logger.error("Persistence file for " + type + " / " + subType + " / " + id + " does not exist at "
+ pathF.getAbsolutePath());
} else if (!pathF.delete()) {
throw new XmlPersistenceExecption("Could not delete persistence file for " + type + " / " + subType + " / "
+ id + " at " + pathF.getAbsolutePath());
}
if (verbose)
logger.info("Done.");
}
/**
* @param type
* @param subType
*/
public void removeAll(String type, String subType) {
File pathF;
if (subType == null)
pathF = xmlPathHelper.getPathF(type);
else
pathF = xmlPathHelper.getPathF(type, subType);
if (!pathF.exists()) {
if (subType == null)
logger.error("Path for " + type + " at " + pathF.getAbsolutePath()
+ " does not exist, so removing not possible!");
else
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so removing not possible!");
} else {
File[] filesToRemove = pathF.listFiles();
boolean removed = FileHelper.deleteFiles(filesToRemove, verbose);
if (!removed) {
if (subType == null)
throw new XmlPersistenceExecption("Could not delete persistence files for " + type + " at "
+ pathF.getAbsolutePath());
throw new XmlPersistenceExecption("Could not delete persistence files for " + type + " / " + subType
+ " at " + pathF.getAbsolutePath());
}
}
}
/**
*
* @param type
* @param subType
*
* @return
*/
public Set<String> queryKeySet(String type, String subType) {
// if a sub type is required, then it's simple:
if (subType != null) {
File pathF = this.xmlPathHelper.getPathF(type, subType);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so no objects exist!");
return Collections.emptySet();
}
Set<String> keySet = new HashSet<String>();
for (File f : pathF.listFiles()) {
String name = f.getName();
keySet.add(name.substring(0, name.length() - XmlPersistencePathBuilder.FILE_EXT.length()));
}
if (verbose)
logger.info("Found " + keySet.size() + " elements for " + type + " / " + subType);
return keySet;
}
// otherwise we need to iterate any existing subTypes and create a combined key set
File pathF = this.xmlPathHelper.getPathF(type);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so no objects exist!");
return Collections.emptySet();
}
Set<String> keySet = new HashSet<String>();
File[] subTypeFiles = pathF.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isFile()) {
keySet.add(xmlPathHelper.getId(subTypeFile.getName()));
} else {
for (File f : subTypeFile.listFiles()) {
keySet.add(xmlPathHelper.getId(f.getName()));
}
}
}
if (verbose)
logger.info("Found " + keySet.size() + " elements for " + type);
return keySet;
}
/**
*
* @param type
* @param subType
*
* @return
*/
public long querySize(String type, String subType) {
// if a sub type is required, then it's simple:
if (subType != null) {
File pathF = this.xmlPathHelper.getPathF(type, subType);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so no objects exist!");
return 0l;
}
int length = pathF.listFiles().length;
if (verbose)
logger.info("Found " + length + " elements for " + type + " / " + subType);
return length;
}
// otherwise we need to iterate any existing sub types and
// return the size of the combined collection
File pathF = this.xmlPathHelper.getPathF(type);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so no objects exist!");
return 0l;
}
long numberOfFiles = 0l;
File[] subTypeFiles = pathF.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isFile()) {
numberOfFiles++;
} else {
numberOfFiles += subTypeFile.listFiles().length;
}
}
if (verbose)
logger.info("Found " + numberOfFiles + " elements for " + type);
return numberOfFiles;
}
// XXX think about allowing paged loading...
// /**
// * @param type
// * @param subType
// * @param firstResult
// * @param maxResults
// *
// * @return
// */
// public List<Element> queryFrom(String type, String subType, int firstResult, int maxResults) {
//
// File pathF = this.xmlPathHelper.getPathF(type, subType);
// if (!pathF.exists()) {
// logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
// + " does not exist, so no objects exist!");
// return Collections.emptyList();
// }
//
// File[] listFiles = pathF.listFiles();
// Arrays.sort(listFiles, new Comparator<File>() {
//
// @Override
// public int compare(File file1, File file2) {
// return file1.getName().compareTo(file2.getName());
// }
// });
//
// // make sure positions are not illegal
// int size = listFiles.length;
// if (firstResult >= size)
// return Collections.emptyList();
//
// if ((firstResult + maxResults) > size)
// maxResults = size - firstResult;
//
// File[] result = Arrays.copyOfRange(listFiles, firstResult, firstResult + maxResults);
//
// List<T> list = new ArrayList<T>();
// for (File f : result) {
// list.add(loadObject(clazz, f));
// }
//
// return list;
// }
/**
*
* @param type
* @param subType
*
* @return
*/
public List<Element> queryAll(String type, String subType, DocumentBuilder docBuilder) {
// if a sub type is required, then it's simple:
if (subType != null) {
File pathF = this.xmlPathHelper.getPathF(type, subType);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so no objects exist!");
return Collections.emptyList();
}
List<Element> list = new ArrayList<Element>();
for (File subTypeF : pathF.listFiles()) {
list.add(parseFile(subTypeF, docBuilder));
}
if (verbose)
logger.info("Loaded " + list.size() + " elements for " + type + " / " + subType);
return list;
}
// otherwise we need to iterate any existing sub types and
// return those elements as well
File pathF = this.xmlPathHelper.getPathF(type);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " at " + pathF.getAbsolutePath()
+ " does not exist, so no objects exist!");
return Collections.emptyList();
}
List<Element> list = new ArrayList<Element>();
File[] subTypeFiles = pathF.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isFile()) {
list.add(parseFile(subTypeFile, docBuilder));
} else {
for (File subTypeF : subTypeFile.listFiles()) {
list.add(parseFile(subTypeF, docBuilder));
}
}
}
if (verbose)
logger.info("Loaded " + list.size() + " elements for " + type);
return list;
}
/**
*
* @param type
* @param subType
* @param id
*
* @return
*/
public Element queryById(String type, String subType, String id, DocumentBuilder docBuilder) {
File pathF = this.xmlPathHelper.getPathF(type, subType, id);
if (!pathF.exists()) {
logger.error("Path for " + type + " / " + subType + " / " + id + " at " + pathF.getAbsolutePath()
+ " does not exist, so object does not exist!");
return null;
}
return parseFile(pathF, docBuilder);
}
/**
* @param subTypeF
* @return
*/
private Element parseFile(File subTypeF, DocumentBuilder docBuilder) {
try {
Document document = docBuilder.parse(subTypeF);
return document.getDocumentElement();
} catch (SAXException e) {
throw new XmlPersistenceExecption("Failed to parse file " + subTypeF.getAbsolutePath(), e);
} catch (IOException e) {
throw new XmlPersistenceExecption("Failed to read file " + subTypeF.getAbsolutePath(), e);
}
}
}

View File

@ -0,0 +1,23 @@
package ch.eitchnet.xmlpers;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class XmlPersistenceExecption extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* @param message
* @param cause
*/
public XmlPersistenceExecption(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
*/
public XmlPersistenceExecption(String message) {
super(message);
}
}

View File

@ -0,0 +1,120 @@
package ch.eitchnet.xmlpers;
import org.apache.log4j.Logger;
import ch.eitchnet.utils.helper.SystemHelper;
import ch.eitchnet.utils.objectfilter.ITransactionObject;
import ch.eitchnet.utils.objectfilter.ObjectFilter;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class XmlPersistenceHandler {
/**
*
*/
public static final String CONFIG_VERBOSE = "ch.eitchnet.xmlpers.config.verbose";
/**
*
*/
public static final String CONFIG_BASEPATH = "ch.eitchnet.xmlpers.config.basepath";
/**
*
*/
public static final String CONFIG_DAO_FACTORY_CLASS = "ch.eitchnet.xmlpers.config.daoFactoryClass";
protected static final Logger logger = Logger.getLogger(XmlPersistenceHandler.class);
protected boolean verbose;
protected ThreadLocal<XmlPersistenceTransaction> xmlPersistenceTxThreadLocal;
protected XmlFilePersister persister;
protected XmlDaoFactory xmlDaoFactory;
/**
*
*/
public void initialize() {
String basePath = SystemHelper.getProperty(XmlPersistenceHandler.class.getSimpleName(), CONFIG_BASEPATH, null);
verbose = SystemHelper.getPropertyBool(XmlPersistenceHandler.class.getSimpleName(), CONFIG_VERBOSE,
Boolean.FALSE).booleanValue();
// get class to use as transaction
String daoFactoryClassName = SystemHelper.getProperty(XmlPersistenceHandler.class.getSimpleName(),
CONFIG_DAO_FACTORY_CLASS, null);
try {
@SuppressWarnings("unchecked")
Class<XmlDaoFactory> xmlDaoFactoryClass = (Class<XmlDaoFactory>) Class.forName(daoFactoryClassName);
xmlDaoFactory = xmlDaoFactoryClass.newInstance();
} catch (ClassNotFoundException e) {
throw new XmlPersistenceExecption("XmlDaoFactory class does not exist " + daoFactoryClassName, e);
} catch (Exception e) {
throw new XmlPersistenceExecption("Failed to load class " + daoFactoryClassName, e);
}
XmlPersistencePathBuilder pathBuilder = new XmlPersistencePathBuilder(basePath);
persister = new XmlFilePersister(pathBuilder, verbose);
// initialize the Thread local object which is used per transaction
xmlPersistenceTxThreadLocal = new ThreadLocal<XmlPersistenceTransaction>();
}
/**
*
*/
public XmlPersistenceTransaction openTx() {
if (verbose)
logger.info("Opening new transaction...");
// make sure no previous filter exists
XmlPersistenceTransaction xmlPersistenceTx = this.xmlPersistenceTxThreadLocal.get();
if (xmlPersistenceTx != null)
throw new XmlPersistenceExecption("Previous transaction not properly closed");
// set a new persistence transaction object
ObjectFilter<ITransactionObject> objectFilter = new ObjectFilter<ITransactionObject>();
xmlPersistenceTx = new XmlPersistenceTransaction();
xmlPersistenceTx.initialize(persister, xmlDaoFactory, objectFilter, verbose);
this.xmlPersistenceTxThreadLocal.set(xmlPersistenceTx);
return xmlPersistenceTx;
}
/**
*
*/
public XmlPersistenceTransaction getTx() {
XmlPersistenceTransaction xmlPersistenceTx = this.xmlPersistenceTxThreadLocal.get();
if (xmlPersistenceTx == null)
throw new XmlPersistenceExecption("No transaction currently open!");
return xmlPersistenceTx;
}
/**
*
*/
public void commitTx() {
if (verbose)
logger.info("Committing transaction...");
try {
XmlPersistenceTransaction xmlPersistenceTx = this.xmlPersistenceTxThreadLocal.get();
if (xmlPersistenceTx == null)
throw new XmlPersistenceExecption("No transaction currently open!");
xmlPersistenceTx.commitTx();
} finally {
this.xmlPersistenceTxThreadLocal.set(null);
}
}
}

View File

@ -0,0 +1,144 @@
package ch.eitchnet.xmlpers;
import java.io.File;
import java.io.IOException;
import org.apache.log4j.Logger;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class XmlPersistencePathBuilder {
private static final Logger logger = Logger.getLogger(XmlPersistencePathBuilder.class);
/**
*
*/
public static final String FILE_EXT = ".xml";
/**
*
*/
public static final int EXT_LENGTH = FILE_EXT.length();
private String basePath;
/**
* @param basePath
*/
public XmlPersistencePathBuilder(String basePath) {
File basePathF = new File(basePath);
if (!basePathF.exists())
throw new XmlPersistenceExecption("The database store path does not exist at "
+ basePathF.getAbsolutePath());
if (!basePathF.canWrite())
throw new XmlPersistenceExecption("The database store path is not writeable at "
+ basePathF.getAbsolutePath());
try {
this.basePath = basePathF.getCanonicalPath();
} catch (IOException e) {
throw new XmlPersistenceExecption("Failed to build canonical path from " + basePath, e);
}
logger.info("Using base path " + basePath);
}
/**
* @param id
* @return
*/
public String getFilename(String id) {
return id.concat(FILE_EXT);
}
/**
* @param filename
* @return
*/
public String getId(String filename) {
if (filename.charAt(filename.length() - EXT_LENGTH) != '.')
throw new XmlPersistenceExecption("The filename does not have a . at index "
+ (filename.length() - EXT_LENGTH));
return filename.substring(0, filename.length() - EXT_LENGTH);
}
/**
* @param type
*
* @return
*/
public String getPath(String type) {
StringBuilder sb = new StringBuilder(basePath);
sb.append("/");
sb.append(type);
return sb.toString();
}
/**
* @param type
*
* @return
*/
public File getPathF(String type) {
return new File(getPath(type));
}
/**
* @param type
* @param subType
* @return
*/
public String getPath(String type, String subType) {
StringBuilder sb = new StringBuilder(basePath);
sb.append("/");
sb.append(type);
sb.append("/");
sb.append(subType);
return sb.toString();
}
/**
* @param type
* @param subType
* @return
*/
public File getPathF(String type, String subType) {
return new File(getPath(type, subType));
}
/**
* @param type
* @param subType
* @param id
* @return
*/
public String getPath(String type, String subType, String id) {
StringBuilder sb = new StringBuilder(basePath);
sb.append("/");
sb.append(type);
sb.append("/");
sb.append(subType);
sb.append("/");
sb.append(getFilename(id));
return sb.toString();
}
/**
* @param type
* @param subType
* @param id
* @return
*/
public File getPathF(String type, String subType, String id) {
return new File(getPath(type, subType, id));
}
}

View File

@ -0,0 +1,300 @@
/*
* Copyright (c) 2010 - 2011
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
package ch.eitchnet.xmlpers;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import ch.eitchnet.utils.objectfilter.ITransactionObject;
import ch.eitchnet.utils.objectfilter.ObjectFilter;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class XmlPersistenceTransaction {
private static final Logger logger = Logger.getLogger(XmlPersistenceTransaction.class);
private boolean verbose;
private XmlFilePersister persister;
private XmlDaoFactory xmlDaoFactory;
private ObjectFilter<ITransactionObject> objectFilter;
private DocumentBuilder docBuilder;
private DOMImplementation domImplementation;
/**
* @param persister
* @param xmlDaoFactory
* @param objectFilter
*/
public void initialize(XmlFilePersister persister, XmlDaoFactory xmlDaoFactory,
ObjectFilter<ITransactionObject> objectFilter, boolean verbose) {
this.persister = persister;
this.xmlDaoFactory = xmlDaoFactory;
this.objectFilter = objectFilter;
this.verbose = verbose;
}
private DocumentBuilder getDocBuilder() {
if (docBuilder == null) {
try {
docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new XmlPersistenceExecption("Failed to load document builder: " + e.getLocalizedMessage(), e);
}
}
return docBuilder;
}
/**
* @return
*/
protected DOMImplementation getDomImpl() {
if (domImplementation == null)
domImplementation = getDocBuilder().getDOMImplementation();
return domImplementation;
}
/*
* modifying methods
*/
/**
* @param object
*/
public void add(ITransactionObject object) {
this.objectFilter.add(object);
}
/**
* @param objects
*/
public void addAll(List<ITransactionObject> objects) {
this.objectFilter.addAll(objects);
}
/**
* @param object
*/
public void update(ITransactionObject object) {
this.objectFilter.update(object);
}
/**
* @param objects
*/
public void updateAll(List<ITransactionObject> objects) {
this.objectFilter.updateAll(objects);
}
/**
* @param object
*/
public void remove(ITransactionObject object) {
this.objectFilter.remove(object);
}
/**
* @param objects
*/
public void removeAll(List<ITransactionObject> objects) {
this.objectFilter.removeAll(objects);
}
/*
* querying methods
*/
/**
* @param type
* @return
*/
public Set<String> queryKeySet(String type) {
return queryKeySet(type, null);
}
/**
* @param type
* @param subType
* @return
*/
public Set<String> queryKeySet(String type, String subType) {
return this.persister.queryKeySet(type, subType);
}
/**
* @param type
* @return
*/
public long querySize(String type) {
return querySize(type, null);
}
/**
* @param type
* @param subType
* @return
*/
public long querySize(String type, String subType) {
return this.persister.querySize(type, subType);
}
/**
* @param type
* @return
*/
public <T> List<T> queryAll(String type) {
return queryAll(type, null);
}
/**
* @param type
* @param subType
* @return
*/
public <T> List<T> queryAll(String type, String subType) {
// XXX ok, this is very ugly, but for starters it will have to do
XmlDao<Object> dao = xmlDaoFactory.getDao(type);
List<Element> elements = this.persister.queryAll(type, subType, getDocBuilder());
List<T> objects = new ArrayList<T>(elements.size());
for (Element element : elements) {
@SuppressWarnings("unchecked")
T object = (T) dao.parseFromDom(element);
objects.add(object);
}
return objects;
}
/**
* @param type
* @param id
* @return
*/
public <T> T queryById(String type, String id) {
return queryById(type, null, id);
}
/**
* @param type
* @param subType
* @param id
* @return
*/
public <T> T queryById(String type, String subType, String id) {
XmlDao<Object> dao = xmlDaoFactory.getDao(type);
Element element = this.persister.queryById(type, subType, id, getDocBuilder());
if (element == null)
throw new XmlPersistenceExecption("No object exists for " + type + " / " + subType + " / " + id);
@SuppressWarnings("unchecked")
T object = (T) dao.parseFromDom(element);
return object;
}
/*
* committing
*/
/**
*
*/
void commitTx() {
if (verbose)
logger.info("Committing...");
Set<String> keySet = objectFilter.keySet();
if (keySet.isEmpty())
return;
for (String key : keySet) {
XmlDao<Object> dao = xmlDaoFactory.getDao(key);
List<ITransactionObject> removed = objectFilter.getRemoved(key);
if (removed.isEmpty()) {
if (verbose)
logger.info("No objects removed in this tx.");
} else {
if (verbose)
logger.info(removed.size() + " objects removed in this tx.");
for (ITransactionObject object : removed) {
String type = dao.getType(object);
String subType = dao.getSubType(object);
String id = dao.getId(object);
persister.remove(type, subType, id);
}
}
List<ITransactionObject> updated = objectFilter.getUpdated(key);
if (updated.isEmpty()) {
if (verbose)
logger.info("No objects updated in this tx.");
} else {
if (verbose)
logger.info(updated.size() + " objects updated in this tx.");
for (ITransactionObject object : updated) {
String type = dao.getType(object);
String subType = dao.getSubType(object);
String id = dao.getId(object);
Document asDom = dao.serializeToDom(object, getDomImpl());
persister.saveOrUpdate(type, subType, id, asDom);
}
}
List<ITransactionObject> added = objectFilter.getAdded(key);
if (added.isEmpty()) {
if (verbose)
logger.info("No objects added in this tx.");
} else {
if (verbose)
logger.info(updated.size() + " objects added in this tx.");
for (ITransactionObject object : added) {
String type = dao.getType(object);
String subType = dao.getSubType(object);
String id = dao.getId(object);
Document asDom = dao.serializeToDom(object, getDomImpl());
persister.saveOrUpdate(type, subType, id, asDom);
}
}
}
objectFilter.clearCache();
logger.info("Completed TX");
}
}

View File

@ -0,0 +1,311 @@
/*
* Copyright (c) 2010
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
/**
*
*/
package ch.eitchnet.featherlite.plugin.xmlpers.test;
import java.io.File;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.eitchnet.featherlite.plugin.xmlpers.test.impl.MyClass;
import ch.eitchnet.featherlite.plugin.xmlpers.test.impl.MyDaoFactory;
import ch.eitchnet.utils.helper.Log4jConfigurator;
import ch.eitchnet.utils.objectfilter.ITransactionObject;
import ch.eitchnet.xmlpers.XmlPersistenceExecption;
import ch.eitchnet.xmlpers.XmlPersistenceHandler;
import ch.eitchnet.xmlpers.XmlPersistenceTransaction;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class XmlPersistenceTest {
private static final Logger logger = Logger.getLogger(XmlPersistenceTest.class.getName());
private static XmlPersistenceHandler persistenceHandler;
/**
* @throws Exception
* if something goes wrong
*/
@BeforeClass
public static void init() throws Exception {
try {
// set up log4j
Log4jConfigurator.configure();
String userDir = System.getProperty("user.dir");
String basePath = userDir + "/tmp/testdb";
File basePathF = new File(basePath);
if (!basePathF.exists() && !basePathF.mkdirs())
Assert.fail("Could not create temporaray database store in " + basePathF.getAbsolutePath());
System.setProperty(XmlPersistenceHandler.CONFIG_BASEPATH, "tmp/testdb");
System.setProperty(XmlPersistenceHandler.CONFIG_VERBOSE, "true");
System.setProperty(XmlPersistenceHandler.CONFIG_DAO_FACTORY_CLASS, MyDaoFactory.class.getName());
persistenceHandler = new XmlPersistenceHandler();
persistenceHandler.initialize();
logger.info("Initialized persistence handler.");
} catch (Exception e) {
logger.error(e, e);
throw new RuntimeException("Initialization failed: " + e.getLocalizedMessage(), e);
}
}
/**
*
*/
@Test
public void testCreate() {
try {
logger.info("Trying to create...");
// new instance
MyClass myClass = new MyClass("@id", "@name", "@subtype");
// persist instance
XmlPersistenceTransaction tx = persistenceHandler.openTx();
tx.add(myClass);
persistenceHandler.commitTx();
logger.info("Done creating.");
} catch (Exception e) {
logger.error(e, e);
Assert.fail("Failed: " + e.getLocalizedMessage());
}
}
/**
*
*/
@Test
public void testRead() {
try {
logger.info("Trying to read...");
// query MyClass with id @id
XmlPersistenceTransaction tx = persistenceHandler.openTx();
MyClass myClass = tx.queryById(MyClass.class.getName(), "@subtype", "@id");
logger.info("Found MyClass: " + myClass);
persistenceHandler.commitTx();
logger.info("Done reading.");
} catch (Exception e) {
logger.error(e, e);
Assert.fail("Failed: " + e.getLocalizedMessage());
}
}
/**
*
*/
@Test
public void testUpdate() {
try {
logger.info("Trying to update an object...");
// query the instance
XmlPersistenceTransaction tx = persistenceHandler.openTx();
MyClass myClass = tx.queryById(MyClass.class.getName(), "@subtype", "@id");
logger.info("Found MyClass: " + myClass);
// modify the instance
myClass.setName("@name_modified");
// update the instance
tx.update(myClass);
persistenceHandler.commitTx();
logger.info("Done updating.");
} catch (Exception e) {
logger.error(e, e);
Assert.fail("Failed: " + e.getLocalizedMessage());
}
}
/**
*
*/
@Test
public void testRemove() {
logger.info("Trying to remove...");
// query the instance
XmlPersistenceTransaction tx = persistenceHandler.openTx();
MyClass myClass = tx.queryById(MyClass.class.getName(), "@subtype", "@id");
logger.info("Found MyClass: " + myClass);
tx.remove(myClass);
persistenceHandler.commitTx();
logger.info("Done removing.");
}
/**
*
*/
@Test(expected = XmlPersistenceExecption.class)
public void testQueryFail() {
try {
logger.info("Trying to query removed object...");
XmlPersistenceTransaction tx = persistenceHandler.openTx();
MyClass myClass = tx.queryById(MyClass.class.getName(), "@subtype", "@id");
logger.info("Found MyClass: " + myClass);
logger.info("Done querying removed object");
} finally {
persistenceHandler.commitTx();
}
}
/**
*
*/
@Test
public void testReCreate() {
try {
logger.info("Trying to recreate...");
// new instance
MyClass myClass = new MyClass("@id", "@name", "@subtype");
// persist instance
XmlPersistenceTransaction tx = persistenceHandler.openTx();
tx.add(myClass);
persistenceHandler.commitTx();
logger.info("Done creating.");
} catch (Exception e) {
logger.error(e, e);
Assert.fail("Failed: " + e.getLocalizedMessage());
}
}
// /**
// *
// */
// @Test
// public void testQueryFromTo() {
// Assert.fail("Not yet implemented");
// }
/**
*
*/
@Test
public void testQueryAll() {
try {
logger.info("Trying to query all...");
// query all
XmlPersistenceTransaction tx = persistenceHandler.openTx();
List<Object> list = tx.queryAll(MyClass.class.getName());
Assert.assertTrue("Expected only one object, found " + list.size(), list.size() == 1);
// also with subtype
list = tx.queryAll(MyClass.class.getName(), "@subtype");
Assert.assertTrue("Expected only one object, found " + list.size(), list.size() == 1);
// and now something useless
list = tx.queryAll(MyClass.class.getName(), "@inexistant");
Assert.assertTrue("Expected no objects, found " + list.size(), list.size() == 0);
logger.info("Done querying.");
} finally {
persistenceHandler.commitTx();
}
}
/**
*
*/
@Test
public void testKeySet() {
try {
XmlPersistenceTransaction tx = persistenceHandler.openTx();
Set<String> keySet = tx.queryKeySet(MyClass.class.getName());
Assert.assertTrue("Expected one key, found " + keySet.size(), keySet.size() == 1);
// also with subtype
keySet = tx.queryKeySet(MyClass.class.getName(), "@subtype");
Assert.assertTrue("Expected one key, found " + keySet.size(), keySet.size() == 1);
// and now something useless
keySet = tx.queryKeySet(MyClass.class.getName(), "@inexistant");
Assert.assertTrue("Expected no keys, found " + keySet, keySet.size() == 0);
} finally {
persistenceHandler.commitTx();
}
}
/**
*
*/
@Test
public void testRemoveAll() {
try {
XmlPersistenceTransaction tx = persistenceHandler.openTx();
List<ITransactionObject> objects = tx.queryAll(MyClass.class.getName(), "@subType");
tx.removeAll(objects);
} finally {
persistenceHandler.commitTx();
}
}
/**
*
*/
@Test
public void testSize() {
try {
XmlPersistenceTransaction tx = persistenceHandler.openTx();
long size = tx.querySize(MyClass.class.getName(), "@subType");
Assert.assertTrue("Expected size = 0, found: " + size, size == 0);
} finally {
persistenceHandler.commitTx();
}
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2010 - 2011
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
package ch.eitchnet.featherlite.plugin.xmlpers.test.impl;
import ch.eitchnet.utils.objectfilter.ITransactionObject;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class MyClass implements ITransactionObject {
private long txId;
private String id;
private String name;
private String type;
/**
* @param id
* @param name
* @param type
*/
public MyClass(String id, String name, String type) {
super();
this.id = id;
this.name = name;
this.type = type;
}
/**
* @return the id
*/
public String getId() {
return this.id;
}
/**
* @param id
* the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return this.name;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the type
*/
public String getType() {
return this.type;
}
/**
* @param type
* the type to set
*/
public void setType(String type) {
this.type = type;
}
/**
* @see ch.eitchnet.utils.objectfilter.ITransactionObject#setTransactionID(long)
*/
@Override
public void setTransactionID(long id) {
this.txId = id;
}
/**
* @see ch.eitchnet.utils.objectfilter.ITransactionObject#getTransactionID()
*/
@Override
public long getTransactionID() {
return this.txId;
}
/**
* @see ch.eitchnet.utils.objectfilter.ITransactionObject#resetTransactionID()
*/
@Override
public void resetTransactionID() {
this.txId = ITransactionObject.UNSET;
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2010 - 2011
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
package ch.eitchnet.featherlite.plugin.xmlpers.test.impl;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import ch.eitchnet.xmlpers.XmlDao;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class MyClassDao implements XmlDao<MyClass> {
/**
* @see ch.eitchnet.xmlpers.XmlDao#getType(java.lang.Object)
*/
@Override
public String getType(MyClass object) {
return MyClass.class.getName();
}
/**
* @see ch.eitchnet.xmlpers.XmlDao#getSubType(java.lang.Object)
*/
@Override
public String getSubType(MyClass object) {
return object.getType();
}
/**
* @see ch.eitchnet.xmlpers.XmlDao#getId(java.lang.Object)
*/
@Override
public String getId(MyClass object) {
return object.getId();
}
/**
* @see ch.eitchnet.xmlpers.XmlDao#serializeToDom(java.lang.Object, org.w3c.dom.DOMImplementation)
*/
@Override
public Document serializeToDom(MyClass object, DOMImplementation domImplementation) {
Document document = domImplementation.createDocument(null, null, null);
Element element = document.createElement("MyClass");
document.appendChild(element);
element.setAttribute("id", object.getId());
element.setAttribute("type", object.getType());
Element nameElement = document.createElement("Name");
element.appendChild(nameElement);
Text textNode = document.createTextNode(object.getName());
nameElement.appendChild(textNode);
return document;
}
/**
* @see ch.eitchnet.xmlpers.XmlDao#parseFromDom(org.w3c.dom.Element)
*/
@Override
public MyClass parseFromDom(Element element) {
String id = element.getAttribute("id");
String type = element.getAttribute("type");
Element nameElement = (Element) element.getElementsByTagName("Name").item(0);
String name = nameElement.getTextContent();
MyClass myClass = new MyClass(id, name, type);
return myClass;
}
/**
* @see ch.eitchnet.xmlpers.XmlDao#serializeToSax(java.lang.Object, org.xml.sax.ContentHandler)
*/
@Override
public void serializeToSax(MyClass object, ContentHandler contentHandler) {
try {
contentHandler.startDocument();
// MyClass element / root
{
AttributesImpl atts = new AttributesImpl();
atts.addAttribute("", "", "id", "", object.getId());
atts.addAttribute("", "", "type", "", object.getType());
contentHandler.startElement("", "", "MyClass", atts);
// name element
{
contentHandler.startElement("", "", "Name", null);
char[] nameArr = object.getName().toCharArray();
contentHandler.characters(nameArr, 0, nameArr.length);
contentHandler.endElement("", "", "name");
}
// MyClass end
contentHandler.endElement("", "", "MyClass");
}
// end document
contentHandler.endDocument();
} catch (SAXException e) {
throw new RuntimeException("Failed to serialize " + object + " to SAX", e);
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2010 - 2011
*
* Apixxo AG
* Hauptgasse 25
* 4600 Olten
*
* All rights reserved.
*
*/
package ch.eitchnet.featherlite.plugin.xmlpers.test.impl;
import ch.eitchnet.xmlpers.XmlDao;
import ch.eitchnet.xmlpers.XmlDaoFactory;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class MyDaoFactory implements XmlDaoFactory {
/**
* @see ch.eitchnet.xmlpers.XmlDaoFactory#getDao(java.lang.String)
*/
@SuppressWarnings("unchecked")
@Override
public <T> XmlDao<T> getDao(String type) {
if (type.equals(MyClass.class.getName()))
return (XmlDao<T>) new MyClassDao();
throw new RuntimeException("Class with type " + type + " is unknown!");
}
}