[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:
parent
b8ece51dbe
commit
4956925329
|
@ -4,3 +4,6 @@
|
||||||
*.jar
|
*.jar
|
||||||
*.war
|
*.war
|
||||||
*.ear
|
*.ear
|
||||||
|
|
||||||
|
# Project files
|
||||||
|
tmp
|
||||||
|
|
|
@ -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
|
|
@ -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...
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue