Merge remote-tracking branch 'xmlpers/develop' into develop

This commit is contained in:
Robert von Burg 2016-06-24 17:38:40 +02:00
commit 4ff74ccfdd
63 changed files with 6548 additions and 0 deletions

202
ch.eitchnet.xmlpers/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,38 @@
ch.eitchnet.java.xmlpers
========================
[![Build Status](http://jenkins.eitchnet.ch/buildStatus/icon?job=ch.eitchnet.xmlpers)](http://jenkins.eitchnet.ch/view/ch.eitchnet/job/ch.eitchnet.xmlpers/)
Generic Java XML persistence layer. Implemented to be light-weight and simple to use
Dependencies
------------------------
XmlPers is built by Maven3 and has very few external dependencies. The current dependencies are:
* the Java Runtime Environment 6
* ch.eitchnet.utils
* slf4j 1.7.2
* slf4j-log4j bindings (only during tests)
* JUnit 4.10 (only during tests)
Features
------------------------
The idea behind XmlPers is to have a very lightweight database where each object is saved in its own XML file.
The model for XmlPers is that for each persistable class the following information is available:
* Type (e.g. the class name)
* Optional Sub Type (e.g. some type in your class)
* Id
This is not forced on the model itself, but in the DAO. Persisting changes is done by delegating to XmlFilePersister and the DAO must convert the object to a XML Document.
See the tests for a reference implementation.
Building
------------------------
*Prerequisites:
* JDK 6 is installed and JAVA_HOME is properly set and ../bin is in path
* Maven 3 is installed and MAVEN_HOME is properly set and ../bin is in path
* ch.eitchnet.utils is installed in your local Maven Repository
* Clone repository and change path to root
* Run maven:
* mvn clean install

View File

@ -0,0 +1,71 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ch.eitchnet</groupId>
<artifactId>ch.eitchnet.parent</artifactId>
<version>1.1.0-SNAPSHOT</version>
<relativePath>../ch.eitchnet.parent/pom.xml</relativePath>
</parent>
<artifactId>ch.eitchnet.xmlpers</artifactId>
<packaging>jar</packaging>
<name>ch.eitchnet.xmlpers</name>
<url>https://github.com/eitchnet/ch.eitchnet.xmlpers</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<eitchnet.utils.version>1.1.0-SNAPSHOT</eitchnet.utils.version>
</properties>
<!-- POM Reference: http://maven.apache.org/pom.html#Licenses -->
<inceptionYear>2011</inceptionYear>
<issueManagement>
<system>Github Issues</system>
<url>https://github.com/eitchnet/ch.eitchnet.xmlpers/issues</url>
</issueManagement>
<scm>
<connection>scm:git:https://github.com/eitchnet/ch.eitchnet.xmlpers.git</connection>
<developerConnection>scm:git:git@github.com:eitchnet/ch.eitchnet.xmlpers.git</developerConnection>
<url>https://github.com/eitchnet/ch.eitchnet.xmlpers</url>
<tag>HEAD</tag>
</scm>
<dependencies>
<dependency>
<groupId>ch.eitchnet</groupId>
<artifactId>ch.eitchnet.utils</artifactId>
<version>${eitchnet.utils.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,29 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import org.w3c.dom.Document;
public interface DomParser<T> {
public T getObject();
public void setObject(T object);
public Document toDom();
public void fromDom(Document document);
}

View File

@ -0,0 +1,199 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.io.File;
import java.text.MessageFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.xmlpers.impl.PathBuilder;
import ch.eitchnet.xmlpers.objref.ObjectRef;
public class FileDao {
private static final Logger logger = LoggerFactory.getLogger(FileDao.class);
private final PersistenceTransaction tx;
private final boolean verbose;
private final PathBuilder pathBuilder;
public FileDao(PersistenceTransaction tx, PathBuilder pathBuilder, boolean verbose) {
this.tx = tx;
this.pathBuilder = pathBuilder;
this.verbose = verbose;
}
private void assertIsIdRef(IoOperation ioOperation, ObjectRef objectRef) {
if (!objectRef.isLeaf()) {
String msg = "A {0} operation can only be performed with IdRefs!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, ioOperation);
throw new XmlPersistenceException(msg);
}
}
public <T> boolean exists(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.READ, objectRef);
File path = objectRef.getPath(this.pathBuilder);
return path.exists();
}
public <T> void performCreate(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.CREATE, objectRef);
File path = objectRef.getPath(this.pathBuilder);
logPath(IoOperation.CREATE, path, objectRef);
assertPathNotExists(path, objectRef);
createMissingParents(path, objectRef);
FileIo fileIo = new FileIo(path);
this.tx.getIoMode().write(ctx, fileIo);
}
public <T> void performRead(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.READ, objectRef);
File path = objectRef.getPath(this.pathBuilder);
if (!path.exists()) {
ctx.setObject(null);
return;
}
logPath(IoOperation.READ, path, objectRef);
FileIo fileIo = new FileIo(path);
this.tx.getIoMode().read(ctx, fileIo);
}
public <T> void performUpdate(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.UPDATE, objectRef);
File path = objectRef.getPath(this.pathBuilder);
logPath(IoOperation.UPDATE, path, objectRef);
assertPathIsFileAndWritable(path, objectRef);
FileIo fileIo = new FileIo(path);
this.tx.getIoMode().write(ctx, fileIo);
}
public <T> void performDelete(PersistenceContext<T> ctx) {
ObjectRef objectRef = ctx.getObjectRef();
assertIsIdRef(IoOperation.DELETE, objectRef);
File path = objectRef.getPath(this.pathBuilder);
logPath(IoOperation.DELETE, path, objectRef);
assertPathIsFileAndWritable(path, objectRef);
if (!path.delete()) {
String msg = "Failed to delete file {0}"; //$NON-NLS-1$
throw new RuntimeException(MessageFormat.format(msg, path.getAbsolutePath()));
}
ObjectRef parentRef = objectRef.getParent(this.tx);
deleteEmptyDirectories(parentRef);
}
private void deleteEmptyDirectories(ObjectRef objectRef) {
// root can't be deleted
if (objectRef.isRoot())
return;
if (objectRef.isLeaf()) {
throw new IllegalArgumentException("IdRefs don't reference directories!"); //$NON-NLS-1$
}
objectRef.lock();
try {
File directoryPath = objectRef.getPath(this.pathBuilder);
if (!directoryPath.isDirectory()) {
String msg = "The path for {0} is not a directory: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), directoryPath.getAbsolutePath());
throw new IllegalArgumentException(msg);
}
// stop if empty
if (directoryPath.list().length != 0)
return;
// delete
if (!directoryPath.delete()) {
String msg = "Deletion of empty directory for {0} at {1} failed! Check file permissions!"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), directoryPath.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
// log
if (this.verbose) {
String msg = "Deleted empty directory for {0} at {1}"; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, objectRef.getName(), directoryPath));
}
// recursively delete
ObjectRef parent = objectRef.getParent(this.tx);
deleteEmptyDirectories(parent);
} finally {
objectRef.unlock();
}
}
private void logPath(IoOperation operation, File path, ObjectRef objectRef) {
if (this.verbose) {
String msg = "Path for operation {0} for {1} is at {2}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, operation, objectRef.getName(), path.getAbsolutePath());
logger.info(msg);
}
}
private void createMissingParents(File path, ObjectRef objectRef) {
ObjectRef parentRef = objectRef.getParent(this.tx);
parentRef.lock();
try {
File parentFile = parentRef.getPath(this.pathBuilder);
if (!parentFile.exists() && !parentFile.mkdirs()) {
String msg = "Could not create parent path for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
} finally {
parentRef.unlock();
}
}
private void assertPathIsFileAndWritable(File path, ObjectRef objectRef) {
if (!path.exists()) {
String msg = "Persistence unit does not exist for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
if (!path.isFile() || !path.canWrite()) {
String msg;
msg = "Persistence unit is not a file or is not readable for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
}
private void assertPathNotExists(File path, ObjectRef objectRef) {
if (path.exists()) {
String msg = "Persistence unit already exists for {0} at {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName(), path.getAbsolutePath());
throw new XmlPersistenceException(msg);
}
}
}

View File

@ -0,0 +1,205 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.MessageFormat;
import javanet.staxutils.IndentingXMLStreamWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ch.eitchnet.utils.exceptions.XmlException;
import ch.eitchnet.utils.helper.StringHelper;
import ch.eitchnet.utils.helper.XmlHelper;
import ch.eitchnet.xmlpers.util.DomUtil;
public class FileIo {
public static final String DEFAULT_XML_VERSION = "1.0"; //$NON-NLS-1$
public static final String DEFAULT_ENCODING = "utf-8"; //$NON-NLS-1$
private static final Logger logger = LoggerFactory.getLogger(FileIo.class);
private final File path;
public FileIo(File path) {
this.path = path;
}
public <T> void writeSax(PersistenceContext<T> ctx) {
XMLStreamWriter writer = null;
try {
try (FileWriter fileWriter = new FileWriter(this.path);) {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
writer = factory.createXMLStreamWriter(fileWriter);
writer = new IndentingXMLStreamWriter(writer);
// start document
writer.writeStartDocument(DEFAULT_ENCODING, DEFAULT_XML_VERSION);
// then delegate object writing to caller
SaxParser<T> saxParser = ctx.getParserFactor().getSaxParser();
saxParser.setObject(ctx.getObject());
saxParser.write(writer);
// and now end
writer.writeEndDocument();
writer.flush();
}
} catch (FactoryConfigurationError | XMLStreamException | IOException e) {
if (this.path.exists())
this.path.delete();
String msg = "Writing to file failed due to internal error: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, e.getMessage());
throw new XmlException(msg, e);
}
if (logger.isDebugEnabled()) {
String msg = "Wrote SAX to {0}"; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, this.path.getAbsolutePath()));
}
}
public <T> void readSax(PersistenceContext<T> ctx) {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
SaxParser<T> saxParser = ctx.getParserFactor().getSaxParser();
DefaultHandler defaultHandler = saxParser.getDefaultHandler();
sp.parse(this.path, defaultHandler);
if (logger.isDebugEnabled()) {
String msg = "SAX parsed file {0}"; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, this.path.getAbsolutePath()));
}
ctx.setObject(saxParser.getObject());
} catch (ParserConfigurationException | SAXException | IOException e) {
String msg = "Parsing failed due to internal error: {0}"; //$NON-NLS-1$
throw new XmlPersistenceException(MessageFormat.format(msg, e.getMessage()), e);
}
}
public <T> void writeDom(PersistenceContext<T> ctx) {
String lineSep = System.getProperty(XmlHelper.PROP_LINE_SEPARATOR);
try {
DomParser<T> domParser = ctx.getParserFactor().getDomParser();
domParser.setObject(ctx.getObject());
Document document = domParser.toDom();
String encoding = document.getInputEncoding();
if (encoding == null || encoding.isEmpty()) {
// logger.info("No encoding passed. Using default encoding " + XmlHelper.DEFAULT_ENCODING);
encoding = XmlHelper.DEFAULT_ENCODING;
}
if (!lineSep.equals(StringHelper.NEW_LINE)) {
logger.info("Overriding line separator to \\n"); //$NON-NLS-1$
System.setProperty(XmlHelper.PROP_LINE_SEPARATOR, StringHelper.NEW_LINE);
}
// Set up a transformer
TransformerFactory transfac = TransformerFactory.newInstance();
Transformer transformer = transfac.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
// transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t");
// Transform to file
StreamResult result = new StreamResult(this.path);
Source xmlSource = new DOMSource(document);
transformer.transform(xmlSource, result);
if (logger.isDebugEnabled()) {
String msg = MessageFormat.format("Wrote DOM to {0}", this.path.getAbsolutePath()); //$NON-NLS-1$
logger.info(msg);
}
} catch (TransformerFactoryConfigurationError | TransformerException e) {
if (this.path.exists())
this.path.delete();
String msg = "Writing to file failed due to internal error: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, e.getMessage());
throw new XmlException(msg, e);
} finally {
System.setProperty(XmlHelper.PROP_LINE_SEPARATOR, lineSep);
}
}
public <T> void readDom(PersistenceContext<T> ctx) {
try {
DocumentBuilder docBuilder = DomUtil.createDocumentBuilder();
Document document = docBuilder.parse(this.path);
DomParser<T> domParser = ctx.getParserFactor().getDomParser();
domParser.fromDom(document);
if (logger.isDebugEnabled()) {
String msg = "DOM parsed file {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, this.path.getAbsolutePath());
logger.info(msg);
}
ctx.setObject(domParser.getObject());
} catch (SAXException | IOException e) {
String msg = "Parsing failed due to internal error: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, e.getMessage());
throw new XmlPersistenceException(msg, e);
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public enum IoMode {
DOM {
@Override
public <T> void write(PersistenceContext<T> ctx, FileIo fileIo) {
fileIo.writeDom(ctx);
}
@Override
public <T> void read(PersistenceContext<T> ctx, FileIo fileIo) {
fileIo.readDom(ctx);
}
},
SAX {
@Override
public <T> void write(PersistenceContext<T> ctx, FileIo fileIo) {
fileIo.writeSax(ctx);
}
@Override
public <T> void read(PersistenceContext<T> ctx, FileIo fileIo) {
fileIo.readSax(ctx);
}
};
/**
* @param ctx
* @param fileIo
*/
public <T> void write(PersistenceContext<T> ctx, FileIo fileIo) {
throw new UnsupportedOperationException("Override me!"); //$NON-NLS-1$
}
/**
* @param ctx
* @param fileIo
*/
public <T> void read(PersistenceContext<T> ctx, FileIo fileIo) {
throw new UnsupportedOperationException("Override me!"); //$NON-NLS-1$
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
public enum IoOperation {
CREATE, READ, UPDATE, DELETE;
}

View File

@ -0,0 +1,276 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.io.File;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.xmlpers.impl.PathBuilder;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.util.FilenameUtility;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class MetadataDao {
private static final Logger logger = LoggerFactory.getLogger(MetadataDao.class);
private final PersistenceTransaction tx;
private final PathBuilder pathBuilder;
private final boolean verbose;
public MetadataDao(PathBuilder pathBuilder, PersistenceTransaction tx, boolean verbose) {
this.tx = tx;
this.pathBuilder = pathBuilder;
this.verbose = verbose;
}
public Set<String> queryTypeSet(ObjectRef parentRef) {
assertNotClosed(this.tx);
assertNotIdRef(parentRef);
parentRef.lock();
try {
File queryPath = parentRef.getPath(this.pathBuilder);
Set<String> keySet = queryTypeSet(queryPath);
if (this.verbose) {
String msg = "Found {0} types for {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, keySet.size(), parentRef.getName());
logger.info(msg);
}
return keySet;
} finally {
parentRef.unlock();
}
}
public Set<String> queryKeySet(ObjectRef parentRef) {
assertNotClosed(this.tx);
assertNotRootRef(parentRef);
assertNotIdRef(parentRef);
parentRef.lock();
try {
File queryPath = parentRef.getPath(this.pathBuilder);
Set<String> keySet = queryKeySet(queryPath);
if (this.verbose) {
String msg = "Found {0} objects for {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, keySet.size(), parentRef.getName());
logger.info(msg);
}
return keySet;
} finally {
parentRef.unlock();
}
}
public long queryTypeSize(ObjectRef parentRef) {
assertNotClosed(this.tx);
assertNotRootRef(parentRef);
assertNotIdRef(parentRef);
parentRef.lock();
try {
File queryPath = parentRef.getPath(this.pathBuilder);
long numberOfFiles = queryTypeSize(queryPath);
if (this.verbose) {
String msg = "Found {0} types for {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, numberOfFiles, parentRef.getName());
logger.info(msg);
}
return numberOfFiles;
} finally {
parentRef.unlock();
}
}
public long querySize(ObjectRef parentRef) {
assertNotClosed(this.tx);
parentRef.lock();
try {
File queryPath = parentRef.getPath(this.pathBuilder);
long numberOfFiles = querySize(queryPath);
if (this.verbose) {
String msg = "Found {0} objects for {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, numberOfFiles, parentRef.getName());
logger.info(msg);
}
return numberOfFiles;
} finally {
parentRef.unlock();
}
}
/**
* Returns the types, i.e. directories in the given query path
*
* @param queryPath
* the path for which the types should be gathered
*
* @return a set of types in the given query path
*/
private Set<String> queryTypeSet(File queryPath) {
if (!queryPath.exists())
return Collections.emptySet();
if (!queryPath.isDirectory()) {
String msg = "The path is not a directory, thus can not query type set for it: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, queryPath.getAbsolutePath());
throw new IllegalArgumentException(msg);
}
Set<String> keySet = new HashSet<>();
File[] subTypeFiles = queryPath.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isDirectory()) {
String type = subTypeFile.getName();
keySet.add(type);
}
}
return keySet;
}
/**
* Returns the ids of all objects in the given query path, i.e. the id part of all the files in the given query path
*
* @param queryPath
* the path for which the ids should be gathered
*
* @return a set of ids for the objects in the given query path
*/
private Set<String> queryKeySet(File queryPath) {
if (!queryPath.exists())
return Collections.emptySet();
if (!queryPath.isDirectory()) {
String msg = "The path is not a directory, thus can not query key set for it: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, queryPath.getAbsolutePath());
throw new IllegalArgumentException(msg);
}
Set<String> keySet = new HashSet<>();
File[] subTypeFiles = queryPath.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isFile()) {
String filename = subTypeFile.getName();
String id = FilenameUtility.getId(filename);
keySet.add(id);
}
}
return keySet;
}
/**
* Returns the number of all types, i.e. directories in the given query path
*
* @param queryPath
* the path in which to count the types
*
* @return the number of types in the given query path
*/
private long queryTypeSize(File queryPath) {
if (!queryPath.exists())
return 0L;
if (!queryPath.isDirectory()) {
String msg = "The path is not a directory, thus can not query type size for it: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, queryPath.getAbsolutePath());
throw new IllegalArgumentException(msg);
}
long numberOfFiles = 0l;
File[] subTypeFiles = queryPath.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isDirectory())
numberOfFiles++;
}
return numberOfFiles;
}
/**
* Returns the number of all objects in the given query path
*
* @param queryPath
* the path in which to count the objects
*
* @return the number of objects in the given query path
*/
private long querySize(File queryPath) {
if (!queryPath.exists())
return 0L;
if (!queryPath.isDirectory()) {
String msg = "The path is not a directory, thus can not query key size for it: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, queryPath.getAbsolutePath());
throw new IllegalArgumentException(msg);
}
long numberOfFiles = 0l;
File[] subTypeFiles = queryPath.listFiles();
for (File subTypeFile : subTypeFiles) {
if (subTypeFile.isFile())
numberOfFiles++;
}
return numberOfFiles;
}
private void assertNotClosed(PersistenceTransaction tx) {
if (!tx.isOpen()) {
String msg = "Transaction has been closed and thus no operation can be performed!"; //$NON-NLS-1$
throw new IllegalStateException(msg);
}
}
private void assertNotIdRef(ObjectRef objectRef) {
if (objectRef.isLeaf()) {
String msg = "IdRef not allowed: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName());
throw new IllegalArgumentException(msg);
}
}
private void assertNotRootRef(ObjectRef objectRef) {
if (objectRef.isRoot()) {
String msg = "RootRef not allowed: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName());
throw new IllegalArgumentException(msg);
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.util.ArrayList;
import java.util.List;
public class ModificationResult {
private final String key;
private final List<?> created;
private final List<?> updated;
private final List<?> deleted;
public ModificationResult(String key) {
this.key = key;
this.created = new ArrayList<>();
this.updated = new ArrayList<>();
this.deleted = new ArrayList<>();
}
public ModificationResult(String key, List<?> created, List<?> updated, List<?> deleted) {
this.key = key;
this.created = created;
this.updated = updated;
this.deleted = deleted;
}
public String getKey() {
return this.key;
}
@SuppressWarnings("unchecked")
public <T> List<T> getCreated() {
return (List<T>) this.created;
}
@SuppressWarnings("unchecked")
public <T> List<T> getUpdated() {
return (List<T>) this.updated;
}
@SuppressWarnings("unchecked")
public <T> List<T> getDeleted() {
return (List<T>) this.deleted;
}
}

View File

@ -0,0 +1,305 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import static ch.eitchnet.xmlpers.util.AssertionUtil.assertIsIdRef;
import static ch.eitchnet.xmlpers.util.AssertionUtil.assertIsNotIdRef;
import static ch.eitchnet.xmlpers.util.AssertionUtil.assertIsNotRootRef;
import static ch.eitchnet.xmlpers.util.AssertionUtil.assertNotNull;
import static ch.eitchnet.xmlpers.util.AssertionUtil.assertObjectRead;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ch.eitchnet.utils.objectfilter.ObjectFilter;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.objref.SubTypeRef;
import ch.eitchnet.xmlpers.objref.TypeRef;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class ObjectDao {
private final ObjectFilter objectFilter;
private final FileDao fileDao;
private final PersistenceTransaction tx;
private PersistenceContextFactoryDelegator ctxFactoryDelegator;
public ObjectDao(PersistenceTransaction tx, FileDao fileDao, ObjectFilter objectFilter) {
this.tx = tx;
this.fileDao = fileDao;
this.objectFilter = objectFilter;
this.ctxFactoryDelegator = this.tx.getRealm().getCtxFactoryDelegator();
}
public <T> void add(T object) {
assertNotClosed();
assertNotNull(object);
PersistenceContext<T> ctx = createCtx(object);
ctx.getObjectRef().lock();
this.objectFilter.add(ctx.getObjectRef().getType(), ctx);
}
public <T> void addAll(List<T> objects) {
assertNotClosed();
assertNotNull(objects);
if (!objects.isEmpty()) {
for (T object : objects) {
PersistenceContext<T> ctx = createCtx(object);
ctx.getObjectRef().lock();
this.objectFilter.add(ctx.getObjectRef().getType(), ctx);
}
}
}
public <T> void update(T object) {
assertNotClosed();
assertNotNull(object);
PersistenceContext<T> ctx = createCtx(object);
ctx.getObjectRef().lock();
this.objectFilter.update(ctx.getObjectRef().getType(), ctx);
}
public <T> void updateAll(List<T> objects) {
assertNotClosed();
assertNotNull(objects);
if (!objects.isEmpty()) {
for (T object : objects) {
PersistenceContext<T> ctx = createCtx(object);
ctx.getObjectRef().lock();
this.objectFilter.update(ctx.getObjectRef().getType(), ctx);
}
}
}
public <T> void remove(T object) {
assertNotClosed();
assertNotNull(object);
PersistenceContext<T> ctx = createCtx(object);
ctx.getObjectRef().lock();
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx);
}
public <T> void removeAll(List<T> objects) {
assertNotClosed();
assertNotNull(objects);
if (!objects.isEmpty()) {
for (T object : objects) {
PersistenceContext<T> ctx = createCtx(object);
ctx.getObjectRef().lock();
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx);
}
}
}
public <T> long removeAllBy(TypeRef typeRef) {
assertNotClosed();
long removed = 0;
Set<ObjectRef> refs = new HashSet<>();
typeRef.lock();
refs.add(typeRef);
try {
Set<String> types = this.tx.getMetadataDao().queryTypeSet(typeRef);
for (String type : types) {
ObjectRef childTypeRef = typeRef.getChildTypeRef(this.tx, type);
childTypeRef.lock();
refs.add(childTypeRef);
Set<String> ids = queryKeySet(childTypeRef);
for (String id : ids) {
ObjectRef idRef = childTypeRef.getChildIdRef(this.tx, id);
PersistenceContext<T> ctx = createCtx(idRef);
ctx.getObjectRef().lock();
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx);
removed++;
}
}
} finally {
for (ObjectRef ref : refs) {
ref.unlock();
}
}
return removed;
}
public <T> long removeAllBy(SubTypeRef subTypeRef) {
assertNotClosed();
assertIsNotRootRef(subTypeRef);
assertIsNotIdRef(subTypeRef);
long removed = 0;
subTypeRef.lock();
try {
Set<String> ids = queryKeySet(subTypeRef);
for (String id : ids) {
ObjectRef idRef = subTypeRef.getChildIdRef(this.tx, id);
PersistenceContext<T> ctx = createCtx(idRef);
ctx.getObjectRef().lock();
this.objectFilter.remove(ctx.getObjectRef().getType(), ctx);
removed++;
}
} finally {
subTypeRef.unlock();
}
return removed;
}
public <T> void removeById(ObjectRef objectRef) {
assertNotClosed();
assertIsIdRef(objectRef);
PersistenceContext<T> ctx = createCtx(objectRef);
ctx.getObjectRef().lock();
this.objectFilter.remove(objectRef.getType(), ctx);
}
public <T> void removeAll(ObjectRef parentRef) {
assertNotClosed();
assertIsNotIdRef(parentRef);
assertIsNotRootRef(parentRef);
parentRef.lock();
try {
Set<String> keySet = queryKeySet(parentRef);
for (String id : keySet) {
ObjectRef childRef = parentRef.getChildIdRef(this.tx, id);
PersistenceContext<T> ctx = createCtx(childRef);
ctx.getObjectRef().lock();
this.objectFilter.remove(childRef.getType(), ctx);
}
} finally {
parentRef.unlock();
}
}
public <T> boolean hasElement(ObjectRef objectRef) {
assertNotClosed();
assertIsIdRef(objectRef);
objectRef.lock();
try {
PersistenceContext<T> ctx = objectRef.<T> createPersistenceContext(this.tx);
return this.fileDao.exists(ctx);
} finally {
objectRef.unlock();
}
}
public <T> T queryById(ObjectRef objectRef) {
assertNotClosed();
assertIsIdRef(objectRef);
objectRef.lock();
try {
PersistenceContext<T> ctx = objectRef.<T> createPersistenceContext(this.tx);
this.fileDao.performRead(ctx);
return ctx.getObject();
} finally {
objectRef.unlock();
}
}
public <T> List<T> queryAll(ObjectRef parentRef) {
assertNotClosed();
assertIsNotIdRef(parentRef);
parentRef.lock();
try {
MetadataDao metadataDao = this.tx.getMetadataDao();
Set<String> keySet = metadataDao.queryKeySet(parentRef);
List<T> result = new ArrayList<>();
for (String id : keySet) {
ObjectRef childRef = parentRef.getChildIdRef(this.tx, id);
PersistenceContext<T> childCtx = childRef.createPersistenceContext(this.tx);
childCtx.getObjectRef().lock();
try {
this.fileDao.performRead(childCtx);
assertObjectRead(childCtx);
result.add(childCtx.getObject());
} finally {
childCtx.getObjectRef().unlock();
}
}
return result;
} finally {
parentRef.unlock();
}
}
public Set<String> queryKeySet(ObjectRef parentRef) {
assertNotClosed();
assertIsNotIdRef(parentRef);
parentRef.lock();
try {
MetadataDao metadataDao = this.tx.getMetadataDao();
Set<String> keySet = metadataDao.queryKeySet(parentRef);
return keySet;
} finally {
parentRef.unlock();
}
}
public long querySize(ObjectRef parentRef) {
assertNotClosed();
assertIsNotIdRef(parentRef);
parentRef.lock();
try {
MetadataDao metadataDao = this.tx.getMetadataDao();
long size = metadataDao.querySize(parentRef);
return size;
} finally {
parentRef.unlock();
}
}
public <T> PersistenceContext<T> createCtx(T object) {
return this.ctxFactoryDelegator.<T> getCtxFactory(object.getClass()).createCtx(this.tx.getObjectRefCache(),
object);
}
public <T> PersistenceContext<T> createCtx(ObjectRef objectRef) {
String type = objectRef.getType();
PersistenceContextFactory<T> ctxFactory = this.ctxFactoryDelegator.<T> getCtxFactory(type);
return ctxFactory.createCtx(objectRef);
}
private void assertNotClosed() {
if (!this.tx.isOpen())
throw new IllegalStateException("Transaction has been closed and thus no operation can be performed!"); //$NON-NLS-1$
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
public interface ParserFactory<T> {
public DomParser<T> getDomParser();
public SaxParser<T> getSaxParser();
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
@SuppressWarnings("nls")
public class PersistenceConstants {
private static final String PROP_PREFIX = "ch.eitchnet.xmlpers.";
public static final String PROP_VERBOSE = PROP_PREFIX + "verbose";
public static final String PROP_BASEPATH = PROP_PREFIX + "basePath";
public static final String PROP_DAO_FACTORY_CLASS = PROP_PREFIX + "daoFactoryClass";
public static final String PROP_XML_IO_MOD = PROP_PREFIX + "ioMode";
public static final String PROP_LOCK_TIME_MILLIS = PROP_PREFIX + "lockTimeSeconds";
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import ch.eitchnet.xmlpers.objref.ObjectRef;
public class PersistenceContext<T> {
private final ObjectRef objectRef;
private T object;
private ParserFactory<T> parserFactory;
public PersistenceContext(ObjectRef objectRef) {
this.objectRef = objectRef;
}
public ObjectRef getObjectRef() {
return this.objectRef;
}
public T getObject() {
return this.object;
}
public void setObject(T object) {
this.object = object;
}
public ParserFactory<T> getParserFactor() {
return this.parserFactory;
}
public void setParserFactory(ParserFactory<T> parserFactory) {
this.parserFactory = parserFactory;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.objectRef == null) ? 0 : this.objectRef.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PersistenceContext<?> other = (PersistenceContext<?>) obj;
if (this.objectRef == null) {
if (other.objectRef != null)
return false;
} else if (!this.objectRef.equals(other.objectRef))
return false;
return true;
}
@SuppressWarnings("nls")
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PersistenceContext [objectRef=");
builder.append(this.objectRef);
builder.append(", object=");
builder.append(this.object);
builder.append(", parserFactory=");
builder.append(this.parserFactory);
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
public interface PersistenceContextFactory<T> {
public PersistenceContext<T> createCtx(ObjectRef objectRef);
public PersistenceContext<T> createCtx(ObjectReferenceCache objectRefCache, T t);
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class PersistenceContextFactoryDelegator {
private Map<String, PersistenceContextFactory<?>> contextFactoryCacheByType;
private Map<Class<?>, PersistenceContextFactory<?>> contextFactoryCacheByClass;
public PersistenceContextFactoryDelegator() {
this.contextFactoryCacheByType = new HashMap<>();
this.contextFactoryCacheByClass = new HashMap<>();
}
public void registerPersistenceContextFactory(Class<?> classType, String type,
PersistenceContextFactory<?> ctxFactory) {
this.contextFactoryCacheByClass.put(classType, ctxFactory);
this.contextFactoryCacheByType.put(type, ctxFactory);
}
public <T> PersistenceContextFactory<T> getCtxFactory(Class<?> classType) {
@SuppressWarnings("unchecked")
PersistenceContextFactory<T> ctxFactory = (PersistenceContextFactory<T>) this.contextFactoryCacheByClass
.get(classType);
if (ctxFactory != null)
return ctxFactory;
String msg = "No context factory is registered for {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, classType);
throw new IllegalArgumentException(msg);
}
public <T> PersistenceContextFactory<T> getCtxFactory(String type) {
@SuppressWarnings("unchecked")
PersistenceContextFactory<T> ctxFactory = (PersistenceContextFactory<T>) this.contextFactoryCacheByType
.get(type);
if (ctxFactory != null)
return ctxFactory;
String msg = "No context factory is registered for type {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, type);
throw new IllegalArgumentException(msg);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public interface PersistenceManager {
public static final String DEFAULT_REALM = "defaultRealm"; //$NON-NLS-1$
public PersistenceContextFactoryDelegator getCtxFactory();
public PersistenceTransaction openTx();
public PersistenceTransaction openTx(String realm);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.util.Properties;
import ch.eitchnet.xmlpers.impl.DefaultPersistenceManager;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class PersistenceManagerLoader {
public static PersistenceManager load(Properties properties) {
DefaultPersistenceManager persistenceManager = new DefaultPersistenceManager();
persistenceManager.initialize(properties);
return persistenceManager;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
public interface PersistenceRealm {
/**
* @return the realm
*/
public abstract String getRealmName();
public abstract PersistenceContextFactoryDelegator getCtxFactoryDelegator();
/**
* @return the objectRefCache
*/
public abstract ObjectReferenceCache getObjectRefCache();
/**
* @return the persistenceManager
*/
public abstract PersistenceManager getPersistenceManager();
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public interface PersistenceTransaction extends AutoCloseable {
/**
* Returns the {@link TransactionResult} for this transaction
*
* @return the {@link TransactionResult}
*
* @throws IllegalStateException
* if the transaction has not yet been closed
*/
public TransactionResult getTransactionResult() throws IllegalStateException;
/**
* @throws IllegalStateException
* if a result is already set
*/
public void setTransactionResult(TransactionResult txResult) throws IllegalStateException;
public void setCloseStrategy(TransactionCloseStrategy closeStrategy);
public void autoCloseableCommit();
public void autoCloseableRollback();
@Override
public void close() throws XmlPersistenceException;
public boolean isOpen();
public ObjectDao getObjectDao();
public MetadataDao getMetadataDao();
public FileDao getFileDao();
public ObjectReferenceCache getObjectRefCache();
public PersistenceRealm getRealm();
public IoMode getIoMode();
public void setIoMode(IoMode ioMode);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.xml.sax.helpers.DefaultHandler;
public interface SaxParser<T> {
public T getObject();
public void setObject(T object);
public DefaultHandler getDefaultHandler();
public void write(XMLStreamWriter xmlWriter) throws XMLStreamException;
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
public enum TransactionCloseStrategy {
COMMIT() {
@Override
public void close(PersistenceTransaction tx) {
tx.autoCloseableCommit();
}
},
ROLLBACK() {
@Override
public void close(PersistenceTransaction tx) {
tx.autoCloseableRollback();
}
};
/**
* @param tx
*/
public void close(PersistenceTransaction tx) {
throw new UnsupportedOperationException("Override in enum!"); //$NON-NLS-1$
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import ch.eitchnet.utils.helper.StringHelper;
public class TransactionResult {
private String realm;
private TransactionState state;
private Exception failCause;
private Date startTime;
private long txDuration;
private long closeDuration;
private Map<String, ModificationResult> modificationByKey;
public TransactionResult() {
this.state = TransactionState.OPEN;
this.modificationByKey = new HashMap<>();
}
/**
* @return the realm
*/
public String getRealm() {
return this.realm;
}
/**
* @param realm
* the realm to set
*/
public void setRealm(String realm) {
this.realm = realm;
}
/**
* @return the state
*/
public TransactionState getState() {
return this.state;
}
/**
* @param state
* the state to set
*/
public void setState(TransactionState state) {
this.state = state;
}
/**
* The internal exception why the transaction failed
*
* @return the failCause
*/
public Exception getFailCause() {
return this.failCause;
}
/**
* @param failCause
* the failCause to set
*/
public void setFailCause(Exception failCause) {
this.failCause = failCause;
}
/**
* Start time of the transaction
*
* @return the startTime
*/
public Date getStartTime() {
return this.startTime;
}
/**
* @param startTime
* the startTime to set
*/
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
/**
* The duration the transaction was open in nanoseconds
*
* @return the txDuration
*/
public long getTxDuration() {
return this.txDuration;
}
/**
* @param txDuration
* the txDuration to set
*/
public void setTxDuration(long txDuration) {
this.txDuration = txDuration;
}
/**
* The duration the transaction took to close in nanoseconds
*
* @return the closeDuration
*/
public long getCloseDuration() {
return this.closeDuration;
}
/**
* @param closeDuration
* the closeDuration to set
*/
public void setCloseDuration(long closeDuration) {
this.closeDuration = closeDuration;
}
/**
* @param modificationByKey
* the modificationByKey to set
*/
public void setModificationByKey(Map<String, ModificationResult> modificationByKey) {
this.modificationByKey = modificationByKey;
}
/**
* @return
*/
public Set<String> getKeys() {
return this.modificationByKey.keySet();
}
/**
* @param key
* @return
*/
public ModificationResult getModificationResult(String key) {
return this.modificationByKey.get(key);
}
@SuppressWarnings("nls")
public String getLogMessage() {
int nrOfObjects = 0;
for (ModificationResult result : this.modificationByKey.values()) {
nrOfObjects += result.getCreated().size();
nrOfObjects += result.getUpdated().size();
nrOfObjects += result.getDeleted().size();
}
StringBuilder sb = new StringBuilder();
sb.append("TX for realm ");
sb.append(getRealm());
switch (this.state) {
case OPEN:
sb.append(" is still open after ");
break;
case COMMITTED:
sb.append(" was completed after ");
break;
case ROLLED_BACK:
sb.append(" was rolled back after ");
break;
case FAILED:
sb.append(" has failed after ");
break;
default:
sb.append(" is in unhandled state ");
sb.append(this.state);
sb.append(" after ");
}
sb.append(StringHelper.formatNanoDuration(this.txDuration));
sb.append(" with close operation taking ");
sb.append(StringHelper.formatNanoDuration(this.closeDuration));
sb.append(". ");
sb.append(nrOfObjects);
sb.append(" objects in ");
sb.append(this.modificationByKey.size());
sb.append(" types were modified.");
return sb.toString();
}
/**
* Clears all fields of this result, allowing it to be reused
*/
public void clear() {
this.realm = null;
this.state = null;
this.failCause = null;
this.startTime = null;
this.txDuration = 0L;
this.closeDuration = 0L;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
public enum TransactionState {
OPEN, //
COMMITTED, //
ROLLED_BACK, //
FAILED;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.api;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class XmlPersistenceException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* @param message
* @param cause
*/
public XmlPersistenceException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
*/
public XmlPersistenceException(String message) {
super(message);
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.impl;
import java.io.File;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.PropertiesHelper;
import ch.eitchnet.utils.helper.StringHelper;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceContextFactoryDelegator;
import ch.eitchnet.xmlpers.api.PersistenceManager;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
import ch.eitchnet.xmlpers.objref.LockableObject;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class DefaultPersistenceManager implements PersistenceManager {
protected static final Logger logger = LoggerFactory.getLogger(DefaultPersistenceManager.class);
protected boolean initialized;
protected boolean verbose;
protected IoMode defaultIoMode;
protected Properties properties;
protected Map<String, DefaultPersistenceRealm> realmMap;
private PersistenceContextFactoryDelegator ctxFactory;
public void initialize(Properties properties) {
if (this.initialized)
throw new IllegalStateException("Already initialized!"); //$NON-NLS-1$
String context = DefaultPersistenceManager.class.getSimpleName();
// get properties
boolean verbose = PropertiesHelper.getPropertyBool(properties, context, PersistenceConstants.PROP_VERBOSE,
Boolean.FALSE).booleanValue();
String ioModeS = PropertiesHelper.getProperty(properties, context, PersistenceConstants.PROP_XML_IO_MOD,
IoMode.DOM.name());
IoMode ioMode = IoMode.valueOf(ioModeS);
long lockTime = PropertiesHelper.getPropertyLong(properties, context,
PersistenceConstants.PROP_LOCK_TIME_MILLIS, 10000L);
// set lock time on LockableObject
try {
Field lockTimeField = LockableObject.class.getDeclaredField("tryLockTime");//$NON-NLS-1$
lockTimeField.setAccessible(true);
lockTimeField.setLong(null, lockTime);
logger.info("Using a max lock acquire time of " + StringHelper.formatMillisecondsDuration(lockTime)); //$NON-NLS-1$
} catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("Failed to configure tryLockTime on LockableObject!", e); //$NON-NLS-1$
}
// validate base path
validateBasePath(properties);
this.properties = properties;
this.verbose = verbose;
this.defaultIoMode = ioMode;
this.realmMap = new HashMap<>();
this.ctxFactory = new PersistenceContextFactoryDelegator();
}
private void validateBasePath(Properties properties) {
String context = DefaultPersistenceManager.class.getSimpleName();
String basePath = PropertiesHelper.getProperty(properties, context, PersistenceConstants.PROP_BASEPATH, null);
// validate base path exists and is writable
File basePathF = new File(basePath);
if (!basePathF.exists())
throw new XmlPersistenceException(MessageFormat.format("The database store path does not exist at {0}", //$NON-NLS-1$
basePathF.getAbsolutePath()));
if (!basePathF.canWrite())
throw new XmlPersistenceException(MessageFormat.format("The database store path is not writeable at {0}", //$NON-NLS-1$
basePathF.getAbsolutePath()));
}
@Override
public PersistenceContextFactoryDelegator getCtxFactory() {
return this.ctxFactory;
}
@Override
public PersistenceTransaction openTx() {
return openTx(DEFAULT_REALM);
}
@Override
public synchronized PersistenceTransaction openTx(String realmName) {
DefaultPersistenceRealm persistenceRealm = this.realmMap.get(realmName);
if (persistenceRealm == null) {
PathBuilder pathBuilder = new PathBuilder(realmName, this.properties);
ObjectReferenceCache objectRefCache = new ObjectReferenceCache(realmName);
persistenceRealm = new DefaultPersistenceRealm(realmName, this, this.ctxFactory, pathBuilder,
objectRefCache);
this.realmMap.put(realmName, persistenceRealm);
}
PersistenceTransaction tx = new DefaultPersistenceTransaction(persistenceRealm, this.verbose);
tx.setIoMode(this.defaultIoMode);
return tx;
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.impl;
import ch.eitchnet.xmlpers.api.PersistenceContextFactoryDelegator;
import ch.eitchnet.xmlpers.api.PersistenceManager;
import ch.eitchnet.xmlpers.api.PersistenceRealm;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
public class DefaultPersistenceRealm implements PersistenceRealm {
private final PersistenceManager persistenceManager;
private final PersistenceContextFactoryDelegator ctxFactory;
private final String realmName;
private final ObjectReferenceCache objectRefCache;
private final PathBuilder pathBuilder;
public DefaultPersistenceRealm(String realm, PersistenceManager persistenceManager,
PersistenceContextFactoryDelegator ctxFactory, PathBuilder pathBuilder, ObjectReferenceCache objectRefCache) {
this.ctxFactory = ctxFactory;
this.pathBuilder = pathBuilder;
this.realmName = realm;
this.persistenceManager = persistenceManager;
this.objectRefCache = objectRefCache;
}
/**
* @return the realm
*/
@Override
public String getRealmName() {
return this.realmName;
}
@Override
public PersistenceContextFactoryDelegator getCtxFactoryDelegator() {
return this.ctxFactory;
}
/**
* @return the objectRefCache
*/
@Override
public ObjectReferenceCache getObjectRefCache() {
return this.objectRefCache;
}
/**
* @return the persistenceManager
*/
@Override
public PersistenceManager getPersistenceManager() {
return this.persistenceManager;
}
/**
* @return the pathBuilder
*/
PathBuilder getPathBuilder() {
return this.pathBuilder;
}
}

View File

@ -0,0 +1,324 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.impl;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.StringHelper;
import ch.eitchnet.utils.objectfilter.ObjectFilter;
import ch.eitchnet.xmlpers.api.FileDao;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.MetadataDao;
import ch.eitchnet.xmlpers.api.ModificationResult;
import ch.eitchnet.xmlpers.api.ObjectDao;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceRealm;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.api.TransactionCloseStrategy;
import ch.eitchnet.xmlpers.api.TransactionResult;
import ch.eitchnet.xmlpers.api.TransactionState;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class DefaultPersistenceTransaction implements PersistenceTransaction {
private static final Logger logger = LoggerFactory.getLogger(DefaultPersistenceTransaction.class);
private final DefaultPersistenceRealm realm;
private final boolean verbose;
private final ObjectFilter objectFilter;
private final ObjectDao objectDao;
private final MetadataDao metadataDao;
private FileDao fileDao;
private IoMode ioMode;
private TransactionCloseStrategy closeStrategy;
private TransactionState state;
private long startTime;
private Date startTimeDate;
private TransactionResult txResult;
public DefaultPersistenceTransaction(DefaultPersistenceRealm realm, boolean verbose) {
this.startTime = System.nanoTime();
this.startTimeDate = new Date();
this.realm = realm;
this.verbose = verbose;
this.objectFilter = new ObjectFilter();
this.fileDao = new FileDao(this, realm.getPathBuilder(), verbose);
this.objectDao = new ObjectDao(this, this.fileDao, this.objectFilter);
this.metadataDao = new MetadataDao(realm.getPathBuilder(), this, verbose);
this.closeStrategy = TransactionCloseStrategy.COMMIT;
this.state = TransactionState.OPEN;
}
@Override
public void setTransactionResult(TransactionResult txResult) throws IllegalStateException {
if (this.txResult != null) {
String msg = "The transaction already has a result set!"; //$NON-NLS-1$
throw new IllegalStateException(msg);
}
this.txResult = txResult;
}
@Override
public TransactionResult getTransactionResult() throws IllegalStateException {
if (isOpen()) {
String msg = "The transaction is still open thus has no result yet! Either commit or rollback before calling this method"; //$NON-NLS-1$
throw new IllegalStateException(msg);
}
return this.txResult;
}
@Override
public PersistenceRealm getRealm() {
return this.realm;
}
@Override
public ObjectDao getObjectDao() {
return this.objectDao;
}
@Override
public MetadataDao getMetadataDao() {
return this.metadataDao;
}
@Override
public FileDao getFileDao() {
return this.fileDao;
}
@Override
public ObjectReferenceCache getObjectRefCache() {
return this.realm.getObjectRefCache();
}
@Override
public void setCloseStrategy(TransactionCloseStrategy closeStrategy) {
this.closeStrategy = closeStrategy;
}
@Override
public void close() throws XmlPersistenceException {
this.closeStrategy.close(this);
}
@Override
public void autoCloseableRollback() {
long start = System.nanoTime();
if (this.state == TransactionState.COMMITTED)
throw new IllegalStateException("Transaction has already been committed!"); //$NON-NLS-1$
if (this.state != TransactionState.ROLLED_BACK) {
unlockObjectRefs();
this.state = TransactionState.ROLLED_BACK;
this.objectFilter.clearCache();
long end = System.nanoTime();
long txDuration = end - this.startTime;
long closeDuration = end - start;
if (this.txResult != null) {
this.txResult.clear();
this.txResult.setState(this.state);
this.txResult.setStartTime(this.startTimeDate);
this.txResult.setTxDuration(txDuration);
this.txResult.setCloseDuration(closeDuration);
this.txResult.setRealm(this.realm.getRealmName());
this.txResult.setModificationByKey(Collections.<String, ModificationResult> emptyMap());
}
}
}
@Override
public void autoCloseableCommit() throws XmlPersistenceException {
long start = System.nanoTime();
try {
if (this.verbose) {
String msg = "Committing {0} operations in TX...";//$NON-NLS-1$
logger.info(MessageFormat.format(msg, this.objectFilter.sizeCache()));
}
Set<String> keySet = this.objectFilter.keySet();
Map<String, ModificationResult> modifications;
if (this.txResult == null)
modifications = null;
else
modifications = new HashMap<>(keySet.size());
for (String key : keySet) {
List<Object> removed = this.objectFilter.getRemoved(key);
if (removed.isEmpty()) {
if (this.verbose)
logger.info("No objects removed in this tx."); //$NON-NLS-1$
} else {
if (this.verbose)
logger.info(removed.size() + " objects removed in this tx."); //$NON-NLS-1$
for (Object object : removed) {
@SuppressWarnings("unchecked")
PersistenceContext<Object> ctx = (PersistenceContext<Object>) object;
this.fileDao.performDelete(ctx);
}
}
List<Object> updated = this.objectFilter.getUpdated(key);
if (updated.isEmpty()) {
if (this.verbose)
logger.info("No objects updated in this tx."); //$NON-NLS-1$
} else {
if (this.verbose)
logger.info(updated.size() + " objects updated in this tx."); //$NON-NLS-1$
for (Object object : updated) {
@SuppressWarnings("unchecked")
PersistenceContext<Object> ctx = (PersistenceContext<Object>) object;
this.fileDao.performUpdate(ctx);
}
}
List<Object> added = this.objectFilter.getAdded(key);
if (added.isEmpty()) {
if (this.verbose)
logger.info("No objects added in this tx."); //$NON-NLS-1$
} else {
if (this.verbose)
logger.info(added.size() + " objects added in this tx."); //$NON-NLS-1$
for (Object object : added) {
@SuppressWarnings("unchecked")
PersistenceContext<Object> ctx = (PersistenceContext<Object>) object;
this.fileDao.performCreate(ctx);
}
}
if (modifications != null) {
ModificationResult result = new ModificationResult(key, added, updated, removed);
modifications.put(key, result);
}
}
if (this.txResult != null) {
this.txResult.clear();
this.txResult.setState(TransactionState.COMMITTED);
this.txResult.setModificationByKey(modifications);
}
} catch (Exception e) {
if (this.txResult == null) {
long end = System.nanoTime();
long txDuration = end - this.startTime;
long closeDuration = end - start;
StringBuilder sb = new StringBuilder();
sb.append("TX has failed after "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(txDuration));
sb.append(" with close operation taking "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(closeDuration));
logger.info(sb.toString());
throw e;
}
this.txResult.clear();
this.txResult.setState(TransactionState.FAILED);
this.txResult.setModificationByKey(Collections.<String, ModificationResult> emptyMap());
this.txResult.setFailCause(e);
} finally {
// clean up
unlockObjectRefs();
this.objectFilter.clearCache();
}
long end = System.nanoTime();
long txDuration = end - this.startTime;
long closeDuration = end - start;
if (this.txResult == null) {
StringBuilder sb = new StringBuilder();
sb.append("TX was completed after "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(txDuration));
sb.append(" with close operation taking "); //$NON-NLS-1$
sb.append(StringHelper.formatNanoDuration(closeDuration));
logger.info(sb.toString());
} else {
this.txResult.setStartTime(this.startTimeDate);
this.txResult.setTxDuration(txDuration);
this.txResult.setCloseDuration(closeDuration);
this.txResult.setRealm(this.realm.getRealmName());
if (this.txResult.getState() == TransactionState.FAILED) {
String msg = "Failed to commit TX due to underlying exception: {0}"; //$NON-NLS-1$
String causeMsg = this.txResult.getFailCause() == null ? null : this.txResult.getFailCause()
.getMessage();
msg = MessageFormat.format(msg, causeMsg);
throw new XmlPersistenceException(msg, this.txResult.getFailCause());
}
}
}
@SuppressWarnings("rawtypes")
private void unlockObjectRefs() {
List<PersistenceContext> lockedObjects = this.objectFilter.getAll(PersistenceContext.class);
for (PersistenceContext lockedObject : lockedObjects) {
lockedObject.getObjectRef().unlock();
}
}
@Override
public boolean isOpen() {
return this.state == TransactionState.OPEN;
}
@Override
public void setIoMode(IoMode ioMode) {
this.ioMode = ioMode;
}
@Override
public IoMode getIoMode() {
return this.ioMode;
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.impl;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.PropertiesHelper;
import ch.eitchnet.utils.helper.StringHelper;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PathBuilder {
private static final Logger logger = LoggerFactory.getLogger(PathBuilder.class);
public static final String FILE_EXT = ".xml"; //$NON-NLS-1$
public static final int EXT_LENGTH = PathBuilder.FILE_EXT.length();
private final String basePath;
public PathBuilder(String realm, Properties properties) {
// get properties
String context = PathBuilder.class.getSimpleName();
String basePath = PropertiesHelper.getProperty(properties, context, PersistenceConstants.PROP_BASEPATH, null);
// validate base path exists and is writable
File basePathF = new File(basePath);
if (!basePathF.exists())
throw new XmlPersistenceException(MessageFormat.format("The database store path does not exist at {0}", //$NON-NLS-1$
basePathF.getAbsolutePath()));
if (!basePathF.canWrite())
throw new XmlPersistenceException(MessageFormat.format("The database store path is not writeable at {0}", //$NON-NLS-1$
basePathF.getAbsolutePath()));
File realmPathF = new File(basePathF, realm);
if (!realmPathF.exists() && !realmPathF.mkdir())
throw new XmlPersistenceException(MessageFormat.format("Could not create path for realm {0} at {1}", //$NON-NLS-1$
realm, basePathF.getAbsolutePath()));
// we want a clean base path
String canonicalBasePath;
try {
canonicalBasePath = realmPathF.getCanonicalPath();
} catch (IOException e) {
throw new XmlPersistenceException(MessageFormat.format(
"Failed to build canonical path from {0}", realmPathF.getAbsolutePath()), e); //$NON-NLS-1$
}
// this.basePathF = basePathF;
this.basePath = canonicalBasePath;
logger.info(MessageFormat.format("Using base path {0}", this.basePath)); //$NON-NLS-1$
}
String getFilename(String id) {
return id.concat(PathBuilder.FILE_EXT);
}
String getPathAsString(String type, String subType, String id) {
StringBuilder sb = new StringBuilder(this.basePath);
if (StringHelper.isNotEmpty(type)) {
sb.append(File.separatorChar);
sb.append(type);
}
if (StringHelper.isNotEmpty(subType)) {
sb.append(File.separatorChar);
sb.append(subType);
}
if (StringHelper.isNotEmpty(id)) {
sb.append(File.separatorChar);
sb.append(getFilename(id));
}
return sb.toString();
}
public File getRootPath() {
File path = new File(getPathAsString(null, null, null));
return path;
}
public File getTypePath(String type) {
File path = new File(getPathAsString(type, null, null));
return path;
}
public File getSubTypePath(String type, String subType) {
File path = new File(getPathAsString(type, subType, null));
return path;
}
public File getIdOfTypePath(String type, String id) {
File path = new File(getPathAsString(type, null, id));
return path;
}
public File getIdOfSubTypePath(String type, String subType, String id) {
File path = new File(getPathAsString(type, subType, id));
return path;
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.io.File;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceContextFactory;
import ch.eitchnet.xmlpers.api.PersistenceContextFactoryDelegator;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public class IdOfSubTypeRef extends ObjectRef {
private final String type;
private final String subType;
private final String id;
public IdOfSubTypeRef(String realmName, String type, String subType, String id) {
super(realmName, RefNameCreator.createIdOfSubTypeName(realmName, type, subType, id));
this.type = type;
this.subType = subType;
this.id = id;
}
@Override
public String getType() {
return this.type;
}
public String getSubType() {
return this.subType;
}
/**
* @return the id
*/
public String getId() {
return this.id;
}
@Override
public boolean isRoot() {
return false;
}
@Override
public boolean isLeaf() {
return true;
}
@Override
public ObjectRef getParent(PersistenceTransaction tx) {
return tx.getRealm().getObjectRefCache().getSubTypeRef(this.type, this.subType);
}
@Override
public ObjectRef getChildIdRef(PersistenceTransaction tx, String id) {
String msg = MessageFormat.format("Already a leaf: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public ObjectRef getChildTypeRef(PersistenceTransaction tx, String type) {
String msg = MessageFormat.format("Already a leaf: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public File getPath(PathBuilder pathBuilder) {
return pathBuilder.getIdOfSubTypePath(this.type, this.subType, this.id);
}
@Override
public <T> PersistenceContext<T> createPersistenceContext(PersistenceTransaction tx) {
PersistenceContextFactoryDelegator ctxFactoryDelegator = tx.getRealm().getCtxFactoryDelegator();
PersistenceContextFactory<T> persistenceContextFactory = ctxFactoryDelegator.<T> getCtxFactory(this.type);
return persistenceContextFactory.createCtx(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode());
result = prime * result + ((this.subType == null) ? 0 : this.subType.hashCode());
result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IdOfSubTypeRef other = (IdOfSubTypeRef) obj;
if (this.realmName == null) {
if (other.realmName != null)
return false;
} else if (!this.realmName.equals(other.realmName))
return false;
if (this.id == null) {
if (other.id != null)
return false;
} else if (!this.id.equals(other.id))
return false;
if (this.subType == null) {
if (other.subType != null)
return false;
} else if (!this.subType.equals(other.subType))
return false;
if (this.type == null) {
if (other.type != null)
return false;
} else if (!this.type.equals(other.type))
return false;
return true;
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.io.File;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceContextFactory;
import ch.eitchnet.xmlpers.api.PersistenceContextFactoryDelegator;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public class IdOfTypeRef extends ObjectRef {
private final String type;
private final String id;
public IdOfTypeRef(String realmName, String type, String id) {
super(realmName, RefNameCreator.createIdOfTypeName(realmName, type, id));
this.type = type;
this.id = id;
}
@Override
public String getType() {
return this.type;
}
/**
* @return the id
*/
public String getId() {
return this.id;
}
@Override
public boolean isRoot() {
return false;
}
@Override
public boolean isLeaf() {
return true;
}
@Override
public ObjectRef getParent(PersistenceTransaction tx) {
return tx.getRealm().getObjectRefCache().getTypeRef(this.type);
}
@Override
public ObjectRef getChildIdRef(PersistenceTransaction tx, String id) {
String msg = MessageFormat.format("Already a leaf: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public ObjectRef getChildTypeRef(PersistenceTransaction tx, String type) {
String msg = MessageFormat.format("Already a leaf: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public File getPath(PathBuilder pathBuilder) {
return pathBuilder.getIdOfTypePath(this.type, this.id);
}
@Override
public <T> PersistenceContext<T> createPersistenceContext(PersistenceTransaction tx) {
PersistenceContextFactoryDelegator ctxFactoryDelegator = tx.getRealm().getCtxFactoryDelegator();
PersistenceContextFactory<T> persistenceContextFactory = ctxFactoryDelegator.<T> getCtxFactory(this.type);
return persistenceContextFactory.createCtx(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode());
result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IdOfTypeRef other = (IdOfTypeRef) obj;
if (this.realmName == null) {
if (other.realmName != null)
return false;
} else if (!this.realmName.equals(other.realmName))
return false;
if (this.id == null) {
if (other.id != null)
return false;
} else if (!this.id.equals(other.id))
return false;
if (this.type == null) {
if (other.type != null)
return false;
} else if (!this.type.equals(other.type))
return false;
return true;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.text.MessageFormat;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.StringHelper;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
public class LockableObject {
private static final Logger logger = LoggerFactory.getLogger(LockableObject.class);
private static long tryLockTime = 10000L;
private final ReentrantLock lock;
public LockableObject() {
this.lock = new ReentrantLock(true);
}
public static long getLockTime() {
return tryLockTime;
}
/**
* @see java.util.concurrent.locks.ReentrantLock#tryLock(long, TimeUnit)
*/
public void lock() {
try {
if (!this.lock.tryLock(tryLockTime, TimeUnit.MILLISECONDS)) {
String msg = "Failed to acquire lock after {0} for {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, StringHelper.formatMillisecondsDuration(tryLockTime), toString());
throw new XmlPersistenceException(msg);
}
if (logger.isDebugEnabled())
logger.debug("locked " + toString()); //$NON-NLS-1$
} catch (InterruptedException e) {
throw new XmlPersistenceException("Thread interrupted: " + e.getMessage(), e); //$NON-NLS-1$
}
}
/**
* @see java.util.concurrent.locks.ReentrantLock#unlock()
*/
public void unlock() {
this.lock.unlock();
if (logger.isDebugEnabled())
logger.debug("unlocking " + toString()); //$NON-NLS-1$
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.io.File;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public abstract class ObjectRef extends LockableObject {
protected final String realmName;
protected final String name;
protected ObjectRef(String realmName, String name) {
this.realmName = realmName;
this.name = name;
}
public String getRealmName() {
return this.realmName;
}
public String getName() {
return this.name;
}
public abstract boolean isRoot();
public abstract boolean isLeaf();
public abstract String getType();
public abstract ObjectRef getParent(PersistenceTransaction tx);
public abstract ObjectRef getChildIdRef(PersistenceTransaction tx, String id);
public abstract ObjectRef getChildTypeRef(PersistenceTransaction tx, String type);
public abstract File getPath(PathBuilder pathBuilder);
public abstract <T> PersistenceContext<T> createPersistenceContext(PersistenceTransaction tx);
@Override
public String toString() {
return getName();
}
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.util.HashMap;
import java.util.Map;
import ch.eitchnet.utils.helper.StringHelper;
public class ObjectReferenceCache {
private final String realmName;
private final RootRef rootRef;
private final Map<String, TypeRef> typeRefMap;
private final Map<String, SubTypeRef> subTypeRefMap;
private final Map<String, IdOfTypeRef> idOfTypeRefMap;
private final Map<String, IdOfSubTypeRef> idOfSubTypeRefMap;
public ObjectReferenceCache(String realmName) {
if (StringHelper.isEmpty(realmName))
throw new IllegalArgumentException("Realm name may not be empty!"); //$NON-NLS-1$
this.realmName = realmName;
this.rootRef = new RootRef(this.realmName);
this.typeRefMap = new HashMap<>();
this.subTypeRefMap = new HashMap<>();
this.idOfTypeRefMap = new HashMap<>();
this.idOfSubTypeRefMap = new HashMap<>();
}
public String getRealmName() {
return this.realmName;
}
public synchronized RootRef getRootRef() {
return this.rootRef;
}
public synchronized TypeRef getTypeRef(String type) {
String key = RefNameCreator.createTypeName(this.realmName, type);
TypeRef ref = this.typeRefMap.get(key);
if (ref == null) {
ref = new TypeRef(this.realmName, type);
this.typeRefMap.put(key, ref);
}
return ref;
}
public synchronized SubTypeRef getSubTypeRef(String type, String subType) {
String key = RefNameCreator.createSubTypeName(this.realmName, type, subType);
SubTypeRef ref = this.subTypeRefMap.get(key);
if (ref == null) {
ref = new SubTypeRef(this.realmName, type, subType);
this.subTypeRefMap.put(key, ref);
}
return ref;
}
public synchronized IdOfTypeRef getIdOfTypeRef(String type, String id) {
String key = RefNameCreator.createIdOfTypeName(this.realmName, type, id);
IdOfTypeRef idRef = this.idOfTypeRefMap.get(key);
if (idRef == null) {
idRef = new IdOfTypeRef(this.realmName, type, id);
this.idOfTypeRefMap.put(key, idRef);
}
return idRef;
}
public synchronized IdOfSubTypeRef getIdOfSubTypeRef(String type, String subType, String id) {
String key = RefNameCreator.createIdOfSubTypeName(this.realmName, type, subType, id);
IdOfSubTypeRef idRef = this.idOfSubTypeRefMap.get(key);
if (idRef == null) {
idRef = new IdOfSubTypeRef(this.realmName, type, subType, id);
this.idOfSubTypeRefMap.put(key, idRef);
}
return idRef;
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import ch.eitchnet.utils.helper.StringHelper;
public class RefNameCreator {
protected static final String SLASH = "/"; //$NON-NLS-1$
// FIXME validate each name part that it is a valid literal for file names...
public static String createRootName(String realmName) {
assertRealmName(realmName);
return SLASH + realmName + SLASH;
}
public static String createTypeName(String realmName, String type) {
assertRealmName(realmName);
assertType(type);
return SLASH + realmName + SLASH + type + SLASH;
}
public static String createIdOfTypeName(String realmName, String type, String id) {
assertRealmName(realmName);
assertType(type);
assertId(id);
return SLASH + realmName + SLASH + type + SLASH + id;
}
public static String createSubTypeName(String realmName, String type, String subType) {
assertRealmName(realmName);
assertType(type);
assertSubType(subType);
return SLASH + realmName + SLASH + type + SLASH + subType + SLASH;
}
public static String createIdOfSubTypeName(String realmName, String type, String subType, String id) {
assertRealmName(realmName);
assertType(type);
assertSubType(subType);
assertId(id);
return SLASH + realmName + SLASH + type + SLASH + subType + SLASH + id;
}
private static void assertRealmName(String realmName) {
if (StringHelper.isEmpty(realmName)) {
throw new IllegalArgumentException("Realm name may not be empty!"); //$NON-NLS-1$
}
}
private static void assertType(String type) {
if (StringHelper.isEmpty(type)) {
throw new IllegalArgumentException("type may not be empty!"); //$NON-NLS-1$
}
}
private static void assertSubType(String subType) {
if (StringHelper.isEmpty(subType)) {
throw new IllegalArgumentException("subType may not be empty!"); //$NON-NLS-1$
}
}
private static void assertId(String id) {
if (StringHelper.isEmpty(id)) {
throw new IllegalArgumentException("id may not be empty!"); //$NON-NLS-1$
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.io.File;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public class RootRef extends ObjectRef {
public RootRef(String realmName) {
super(realmName, RefNameCreator.createRootName(realmName));
}
@Override
public boolean isRoot() {
return true;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public String getType() {
String msg = MessageFormat.format("RootRef has no type: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public ObjectRef getParent(PersistenceTransaction tx) {
String msg = MessageFormat.format("RootRef has no parent: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public ObjectRef getChildIdRef(PersistenceTransaction tx, String id) {
String msg = MessageFormat.format("RootRef has no child id: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public ObjectRef getChildTypeRef(PersistenceTransaction tx, String type) {
return tx.getRealm().getObjectRefCache().getTypeRef(type);
}
@Override
public File getPath(PathBuilder pathBuilder) {
return pathBuilder.getRootPath();
}
@Override
public <T> PersistenceContext<T> createPersistenceContext(PersistenceTransaction tx) {
String msg = MessageFormat.format("{0} is not a leaf and can thus not have a Persistence Context", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RootRef other = (RootRef) obj;
if (this.realmName == null) {
if (other.realmName != null)
return false;
} else if (!this.realmName.equals(other.realmName))
return false;
return true;
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.io.File;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public class SubTypeRef extends ObjectRef {
private final String type;
private final String subType;
public SubTypeRef(String realmName, String type, String subType) {
super(realmName, RefNameCreator.createSubTypeName(realmName, type, subType));
this.type = type;
this.subType = subType;
}
@Override
public String getType() {
return this.type;
}
public String getSubType() {
return this.subType;
}
@Override
public boolean isRoot() {
return false;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public ObjectRef getParent(PersistenceTransaction tx) {
return tx.getRealm().getObjectRefCache().getTypeRef(this.type);
}
@Override
public ObjectRef getChildIdRef(PersistenceTransaction tx, String id) {
return tx.getRealm().getObjectRefCache().getIdOfSubTypeRef(this.type, this.subType, id);
}
@Override
public ObjectRef getChildTypeRef(PersistenceTransaction tx, String type) {
String msg = MessageFormat.format("A SubType has no child type: {0}", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public File getPath(PathBuilder pathBuilder) {
return pathBuilder.getSubTypePath(this.type, this.subType);
}
@Override
public <T> PersistenceContext<T> createPersistenceContext(PersistenceTransaction tx) {
String msg = MessageFormat.format("{0} is not a leaf and can thus not have a Persistence Context", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode());
result = prime * result + ((this.subType == null) ? 0 : this.subType.hashCode());
result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SubTypeRef other = (SubTypeRef) obj;
if (this.realmName == null) {
if (other.realmName != null)
return false;
} else if (!this.realmName.equals(other.realmName))
return false;
if (this.subType == null) {
if (other.subType != null)
return false;
} else if (!this.subType.equals(other.subType))
return false;
if (this.type == null) {
if (other.type != null)
return false;
} else if (!this.type.equals(other.type))
return false;
return true;
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.objref;
import java.io.File;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public class TypeRef extends ObjectRef {
private final String type;
public TypeRef(String realmName, String type) {
super(realmName, RefNameCreator.createTypeName(realmName, type));
this.type = type;
}
@Override
public String getType() {
return this.type;
}
@Override
public boolean isRoot() {
return false;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public ObjectRef getParent(PersistenceTransaction tx) {
return tx.getRealm().getObjectRefCache().getRootRef();
}
@Override
public ObjectRef getChildIdRef(PersistenceTransaction tx, String id) {
return tx.getRealm().getObjectRefCache().getIdOfTypeRef(this.type, id);
}
@Override
public ObjectRef getChildTypeRef(PersistenceTransaction tx, String type) {
return tx.getRealm().getObjectRefCache().getSubTypeRef(this.type, type);
}
@Override
public File getPath(PathBuilder pathBuilder) {
return pathBuilder.getTypePath(this.type);
}
@Override
public <T> PersistenceContext<T> createPersistenceContext(PersistenceTransaction tx) {
String msg = MessageFormat.format("{0} is not a leaf and can thus not have a Persistence Context", getName()); //$NON-NLS-1$
throw new UnsupportedOperationException(msg);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.realmName == null) ? 0 : this.realmName.hashCode());
result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TypeRef other = (TypeRef) obj;
if (this.realmName == null) {
if (other.realmName != null)
return false;
} else if (!this.realmName.equals(other.realmName))
return false;
if (this.type == null) {
if (other.type != null)
return false;
} else if (!this.type.equals(other.type))
return false;
return true;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.util;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.objref.ObjectRef;
public class AssertionUtil {
public static void assertNotNull(Object object) {
if (object == null)
throw new RuntimeException("Object may not be null!"); //$NON-NLS-1$
}
public static void assertObjectRead(PersistenceContext<?> context) {
if (context.getObject() == null) {
String msg = "Failed to read object with for {0}"; //$NON-NLS-1$
ObjectRef objectRef = context.getObjectRef();
msg = MessageFormat.format(msg, objectRef.getName());
throw new RuntimeException(msg);
}
}
public static void assertIsIdRef(ObjectRef objectRef) {
if (!objectRef.isLeaf()) {
String msg = "Expected IdRef not {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName());
throw new IllegalArgumentException(msg);
}
}
public static void assertIsNotIdRef(ObjectRef objectRef) {
if (objectRef.isLeaf()) {
String msg = "IdRef not expected {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName());
throw new IllegalArgumentException(msg);
}
}
public static void assertIsNotRootRef(ObjectRef objectRef) {
if (objectRef.isRoot()) {
String msg = "RootRef not expected {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, objectRef.getName());
throw new IllegalArgumentException(msg);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.util;
import java.text.MessageFormat;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class DomUtil {
public static DocumentBuilder createDocumentBuilder() {
try {
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
return docBuilder;
} catch (ParserConfigurationException e) {
String msg = "No Xml Parser could be loaded: {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, e.getMessage());
throw new XmlPersistenceException(msg, e);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.util;
import java.text.MessageFormat;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
import ch.eitchnet.xmlpers.impl.PathBuilder;
public class FilenameUtility {
public static String getId(String filename) {
assertFilename(filename);
return filename.substring(0, filename.length() - PathBuilder.EXT_LENGTH);
}
public static void assertFilename(String filename) {
if (filename.charAt(filename.length() - PathBuilder.EXT_LENGTH) != '.') {
String msg = "The filename does not have a . (dot) at index {0}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, (filename.length() - PathBuilder.EXT_LENGTH));
throw new XmlPersistenceException(msg);
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import java.io.File;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.eitchnet.utils.helper.FileHelper;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceManager;
import ch.eitchnet.xmlpers.api.PersistenceManagerLoader;
import ch.eitchnet.xmlpers.test.impl.BookContextFactory;
import ch.eitchnet.xmlpers.test.impl.MyModelContextFactory;
import ch.eitchnet.xmlpers.test.impl.TestConstants;
import ch.eitchnet.xmlpers.test.model.Book;
import ch.eitchnet.xmlpers.test.model.MyModel;
public abstract class AbstractPersistenceTest {
protected static final Logger logger = LoggerFactory.getLogger(AbstractPersistenceTest.class);
protected PersistenceManager persistenceManager;
@SuppressWarnings("nls")
protected static void cleanPath(String path) {
File file = new File(path).getAbsoluteFile();
File parent = file.getParentFile();
if (!parent.getAbsolutePath().endsWith("/target/db"))
throw new RuntimeException("Bad parent! Must be /target/db/: " + parent);
if (!parent.exists() && !parent.mkdirs())
throw new RuntimeException("Failed to create path " + parent);
if (file.exists() && file.isDirectory())
if (!FileHelper.deleteFiles(file.listFiles(), true))
throw new RuntimeException("Could not clean up path " + file.getAbsolutePath());
if (!file.exists() && !file.mkdir())
throw new RuntimeException("Failed to create path " + file);
File domFile = new File(file, IoMode.DOM.name());
if (!domFile.mkdir())
throw new RuntimeException("Failed to create path " + domFile);
File saxFile = new File(file, IoMode.SAX.name());
if (!saxFile.mkdir())
throw new RuntimeException("Failed to create path " + saxFile);
}
protected void setup(Properties properties) {
properties.setProperty(PersistenceConstants.PROP_VERBOSE, "true"); //$NON-NLS-1$
this.persistenceManager = PersistenceManagerLoader.load(properties);
this.persistenceManager.getCtxFactory().registerPersistenceContextFactory(MyModel.class,
TestConstants.TYPE_RES, new MyModelContextFactory());
this.persistenceManager.getCtxFactory().registerPersistenceContextFactory(Book.class, TestConstants.TYPE_BOOK,
new BookContextFactory());
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertResource;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertResourceUpdated;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.createResource;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.updateResource;
import static org.junit.Assert.assertNull;
import java.util.Properties;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.eitchnet.xmlpers.api.FileDao;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceContextFactory;
import ch.eitchnet.xmlpers.api.PersistenceContextFactoryDelegator;
import ch.eitchnet.xmlpers.api.PersistenceManager;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.impl.DefaultPersistenceRealm;
import ch.eitchnet.xmlpers.impl.DefaultPersistenceTransaction;
import ch.eitchnet.xmlpers.impl.PathBuilder;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
import ch.eitchnet.xmlpers.test.model.MyModel;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
@SuppressWarnings("nls")
public class FileDaoTest extends AbstractPersistenceTest {
private static final String TEST_PATH = "target/db/FileDaoTest/";
private static final boolean VERBOSE = true;
private DefaultPersistenceRealm realm;
private PathBuilder pathBuilder;
@BeforeClass
public static void beforeClass() {
cleanPath(TEST_PATH);
}
private void setup(IoMode ioMode) {
Properties properties = new Properties();
properties.setProperty(PersistenceConstants.PROP_BASEPATH, TEST_PATH + ioMode.name());
setup(properties);
ObjectReferenceCache objectRefCache = new ObjectReferenceCache(PersistenceManager.DEFAULT_REALM);
this.pathBuilder = new PathBuilder(PersistenceManager.DEFAULT_REALM, properties);
this.realm = new DefaultPersistenceRealm(PersistenceManager.DEFAULT_REALM, this.persistenceManager,
this.persistenceManager.getCtxFactory(), this.pathBuilder, objectRefCache);
}
@Test
public void testCrudSax() {
setup(IoMode.SAX);
try (PersistenceTransaction tx = new DefaultPersistenceTransaction(this.realm, VERBOSE)) {
FileDao fileDao = new FileDao(tx, this.pathBuilder, VERBOSE);
tx.setIoMode(IoMode.SAX);
testCrud(this.realm.getCtxFactoryDelegator(), fileDao);
}
}
@Test
public void testCrudDom() {
setup(IoMode.DOM);
try (PersistenceTransaction tx = new DefaultPersistenceTransaction(this.realm, VERBOSE)) {
FileDao fileDao = new FileDao(tx, this.pathBuilder, VERBOSE);
tx.setIoMode(IoMode.DOM);
testCrud(this.realm.getCtxFactoryDelegator(), fileDao);
}
}
private void testCrud(PersistenceContextFactoryDelegator ctxFactoryDelegator, FileDao fileDao) {
MyModel resource = createResource();
assertResource(resource);
Class<? extends MyModel> classType = resource.getClass();
PersistenceContextFactory<MyModel> ctxFactory = ctxFactoryDelegator.getCtxFactory(classType);
ObjectReferenceCache objectRefCache = this.realm.getObjectRefCache();
PersistenceContext<MyModel> context = ctxFactory.createCtx(objectRefCache, resource);
context.setObject(resource);
fileDao.performCreate(context);
context.setObject(null);
fileDao.performRead(context);
assertResource(context.getObject());
updateResource(context.getObject());
fileDao.performUpdate(context);
context.setObject(null);
fileDao.performRead(context);
assertResourceUpdated(context.getObject());
fileDao.performDelete(context);
context.setObject(null);
fileDao.performRead(context);
assertNull(context.getObject());
context.setObject(createResource());
fileDao.performCreate(context);
}
}

View File

@ -0,0 +1,233 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import static ch.eitchnet.xmlpers.test.impl.TestConstants.TYPE_RES;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.RES_TYPE;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.createResource;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.updateResource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.objref.IdOfSubTypeRef;
import ch.eitchnet.xmlpers.objref.LockableObject;
import ch.eitchnet.xmlpers.test.model.MyModel;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class LockingTest extends AbstractPersistenceTest {
private static final String BASE_PATH = "target/db/LockingTest/"; //$NON-NLS-1$
private long waitForWorkersTime;
private boolean run;
@BeforeClass
public static void beforeClass() {
cleanPath(BASE_PATH);
}
@Before
public void before() {
Properties properties = new Properties();
properties.setProperty(PersistenceConstants.PROP_BASEPATH, BASE_PATH + IoMode.DOM.name());
properties.setProperty(PersistenceConstants.PROP_LOCK_TIME_MILLIS, Long.toString(500L));
setup(properties);
this.waitForWorkersTime = LockableObject.getLockTime() + getWaitForWorkersTime() + 300L;
}
@Test
public void shouldLockObjects() throws InterruptedException {
List<CreateResourceWorker> workers = new ArrayList<>(5);
String resoureId = "worker"; //$NON-NLS-1$
for (int i = 0; i < 5; i++) {
String workerName = resoureId + "_" + i; //$NON-NLS-1$
CreateResourceWorker worker = new CreateResourceWorker(workerName, workerName);
worker.start();
workers.add(worker);
logger.info("Setup thread " + worker.getName()); //$NON-NLS-1$
}
int nrOfSuccess = runWorkers(workers);
assertEquals("Only one thread should be able to perform the TX!", 5, nrOfSuccess); //$NON-NLS-1$
}
@Test
public void shouldFailIfResourceAlreadyExists() throws InterruptedException {
List<CreateResourceWorker> workers = new ArrayList<>(5);
String resourceId = "createWorkerRes"; //$NON-NLS-1$
for (int i = 0; i < 5; i++) {
String workerName = resourceId + "_" + i; //$NON-NLS-1$
CreateResourceWorker worker = new CreateResourceWorker(workerName, resourceId);
worker.start();
workers.add(worker);
logger.info("Setup thread " + worker.getName()); //$NON-NLS-1$
}
int nrOfSuccess = runWorkers(workers);
assertEquals("Only one thread should be able to perform the TX!", 1, nrOfSuccess); //$NON-NLS-1$
}
@Test
public void shouldFailUpdateIfLockNotAcquirable() throws InterruptedException {
// prepare workers
List<UpdateResourceWorker> workers = new ArrayList<>(5);
String resourceId = "updatWorkerRes"; //$NON-NLS-1$
for (int i = 0; i < 5; i++) {
String workerName = resourceId + "_" + i; //$NON-NLS-1$
UpdateResourceWorker worker = new UpdateResourceWorker(workerName, resourceId);
worker.start();
workers.add(worker);
logger.info("Setup thread " + worker.getName()); //$NON-NLS-1$
}
// create resource which is to be updated
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
MyModel resource = createResource(resourceId);
tx.getObjectDao().add(resource);
}
int nrOfSuccess = runWorkers(workers);
assertEquals("Only one thread should be able to perform the TX!", 1, nrOfSuccess); //$NON-NLS-1$
}
private int runWorkers(List<? extends AbstractWorker> workers) throws InterruptedException {
setRun(true);
for (AbstractWorker worker : workers) {
worker.join(getWaitForWorkersTime() + 2000L);
}
int nrOfSuccess = 0;
for (AbstractWorker worker : workers) {
if (worker.isSuccess())
nrOfSuccess++;
}
return nrOfSuccess;
}
public long getWaitForWorkersTime() {
return this.waitForWorkersTime;
}
public boolean isRun() {
return this.run;
}
public void setRun(boolean run) {
this.run = run;
}
public abstract class AbstractWorker extends Thread {
protected boolean success;
protected String resourceId;
public AbstractWorker(String name, String resourceId) {
super(name);
this.resourceId = resourceId;
}
@Override
public void run() {
logger.info("Waiting for ok to work..."); //$NON-NLS-1$
while (!isRun()) {
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
logger.info("Starting work..."); //$NON-NLS-1$
try (PersistenceTransaction tx = LockingTest.this.persistenceManager.openTx()) {
doWork(tx);
try {
Thread.sleep(getWaitForWorkersTime());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.success = true;
}
logger.info("Work completed."); //$NON-NLS-1$
}
protected abstract void doWork(PersistenceTransaction tx);
public boolean isSuccess() {
return this.success;
}
}
public class CreateResourceWorker extends AbstractWorker {
public CreateResourceWorker(String name, String resourceId) {
super(name, resourceId);
}
@Override
protected void doWork(PersistenceTransaction tx) {
MyModel resource = createResource(this.resourceId);
tx.getObjectDao().add(resource);
}
}
public class UpdateResourceWorker extends AbstractWorker {
public UpdateResourceWorker(String name, String resourceId) {
super(name, resourceId);
}
@Override
protected void doWork(PersistenceTransaction tx) {
IdOfSubTypeRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, RES_TYPE, this.resourceId);
MyModel resource = tx.getObjectDao().queryById(objectRef);
assertNotNull(resource);
updateResource(resource);
tx.getObjectDao().update(resource);
}
}
}

View File

@ -0,0 +1,226 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.BOOK_ID;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertBook;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertBookUpdated;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.createBook;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.updateBook;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.ObjectDao;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.objref.IdOfTypeRef;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.objref.TypeRef;
import ch.eitchnet.xmlpers.test.impl.TestConstants;
import ch.eitchnet.xmlpers.test.model.Book;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class ObjectDaoBookTest extends AbstractPersistenceTest {
private static final String BASEPATH = "target/db/ObjectDaoTest/"; //$NON-NLS-1$
@Before
public void before() {
cleanPath(BASEPATH);
}
private void setup(IoMode ioMode) {
Properties properties = new Properties();
properties.setProperty(PersistenceConstants.PROP_BASEPATH, BASEPATH + ioMode.name());
setup(properties);
}
@Test
public void testCrudSax() {
setup(IoMode.SAX);
testCrud(IoMode.SAX);
}
@Test
public void testCrudDom() {
setup(IoMode.DOM);
testCrud(IoMode.DOM);
}
private PersistenceTransaction freshTx(IoMode ioMode) {
PersistenceTransaction tx = this.persistenceManager.openTx();
tx.setIoMode(ioMode);
return tx;
}
private void testCrud(IoMode ioMode) {
ObjectDao objectDao;
// create new book
Book book = createBook();
try (PersistenceTransaction tx = freshTx(ioMode);) {
objectDao = tx.getObjectDao();
objectDao.add(book);
}
// read book
try (PersistenceTransaction tx = freshTx(ioMode);) {
IdOfTypeRef bookRef = tx.getObjectRefCache()
.getIdOfTypeRef(TestConstants.TYPE_BOOK, Long.toString(BOOK_ID));
objectDao = tx.getObjectDao();
book = objectDao.queryById(bookRef);
assertBook(book);
// modify book
updateBook(book);
objectDao.update(book);
}
// read modified book
try (PersistenceTransaction tx = freshTx(ioMode);) {
IdOfTypeRef bookRef = tx.getObjectRefCache()
.getIdOfTypeRef(TestConstants.TYPE_BOOK, Long.toString(BOOK_ID));
objectDao = tx.getObjectDao();
book = objectDao.queryById(bookRef);
assertBookUpdated(book);
}
// delete book
try (PersistenceTransaction tx = freshTx(ioMode);) {
objectDao = tx.getObjectDao();
objectDao.remove(book);
}
// fail to read
try (PersistenceTransaction tx = freshTx(ioMode);) {
IdOfTypeRef bookRef = tx.getObjectRefCache()
.getIdOfTypeRef(TestConstants.TYPE_BOOK, Long.toString(BOOK_ID));
objectDao = tx.getObjectDao();
book = objectDao.queryById(bookRef);
assertNull(book);
// and create again
book = createBook();
assertBook(book);
objectDao.add(book);
}
}
@Test
public void testBulkSax() {
setup(IoMode.SAX);
testBulk(IoMode.SAX);
}
@Test
public void testBulkDom() {
setup(IoMode.DOM);
testBulk(IoMode.DOM);
}
private void testBulk(IoMode ioMode) {
// create a list of books
List<Book> books = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
long id = i;
String title = "Bulk Test Book. " + i; //$NON-NLS-1$
String author = "Nick Hornby"; //$NON-NLS-1$
String press = "Penguin Books"; //$NON-NLS-1$
double price = 21.30;
Book book = createBook(id, title, author, press, price);
books.add(book);
}
// save all
try (PersistenceTransaction tx = freshTx(ioMode);) {
ObjectDao objectDao = tx.getObjectDao();
objectDao.addAll(books);
books.clear();
}
// query all
try (PersistenceTransaction tx = freshTx(ioMode);) {
TypeRef typeRef = tx.getObjectRefCache().getTypeRef(TestConstants.TYPE_BOOK);
ObjectDao objectDao = tx.getObjectDao();
books = objectDao.queryAll(typeRef);
assertEquals("Expected to find 10 entries!", 10, books.size()); //$NON-NLS-1$
// delete them all
objectDao.removeAll(books);
}
// now query them again
try (PersistenceTransaction tx = freshTx(ioMode);) {
TypeRef typeRef = tx.getObjectRefCache().getTypeRef(TestConstants.TYPE_BOOK);
ObjectDao objectDao = tx.getObjectDao();
books = objectDao.queryAll(typeRef);
assertEquals("Expected to find 0 entries!", 0, books.size()); //$NON-NLS-1$
}
}
@Test
public void shouldPersistById() {
setup(IoMode.SAX);
String classType = TestConstants.TYPE_BOOK;
long id = System.currentTimeMillis();
String title = "About a boy"; //$NON-NLS-1$
String author = "Nick Hornby"; //$NON-NLS-1$
String press = "Penguin Books"; //$NON-NLS-1$
double price = 21.30;
// create a book
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
Book book = createBook(id, title, author, press, price);
tx.getObjectDao().add(book);
}
// read by id
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfTypeRef(classType, Long.toString(id));
Book book = tx.getObjectDao().queryById(objectRef);
assertNotNull("Expected to read book by ID", book); //$NON-NLS-1$
}
// delete by id
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfTypeRef(classType, Long.toString(id));
tx.getObjectDao().removeById(objectRef);
}
// fail to read by id
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfTypeRef(classType, Long.toString(id));
Book book = tx.getObjectDao().queryById(objectRef);
assertNull("Expected that book was deleted by ID, thus can not be read anymore", book); //$NON-NLS-1$
}
}
}

View File

@ -0,0 +1,357 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import static ch.eitchnet.xmlpers.test.impl.TestConstants.TYPE_RES;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.RES_ID;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.RES_TYPE;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertResource;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.assertResourceUpdated;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.createResource;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.updateResource;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.ObjectDao;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.api.XmlPersistenceException;
import ch.eitchnet.xmlpers.objref.IdOfSubTypeRef;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.objref.SubTypeRef;
import ch.eitchnet.xmlpers.test.impl.TestConstants;
import ch.eitchnet.xmlpers.test.model.ModelBuilder;
import ch.eitchnet.xmlpers.test.model.MyModel;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class ObjectDaoResourceTest extends AbstractPersistenceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private static final String BASEPATH = "target/db/ObjectDaoTest/"; //$NON-NLS-1$
@BeforeClass
public static void beforeClass() {
cleanPath(BASEPATH);
}
private void setup(IoMode ioMode) {
Properties properties = new Properties();
properties.setProperty(PersistenceConstants.PROP_BASEPATH, BASEPATH + ioMode.name());
setup(properties);
}
@Test
public void testCrudSax() {
setup(IoMode.SAX);
testCrud(IoMode.SAX);
}
@Test
public void testCrudDom() {
setup(IoMode.DOM);
testCrud(IoMode.DOM);
}
private PersistenceTransaction freshTx(IoMode ioMode) {
PersistenceTransaction tx = this.persistenceManager.openTx();
tx.setIoMode(ioMode);
return tx;
}
private void testCrud(IoMode ioMode) {
ObjectDao objectDao;
// create new resource
MyModel resource = createResource();
try (PersistenceTransaction tx = freshTx(ioMode);) {
objectDao = tx.getObjectDao();
objectDao.add(resource);
}
// read resource
try (PersistenceTransaction tx = freshTx(ioMode);) {
IdOfSubTypeRef resRef = tx.getObjectRefCache().getIdOfSubTypeRef(TestConstants.TYPE_RES, RES_TYPE, RES_ID);
objectDao = tx.getObjectDao();
resource = objectDao.queryById(resRef);
assertResource(resource);
// modify resource
updateResource(resource);
objectDao.update(resource);
}
// read modified resource
try (PersistenceTransaction tx = freshTx(ioMode);) {
IdOfSubTypeRef resRef = tx.getObjectRefCache().getIdOfSubTypeRef(TestConstants.TYPE_RES, RES_TYPE, RES_ID);
objectDao = tx.getObjectDao();
resource = objectDao.queryById(resRef);
assertResourceUpdated(resource);
}
// delete resource
try (PersistenceTransaction tx = freshTx(ioMode);) {
objectDao = tx.getObjectDao();
objectDao.remove(resource);
}
// fail to read
try (PersistenceTransaction tx = freshTx(ioMode);) {
IdOfSubTypeRef resRef = tx.getObjectRefCache().getIdOfSubTypeRef(TestConstants.TYPE_RES, RES_TYPE, RES_ID);
objectDao = tx.getObjectDao();
resource = objectDao.queryById(resRef);
assertNull(resource);
// and create again
resource = createResource();
assertResource(resource);
objectDao.add(resource);
}
}
@Test
public void testBulkSax() {
setup(IoMode.SAX);
testBulk(IoMode.SAX);
}
@Test
public void testBulkDom() {
setup(IoMode.DOM);
testBulk(IoMode.DOM);
}
private void testBulk(IoMode ioMode) {
// context
String type = "testBulk" + ioMode.name(); //$NON-NLS-1$
// create a list of resources
List<MyModel> resources = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
String id = RES_ID + "_" + i; //$NON-NLS-1$
String name = "Bulk Test Object. " + i; //$NON-NLS-1$
MyModel resource = createResource(id, name, type);
resources.add(resource);
}
// save all
try (PersistenceTransaction tx = freshTx(ioMode);) {
ObjectDao objectDao = tx.getObjectDao();
objectDao.addAll(resources);
resources.clear();
}
// query all
try (PersistenceTransaction tx = freshTx(ioMode);) {
SubTypeRef subTypeRef = tx.getObjectRefCache().getSubTypeRef(TestConstants.TYPE_RES, type);
ObjectDao objectDao = tx.getObjectDao();
resources = objectDao.queryAll(subTypeRef);
assertEquals("Expected to find 10 entries!", 10, resources.size()); //$NON-NLS-1$
// delete them all
objectDao.removeAll(resources);
}
// now query them again
try (PersistenceTransaction tx = freshTx(ioMode);) {
SubTypeRef subTypeRef = tx.getObjectRefCache().getSubTypeRef(TestConstants.TYPE_RES, type);
ObjectDao objectDao = tx.getObjectDao();
resources = objectDao.queryAll(subTypeRef);
assertEquals("Expected to find 0 entries!", 0, resources.size()); //$NON-NLS-1$
}
}
@Test
public void shouldPersistById() {
setup(IoMode.SAX);
String classType = TestConstants.TYPE_RES;
String subType = ModelBuilder.RES_TYPE;
String id = "shouldPersistById"; //$NON-NLS-1$
String name = "shouldPersistById "; //$NON-NLS-1$
// create a resource
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
MyModel resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
}
// read by id
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(classType, subType, id);
MyModel resource = tx.getObjectDao().queryById(objectRef);
assertNotNull("Expected to read resource by ID", resource); //$NON-NLS-1$
}
// delete by id
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(classType, subType, id);
tx.getObjectDao().removeById(objectRef);
}
// fail to read by id
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(classType, subType, id);
MyModel resource = tx.getObjectDao().queryById(objectRef);
assertNull("Expected that resource was deleted by ID, thus can not be read anymore", resource); //$NON-NLS-1$
}
}
@Test
public void shouldFailModifyNotExisting() {
setup(IoMode.SAX);
this.thrown.expect(XmlPersistenceException.class);
this.thrown.expectMessage(containsString("Persistence unit does not exist for")); //$NON-NLS-1$
// update
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
MyModel resource = createResource();
tx.getObjectDao().update(resource);
}
}
@Test
public void shouldFailDeleteNotExisting() {
setup(IoMode.SAX);
this.thrown.expect(XmlPersistenceException.class);
this.thrown.expectMessage(containsString("Persistence unit does not exist for")); //$NON-NLS-1$
// delete
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
MyModel resource = createResource();
tx.getObjectDao().remove(resource);
}
}
@Test
public void shouldAllowAllOperationsInSameTx() {
setup(IoMode.SAX);
String subType = ModelBuilder.RES_TYPE;
String name = "shouldPersistById "; //$NON-NLS-1$
// create
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
String id = "shouldAllowAllOperationsInSameTx_create"; //$NON-NLS-1$
MyModel resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
}
// create / modify
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
String id = "shouldAllowAllOperationsInSameTx_create_modify"; //$NON-NLS-1$
MyModel resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
tx.getObjectDao().update(resource);
}
// create / delete
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
String id = "shouldAllowAllOperationsInSameTx_create_delete"; //$NON-NLS-1$
MyModel resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
tx.getObjectDao().remove(resource);
}
// create / modify / delete
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
String id = "shouldAllowAllOperationsInSameTx_create_modify_delete"; //$NON-NLS-1$
MyModel resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
tx.getObjectDao().update(resource);
tx.getObjectDao().remove(resource);
}
String id = "shouldAllowAllOperationsInSameTx_read_modify"; //$NON-NLS-1$
// prepare for read/modify
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
MyModel resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
}
// read / modify
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id);
Object resource = tx.getObjectDao().queryById(objectRef);
assertNotNull(resource);
tx.getObjectDao().update(resource);
}
// read / delete
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id);
Object resource = tx.getObjectDao().queryById(objectRef);
assertNotNull(resource);
tx.getObjectDao().remove(resource);
}
// make sure deleted, then recreate
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id);
Object resource = tx.getObjectDao().queryById(objectRef);
assertNull(resource);
// recreate
resource = createResource(id, name, subType);
tx.getObjectDao().add(resource);
}
// read / modify / delete
try (PersistenceTransaction tx = this.persistenceManager.openTx()) {
ObjectRef objectRef = tx.getObjectRefCache().getIdOfSubTypeRef(TYPE_RES, subType, id);
Object resource = tx.getObjectDao().queryById(objectRef);
assertNotNull(resource);
tx.getObjectDao().update(resource);
tx.getObjectDao().remove(resource);
}
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.Properties;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.test.impl.TestConstants;
import ch.eitchnet.xmlpers.test.model.ModelBuilder;
import ch.eitchnet.xmlpers.test.model.MyModel;
@SuppressWarnings("nls")
public class RealmTest extends AbstractPersistenceTest {
private static final String REALM_2 = "Realm2";
private static final String REALM_1 = "Realm1";
protected static final String BASE_PATH = "target/db/RealmTest/";
@BeforeClass
public static void beforeClass() {
cleanPath(BASE_PATH);
}
@Before
public void before() {
Properties properties = new Properties();
properties.setProperty(PersistenceConstants.PROP_BASEPATH, BASE_PATH + IoMode.DOM.name());
setup(properties);
}
@Test
public void shouldNotFindObjInBothRealms() {
// object details
String objType = TestConstants.TYPE_RES;
String type = ModelBuilder.RES_TYPE;
String name = ModelBuilder.RES_NAME;
String id = "shouldNotFindObjInBothRealms";
// create in first realm
try (PersistenceTransaction txRealm1 = this.persistenceManager.openTx(REALM_1);) {
MyModel resource1 = ModelBuilder.createResource(id, name, type);
txRealm1.getObjectDao().add(resource1);
}
// find in first realm
try (PersistenceTransaction txRealm1 = this.persistenceManager.openTx(REALM_1);) {
ObjectRef objectRef = txRealm1.getObjectRefCache().getIdOfSubTypeRef(objType, type, id);
MyModel resource = txRealm1.getObjectDao().queryById(objectRef);
assertNotNull("Resource was not found in first realm!", resource);
}
// fail to find in second realm
try (PersistenceTransaction txRealm2 = this.persistenceManager.openTx(REALM_2);) {
ObjectRef objectRef = txRealm2.getObjectRefCache().getIdOfSubTypeRef(objType, type, id);
MyModel resource = txRealm2.getObjectDao().queryById(objectRef);
assertNull("Resource was not created in second realm, thus not expected to be found there!", resource);
}
}
@Test
public void shouldNotDeleteObjInWrongRealm() {
// object details
String objType = TestConstants.TYPE_RES;
String subType = ModelBuilder.RES_TYPE;
String name = ModelBuilder.RES_NAME;
String id = "shouldNotDeleteObjInWrongRealm";
// create in first realm
try (PersistenceTransaction txRealm1 = this.persistenceManager.openTx(REALM_1);) {
MyModel resource1 = ModelBuilder.createResource(id, name, subType);
txRealm1.getObjectDao().add(resource1);
}
// create in second realm
try (PersistenceTransaction txRealm2 = this.persistenceManager.openTx(REALM_2);) {
MyModel resource1 = ModelBuilder.createResource(id, name, subType);
txRealm2.getObjectDao().add(resource1);
}
// delete in second realm
try (PersistenceTransaction txRealm2 = this.persistenceManager.openTx(REALM_2);) {
ObjectRef objectRef = txRealm2.getObjectRefCache().getIdOfSubTypeRef(objType, subType, id);
txRealm2.getObjectDao().removeById(objectRef);
}
// fail to find in second realm
try (PersistenceTransaction txRealm2 = this.persistenceManager.openTx(REALM_2);) {
ObjectRef objectRef = txRealm2.getObjectRefCache().getIdOfSubTypeRef(objType, subType, id);
MyModel resource = txRealm2.getObjectDao().queryById(objectRef);
assertNull("Resource was not deleted from second realm, thus not expected to be found there!", resource);
}
// find in first realm
try (PersistenceTransaction txRealm1 = this.persistenceManager.openTx(REALM_1);) {
ObjectRef objectRef = txRealm1.getObjectRefCache().getIdOfSubTypeRef(objType, subType, id);
MyModel resource = txRealm1.getObjectDao().queryById(objectRef);
assertNotNull("Resource was not found in first realm!", resource);
}
}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.RES_ID;
import static ch.eitchnet.xmlpers.test.model.ModelBuilder.createResource;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import ch.eitchnet.xmlpers.api.IoMode;
import ch.eitchnet.xmlpers.api.ModificationResult;
import ch.eitchnet.xmlpers.api.ObjectDao;
import ch.eitchnet.xmlpers.api.PersistenceConstants;
import ch.eitchnet.xmlpers.api.PersistenceTransaction;
import ch.eitchnet.xmlpers.api.TransactionResult;
import ch.eitchnet.xmlpers.test.model.Book;
import ch.eitchnet.xmlpers.test.model.MyModel;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class TransactionResultTest extends AbstractPersistenceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private static final String BASEPATH = "target/db/TxResultTest/"; //$NON-NLS-1$
@Before
public void setup() {
cleanPath(BASEPATH);
Properties properties = new Properties();
properties.setProperty(PersistenceConstants.PROP_BASEPATH, BASEPATH);
setup(properties);
}
private PersistenceTransaction freshTx() {
PersistenceTransaction tx = this.persistenceManager.openTx();
tx.setIoMode(IoMode.SAX);
return tx;
}
@Test
public void testWithTxResult() {
TransactionResult txResult = new TransactionResult();
performChanges(txResult);
String logMessage = txResult.getLogMessage();
logger.info(logMessage);
assertThat(logMessage, containsString("TX for realm defaultRealm was completed after")); //$NON-NLS-1$
assertThat(logMessage, containsString("30 objects in 2 types were modified")); //$NON-NLS-1$
assertThat(txResult.getKeys(), containsInAnyOrder("Resource", "Book")); //$NON-NLS-1$ //$NON-NLS-2$
ModificationResult resourceModificationResult = txResult.getModificationResult("Resource"); //$NON-NLS-1$
assertEquals(20, resourceModificationResult.getCreated().size());
assertEquals(0, resourceModificationResult.getUpdated().size());
assertEquals(0, resourceModificationResult.getDeleted().size());
ModificationResult bookModificationResult = txResult.getModificationResult("Book"); //$NON-NLS-1$
assertEquals(10, bookModificationResult.getCreated().size());
assertEquals(0, bookModificationResult.getUpdated().size());
assertEquals(0, bookModificationResult.getDeleted().size());
}
@Test
public void testWithoutTxResult() {
performChanges(null);
}
private void performChanges(TransactionResult txResult) {
// create a list of resources
List<MyModel> resources = new ArrayList<>(10);
int i = 0;
for (; i < 10; i++) {
String id = RES_ID + "_" + i; //$NON-NLS-1$
String name = "Tx Result Test 1 Object. " + i; //$NON-NLS-1$
String type = "testTxResult1"; //$NON-NLS-1$
MyModel resource = createResource(id, name, type);
resources.add(resource);
}
for (; i < 20; i++) {
String id = RES_ID + "_" + i; //$NON-NLS-1$
String name = "Tx Result Test 2 Object. " + i; //$NON-NLS-1$
String type = "testTxResult2"; //$NON-NLS-1$
MyModel resource = createResource(id, name, type);
resources.add(resource);
}
// create a list of books
List<Book> books = new ArrayList<>(10);
i = 0;
for (; i < 10; i++) {
String title = "Tx Result Test Book " + i; //$NON-NLS-1$
Book book = new Book((long) i, title, "Douglas Adams", "Apress", Math.random() * i); //$NON-NLS-1$ //$NON-NLS-2$
books.add(book);
}
// save all
try (PersistenceTransaction tx = freshTx();) {
tx.setTransactionResult(txResult);
ObjectDao objectDao = tx.getObjectDao();
objectDao.addAll(resources);
objectDao.addAll(books);
resources.clear();
}
}
}

View File

@ -0,0 +1,298 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import javanet.staxutils.IndentingXMLStreamWriter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ch.eitchnet.utils.exceptions.XmlException;
import ch.eitchnet.utils.helper.XmlHelper;
import ch.eitchnet.xmlpers.test.model.ModelBuilder;
import ch.eitchnet.xmlpers.test.model.MyModel;
import ch.eitchnet.xmlpers.test.model.MyParameter;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
@SuppressWarnings("nls")
public class XmlTestMain {
private static final Logger logger = LoggerFactory.getLogger(XmlTestMain.class);
private static MyModel res;
public static void main(String[] args) throws Exception {
res = ModelBuilder.createResource();
logger.info("Writing Res:\n" + res);
writeSax(res);
writeDom(res);
List<MyModel> resoures;
resoures = readDom();
logger.info("Parsed Resources:\n" + resoures);
resoures = readSax();
logger.info("Parsed Resources:\n" + resoures);
}
/**
* @return
*
*/
private static List<MyModel> readDom() throws Exception {
File file = new File("target/res_dom.xml");
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document document = docBuilder.parse(file);
Element rootElement = document.getDocumentElement();
List<MyModel> resources = new ArrayList<>();
NodeList resElements = rootElement.getElementsByTagName("Resource");
for (int i = 0; i < resElements.getLength(); i++) {
Element resElement = (Element) resElements.item(i);
String id = resElement.getAttribute("id");
String name = resElement.getAttribute("name");
String type = resElement.getAttribute("type");
MyModel res = new MyModel();
res.setId(id);
res.setName(name);
res.setType(type);
NodeList paramElements = resElement.getElementsByTagName("Parameter");
parseParameters(res, paramElements);
resources.add(res);
}
logger.info("DOM parsed file " + file.getAbsolutePath());
return resources;
}
private static void parseParameters(MyModel res, NodeList paramElements) {
for (int i = 0; i < paramElements.getLength(); i++) {
Element paramElement = (Element) paramElements.item(i);
String id = paramElement.getAttribute("id");
String name = paramElement.getAttribute("name");
String type = paramElement.getAttribute("type");
String value = paramElement.getAttribute("value");
MyParameter param = new MyParameter();
param.setId(id);
param.setName(name);
param.setType(type);
param.setValue(value);
res.addParameter(param);
}
}
/**
* @return
*
*/
private static List<MyModel> readSax() throws Exception {
final List<MyModel> resources = new ArrayList<>();
final MyModel[] currentRes = new MyModel[1];
DefaultHandler xmlHandler = new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
switch (qName) {
case "Resource":
MyModel res = new MyModel();
res.setId(attributes.getValue("id"));
res.setName(attributes.getValue("name"));
res.setType(attributes.getValue("type"));
currentRes[0] = res;
resources.add(res);
break;
case "Parameter":
MyParameter param = new MyParameter();
param.setId(attributes.getValue("id"));
param.setName(attributes.getValue("name"));
param.setType(attributes.getValue("type"));
param.setValue(attributes.getValue("value"));
currentRes[0].addParameter(param);
break;
case "model":
break;
default:
throw new IllegalArgumentException("The element '" + qName + "' is unhandled!");
}
}
};
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
File file = new File("target/res_sax.xml");
sp.parse(file, xmlHandler);
logger.info("SAX parsed file " + file.getAbsolutePath());
return resources;
}
private static void writeDom(MyModel res) throws Exception {
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element resElement = doc.createElement("Resource");
resElement.setAttribute("id", res.getId());
resElement.setAttribute("name", res.getName());
resElement.setAttribute("type", res.getType());
for (String paramId : res.getParameterKeySet()) {
MyParameter param = res.getParameterBy(paramId);
Element paramElement = doc.createElement("Parameter");
paramElement.setAttribute("id", param.getId());
paramElement.setAttribute("name", param.getName());
paramElement.setAttribute("type", param.getType());
paramElement.setAttribute("value", param.getValue());
resElement.appendChild(paramElement);
}
Element rootElement = doc.createElement("model");
rootElement.appendChild(resElement);
doc.appendChild(rootElement);
String lineSep = System.getProperty(XmlHelper.PROP_LINE_SEPARATOR);
try {
String encoding = doc.getInputEncoding();
if (encoding == null || encoding.isEmpty()) {
logger.info("No encoding passed. Using default encoding " + XmlHelper.DEFAULT_ENCODING);
encoding = XmlHelper.DEFAULT_ENCODING;
}
if (!lineSep.equals("\n")) {
logger.info("Overriding line separator to \\n");
System.setProperty(XmlHelper.PROP_LINE_SEPARATOR, "\n");
}
// Set up a transformer
TransformerFactory transfac = TransformerFactory.newInstance();
Transformer transformer = transfac.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2");
// transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t");
// Transform to file
File file = new File("target/res_dom.xml");
StreamResult result = new StreamResult(file);
Source xmlSource = new DOMSource(doc);
transformer.transform(xmlSource, result);
logger.info("Wrote DOM to " + file.getAbsolutePath());
} catch (Exception e) {
throw new XmlException("Exception while exporting to file: " + e, e);
} finally {
System.setProperty(XmlHelper.PROP_LINE_SEPARATOR, lineSep);
}
}
private static void writeSax(MyModel res) throws Exception {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
File file = new File("target/res_sax.xml");
try (FileWriter fileWriter = new FileWriter(file)) {
XMLStreamWriter writer = factory.createXMLStreamWriter(fileWriter);
writer = new IndentingXMLStreamWriter(writer);
writer.writeStartDocument("utf-8", "1.0");
writer.writeStartElement("model");
writer.writeStartElement("Resource");
writer.writeAttribute("id", res.getId());
writer.writeAttribute("name", res.getName());
writer.writeAttribute("type", res.getType());
for (String paramId : res.getParameterKeySet()) {
MyParameter param = res.getParameterBy(paramId);
writer.writeEmptyElement("Parameter");
writer.writeAttribute("id", param.getId());
writer.writeAttribute("name", param.getName());
writer.writeAttribute("type", param.getType());
writer.writeAttribute("value", param.getValue());
}
//writer.writeEmptyElement("data");
//writer.writeAttribute("name", "value");
////writer.writeEndElement();
//writer.writeEmptyElement("stuff");
//writer.writeAttribute("attr", "attrVal");
writer.writeEndElement();
writer.writeEndDocument();
writer.flush();
writer.close();
logger.info("Wrote SAX to " + file.getAbsolutePath());
//Transformer transformer = TransformerFactory.newInstance().newTransformer();
//Result outputTarget = new StaxR;
//Source xmlSource;
//transformer.transform(xmlSource, outputTarget);
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceContextFactory;
import ch.eitchnet.xmlpers.objref.IdOfTypeRef;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
import ch.eitchnet.xmlpers.test.model.Book;
public class BookContextFactory implements PersistenceContextFactory<Book> {
@Override
public PersistenceContext<Book> createCtx(ObjectRef objectRef) {
PersistenceContext<Book> ctx = new PersistenceContext<>(objectRef);
ctx.setParserFactory(new BookParserFactory());
return ctx;
}
@Override
public PersistenceContext<Book> createCtx(ObjectReferenceCache objectRefCache, Book t) {
IdOfTypeRef objectRef = objectRefCache.getIdOfTypeRef(TestConstants.TYPE_BOOK, t.getId().toString());
PersistenceContext<Book> ctx = createCtx(objectRef);
ctx.setObject(t);
return ctx;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import ch.eitchnet.xmlpers.api.DomParser;
import ch.eitchnet.xmlpers.test.model.Book;
import ch.eitchnet.xmlpers.util.DomUtil;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class BookDomParser implements DomParser<Book> {
private Book book;
@Override
public Book getObject() {
return this.book;
}
@Override
public void setObject(Book object) {
this.book = object;
}
@SuppressWarnings("nls")
@Override
public Document toDom() {
DocumentBuilder documentBuilder = DomUtil.createDocumentBuilder();
Document document = documentBuilder.getDOMImplementation().createDocument(null, null, null);
Element rootElement = document.createElement("Book");
document.appendChild(rootElement);
rootElement.setAttribute("id", Long.toString(this.book.getId()));
rootElement.setAttribute("title", this.book.getTitle());
rootElement.setAttribute("author", this.book.getAuthor());
rootElement.setAttribute("press", this.book.getPress());
rootElement.setAttribute("price", Double.toString(this.book.getPrice()));
return document;
}
@SuppressWarnings("nls")
@Override
public void fromDom(Document document) {
Element rootElement = document.getDocumentElement();
String idS = rootElement.getAttribute("id");
long id = Long.parseLong(idS);
String title = rootElement.getAttribute("title");
String author = rootElement.getAttribute("author");
String press = rootElement.getAttribute("press");
String priceS = rootElement.getAttribute("price");
double price = Double.parseDouble(priceS);
Book book = new Book(id, title, author, press, price);
this.book = book;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import ch.eitchnet.xmlpers.api.DomParser;
import ch.eitchnet.xmlpers.api.ParserFactory;
import ch.eitchnet.xmlpers.api.SaxParser;
import ch.eitchnet.xmlpers.test.model.Book;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class BookParserFactory implements ParserFactory<Book> {
@Override
public DomParser<Book> getDomParser() {
return new BookDomParser();
}
@Override
public SaxParser<Book> getSaxParser() {
return new BookSaxParser();
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ch.eitchnet.xmlpers.api.SaxParser;
import ch.eitchnet.xmlpers.test.model.Book;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class BookSaxParser extends DefaultHandler implements SaxParser<Book> {
private Book book;
@Override
public Book getObject() {
return this.book;
}
@Override
public void setObject(Book object) {
this.book = object;
}
@Override
public DefaultHandler getDefaultHandler() {
return this;
}
@SuppressWarnings("nls")
@Override
public void write(XMLStreamWriter writer) throws XMLStreamException {
writer.writeEmptyElement("Book");
writer.writeAttribute("id", Long.toString(this.book.getId()));
writer.writeAttribute("title", this.book.getTitle());
writer.writeAttribute("author", this.book.getAuthor());
writer.writeAttribute("press", this.book.getPress());
writer.writeAttribute("price", Double.toString(this.book.getPrice()));
}
@SuppressWarnings("nls")
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case "Book":
String idS = attributes.getValue("id");
long id = Long.parseLong(idS);
Book book = new Book(id);
book.setTitle(attributes.getValue("title"));
book.setAuthor(attributes.getValue("author"));
book.setPress(attributes.getValue("press"));
String priceS = attributes.getValue("price");
double price = Double.parseDouble(priceS);
book.setPrice(price);
this.book = book;
break;
default:
throw new IllegalArgumentException("The element '" + qName + "' is unhandled!");
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import ch.eitchnet.xmlpers.api.PersistenceContext;
import ch.eitchnet.xmlpers.api.PersistenceContextFactory;
import ch.eitchnet.xmlpers.objref.IdOfSubTypeRef;
import ch.eitchnet.xmlpers.objref.ObjectRef;
import ch.eitchnet.xmlpers.objref.ObjectReferenceCache;
import ch.eitchnet.xmlpers.test.model.MyModel;
public class MyModelContextFactory implements PersistenceContextFactory<MyModel> {
@Override
public PersistenceContext<MyModel> createCtx(ObjectReferenceCache objectRefCache, MyModel t) {
IdOfSubTypeRef objectRef = objectRefCache.getIdOfSubTypeRef(TestConstants.TYPE_RES, t.getType(), t.getId());
PersistenceContext<MyModel> ctx = createCtx(objectRef);
ctx.setObject(t);
return ctx;
}
@Override
public PersistenceContext<MyModel> createCtx(ObjectRef objectRef) {
PersistenceContext<MyModel> ctx = new PersistenceContext<>(objectRef);
ctx.setParserFactory(new MyModelParserFactory());
return ctx;
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import ch.eitchnet.xmlpers.api.DomParser;
import ch.eitchnet.xmlpers.test.model.MyModel;
import ch.eitchnet.xmlpers.test.model.MyParameter;
import ch.eitchnet.xmlpers.util.DomUtil;
public class MyModelDomParser implements DomParser<MyModel> {
private MyModel resource;
@Override
public MyModel getObject() {
return this.resource;
}
@Override
public void setObject(MyModel resource) {
this.resource = resource;
}
@SuppressWarnings("nls")
@Override
public Document toDom() {
DocumentBuilder documentBuilder = DomUtil.createDocumentBuilder();
Document document = documentBuilder.getDOMImplementation().createDocument(null, null, null);
Element element = document.createElement("Resource");
document.appendChild(element);
element.setAttribute("id", this.resource.getId());
element.setAttribute("name", this.resource.getName());
element.setAttribute("type", this.resource.getType());
for (String paramId : this.resource.getParameterKeySet()) {
MyParameter param = this.resource.getParameterBy(paramId);
Element paramElement = document.createElement("Parameter");
element.appendChild(paramElement);
paramElement.setAttribute("id", param.getId());
paramElement.setAttribute("name", param.getName());
paramElement.setAttribute("type", param.getType());
paramElement.setAttribute("value", param.getValue());
}
return document;
}
@SuppressWarnings("nls")
@Override
public void fromDom(Document document) {
Element rootElement = document.getDocumentElement();
String id = rootElement.getAttribute("id");
String name = rootElement.getAttribute("name");
String type = rootElement.getAttribute("type");
MyModel resource = new MyModel(id, name, type);
NodeList children = rootElement.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node item = children.item(i);
if (!item.getNodeName().equals("Parameter"))
continue;
Element paramElement = (Element) item;
String paramId = paramElement.getAttribute("id");
String paramName = paramElement.getAttribute("name");
String paramType = paramElement.getAttribute("type");
String paramValue = paramElement.getAttribute("value");
MyParameter param = new MyParameter(paramId, paramName, paramType, paramValue);
resource.addParameter(param);
}
this.resource = resource;
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import ch.eitchnet.xmlpers.api.DomParser;
import ch.eitchnet.xmlpers.api.ParserFactory;
import ch.eitchnet.xmlpers.api.SaxParser;
import ch.eitchnet.xmlpers.test.model.MyModel;
public class MyModelParserFactory implements ParserFactory<MyModel> {
@Override
public DomParser<MyModel> getDomParser() {
return new MyModelDomParser();
}
@Override
public SaxParser<MyModel> getSaxParser() {
return new MyModelSaxParser();
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ch.eitchnet.xmlpers.api.SaxParser;
import ch.eitchnet.xmlpers.test.model.MyModel;
import ch.eitchnet.xmlpers.test.model.MyParameter;
class MyModelSaxParser extends DefaultHandler implements SaxParser<MyModel> {
private MyModel resource;
@Override
public MyModel getObject() {
return this.resource;
}
@Override
public void setObject(MyModel object) {
this.resource = object;
}
@Override
public DefaultHandler getDefaultHandler() {
return this;
}
@SuppressWarnings("nls")
@Override
public void write(XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement("Resource");
writer.writeAttribute("id", this.resource.getId());
writer.writeAttribute("name", this.resource.getName());
writer.writeAttribute("type", this.resource.getType());
for (String paramId : this.resource.getParameterKeySet()) {
MyParameter param = this.resource.getParameterBy(paramId);
writer.writeEmptyElement("Parameter");
writer.writeAttribute("id", param.getId());
writer.writeAttribute("name", param.getName());
writer.writeAttribute("type", param.getType());
writer.writeAttribute("value", param.getValue());
}
writer.writeEndElement();
}
@SuppressWarnings("nls")
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case "Resource":
String id = attributes.getValue("id");
String name = attributes.getValue("name");
String type = attributes.getValue("type");
MyModel resource = new MyModel(id, name, type);
this.resource = resource;
break;
case "Parameter":
id = attributes.getValue("id");
name = attributes.getValue("name");
type = attributes.getValue("type");
String value = attributes.getValue("value");
MyParameter param = new MyParameter(id, name, type, value);
this.resource.addParameter(param);
break;
default:
throw new IllegalArgumentException("The element '" + qName + "' is unhandled!");
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.impl;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class TestConstants {
public static final String TYPE_RES = "Resource"; //$NON-NLS-1$
public static final String TYPE_BOOK = "Book"; //$NON-NLS-1$
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.model;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
public class Book {
private final Long id;
private String title;
private String author;
private String press;
private double price;
/**
* @param id
* @param title
* @param author
* @param press
* @param price
*/
public Book(Long id, String title, String author, String press, double price) {
super();
this.id = id;
this.title = title;
this.author = author;
this.press = press;
this.price = price;
}
/**
*
*/
public Book(Long id) {
if (id == null)
throw new IllegalArgumentException("Id may not be null!"); //$NON-NLS-1$
this.id = id;
}
/**
* @return the id
*/
public Long getId() {
return this.id;
}
/**
* @return the title
*/
public String getTitle() {
return this.title;
}
/**
* @param title
* the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return the author
*/
public String getAuthor() {
return this.author;
}
/**
* @param author
* the author to set
*/
public void setAuthor(String author) {
this.author = author;
}
/**
* @return the press
*/
public String getPress() {
return this.press;
}
/**
* @param press
* the press to set
*/
public void setPress(String press) {
this.press = press;
}
/**
* @return the price
*/
public double getPrice() {
return this.price;
}
/**
* @param price
* the price to set
*/
public void setPrice(double price) {
this.price = price;
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.model;
import org.junit.Assert;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*
*/
@SuppressWarnings("nls")
public class ModelBuilder {
public static final String RES_TYPE = "@subType";
public static final String RES_TYPE_INEXISTANT = "@inexistant";
public static final String RES_NAME = "@name";
public static final String RES_NAME_MODIFIED = "@name_modified";
public static final String RES_ID = "@id";
public static final String PARAM_TYPE = "@paramType";
public static final String PARAM_NAME = "@paramName";
public static final String PARAM_ID = "@paramId";
public static final String PARAM_VALUE_1 = "@paramValue1";
public static final String PARAM_VALUE_2 = "@paramValue2";
public static final long BOOK_ID = 10L;
public static final String BOOK_TITLE = "Nick Hornby";
public static final String BOOK_AUTHOR = "A long way down";
public static final String BOOK_PRESS_1 = "Some press";
public static final String BOOK_PRESS_2 = "Another press";
public static final double BOOK_PRICE = 45.55D;
public static MyModel createResource() {
return createResource(RES_ID, RES_NAME, RES_TYPE);
}
public static MyModel createResource(String id) {
return createResource(id, RES_NAME, RES_TYPE);
}
public static MyModel createResource(String id, String name, String type) {
MyModel resource = new MyModel(id, name, type);
MyParameter param = new MyParameter(PARAM_ID, PARAM_NAME, PARAM_TYPE, PARAM_VALUE_1);
resource.addParameter(param);
return resource;
}
public static void updateResource(MyModel resource) {
resource.setName(RES_NAME_MODIFIED);
resource.getParameterBy(PARAM_ID).setValue(PARAM_VALUE_2);
}
public static Book createBook() {
Book book = new Book(BOOK_ID, BOOK_TITLE, BOOK_AUTHOR, BOOK_PRESS_1, BOOK_PRICE);
return book;
}
public static Book createBook(long id, String title, String author, String press, double price) {
Book book = new Book(id, title, author, press, price);
return book;
}
public static void updateBook(Book book) {
book.setPress(BOOK_PRESS_2);
}
public static void assertBook(Book book) {
Assert.assertNotNull(book);
Assert.assertEquals(BOOK_ID, book.getId().longValue());
Assert.assertEquals(BOOK_TITLE, book.getTitle());
Assert.assertEquals(BOOK_AUTHOR, book.getAuthor());
Assert.assertEquals(BOOK_PRESS_1, book.getPress());
Assert.assertEquals(BOOK_PRICE, book.getPrice(), 0.0);
}
public static void assertBookUpdated(Book book) {
Assert.assertNotNull(book);
Assert.assertEquals(BOOK_ID, book.getId().longValue());
Assert.assertEquals(BOOK_TITLE, book.getTitle());
Assert.assertEquals(BOOK_AUTHOR, book.getAuthor());
Assert.assertEquals(BOOK_PRESS_2, book.getPress());
Assert.assertEquals(BOOK_PRICE, book.getPrice(), 0.0);
}
public static void assertResource(MyModel resource) {
Assert.assertNotNull(resource);
Assert.assertEquals(RES_ID, resource.getId());
Assert.assertEquals(RES_NAME, resource.getName());
Assert.assertEquals(RES_TYPE, resource.getType());
MyParameter param = resource.getParameterBy(PARAM_ID);
Assert.assertNotNull(param);
Assert.assertEquals(PARAM_ID, param.getId());
Assert.assertEquals(PARAM_NAME, param.getName());
Assert.assertEquals(PARAM_TYPE, param.getType());
Assert.assertEquals(PARAM_VALUE_1, param.getValue());
}
public static void assertResourceUpdated(MyModel resource) {
Assert.assertNotNull(resource);
Assert.assertEquals(RES_ID, resource.getId());
Assert.assertEquals(RES_NAME_MODIFIED, resource.getName());
Assert.assertEquals(RES_TYPE, resource.getType());
MyParameter param = resource.getParameterBy(PARAM_ID);
Assert.assertNotNull(param);
Assert.assertEquals(PARAM_ID, param.getId());
Assert.assertEquals(PARAM_NAME, param.getName());
Assert.assertEquals(PARAM_TYPE, param.getType());
Assert.assertEquals(PARAM_VALUE_2, param.getValue());
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MyModel {
private String id;
private String name;
private String type;
private Map<String, MyParameter> parameters = new HashMap<>();
/**
*
*/
public MyModel() {
// empty constructor
}
/**
* @param id
* @param name
* @param type
*/
public MyModel(String id, String name, String type) {
super();
this.id = id;
this.name = name;
this.type = type;
}
@SuppressWarnings("nls")
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Resource [id=");
builder.append(this.id);
builder.append(", name=");
builder.append(this.name);
builder.append(", type=");
builder.append(this.type);
builder.append(", parameters=");
for (Entry<String, MyParameter> param : this.parameters.entrySet()) {
builder.append("\n");
builder.append(" " + param.getKey() + " = " + param.getValue());
}
builder.append("]");
return builder.toString();
}
/**
* @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;
}
public void addParameter(MyParameter parameter) {
this.parameters.put(parameter.getId(), parameter);
}
public Set<String> getParameterKeySet() {
return this.parameters.keySet();
}
public MyParameter getParameterBy(String id) {
return this.parameters.get(id);
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.eitchnet.xmlpers.test.model;
public class MyParameter {
private String id;
private String name;
private String type;
private String value;
/**
*
*/
public MyParameter() {
// empty constructor
}
/**
* @param id
* @param name
* @param type
* @param value
*/
public MyParameter(String id, String name, String type, String value) {
super();
this.id = id;
this.name = name;
this.type = type;
this.value = value;
}
@SuppressWarnings("nls")
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Parameter [id=");
builder.append(this.id);
builder.append(", name=");
builder.append(this.name);
builder.append(", type=");
builder.append(this.type);
builder.append(", value=");
builder.append(this.value);
builder.append("]");
return builder.toString();
}
/**
* @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;
}
/**
* @return the value
*/
public String getValue() {
return this.value;
}
/**
* @param value
* the value to set
*/
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration PUBLIC
"-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration debug="false" xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %5p [%t] %C{1} %M - %m%n" />
</layout>
</appender>
<appender name="FILE" class="org.apache.log4j.FileAppender">
<param name="File" value="sample.log"/>
<param name="BufferedIO" value="true" />
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %5p [%t] %C{1} %M - %m%n" />
</layout>
</appender>
<logger name="ch.eitchnet">
<level value="INFO" />
</logger>
<root>
<priority value="INFO" />
<appender-ref ref="CONSOLE" />
<!-- appender-ref ref="FILE" / -->
</root>
</log4j:configuration>