mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-03 07:13:02 +00:00
feat: Implemenation of the Serialized Lob Pattern (#2795)
* #1596:Adding module for SLOB Pattern * #1596:Adding Interface for Serializers * #1596:Adding Objects * #1596:Adding BLOB Based Serializer * #1596:Implemented Clob Based Serializer * #1596:Implemented Service for DB operations * #1596:Implemented basic flow using Clob Serializer * #1596:Added H2 DB Dependency * #1596:Added Java Doc Stubs * Updating Objects * #1596:Completed Clob Serializer along with Tests * #1596:Completed Slob Serializer along with Tests * #1596:Completed Blob Serializer along with Tests * #1596:Updated DatabaseService to handle Blob and Clob Data * #1596:Added UML and updated README.md * #1596:Updated data types for DB * #1596:Code Style Formatting Cnames * Adding Java Docs * #1596:Reformatted as per Code Style * #1596:Updating Java Doc * #1596:Updating Main function to handle both Serializers * #1596:Updated Java Docs * #1596:Updated Java Docs * #1596:Updated Java Docs and formatting * #1596:Updated Java Docs and formatting * #1596:Adding Java Docs * #1596:Reformatted as per Code Style * #1596:Updating Java Doc * #1596:Updating Main function to handle both Serializers * #1596:Updated Java Docs * #1596:Updated Java Docs * #1596:Updated Java Docs and formatting * #1596:Updated Java Docs and formatting * #1596:Updated Java Docs and formatting * #1596:Updated Java Docs and formatting * #1596:Updated README.md * #1596:Updated Java Docs and README.md * #1596:Updated Extension * #1596:Updated README.md * #1596:Updated Documentation as per review comments * #1596:Updated Documentation as per review comments * #1596: Updated README.md * #1596:Updated Documentation as per review comments * #1596: Added SLOB Module --------- Co-authored-by: SHRADDHAP18 <87650482+SHRADDHAP18@users.noreply.github.com>
This commit is contained in:
1 parent
be3a184445
commit
b5ddd62145
14 files changed
+1563
No files matched your search
@@ -215,6 +215,7 @@
|
|||||||
<module>single-table-inheritance</module>
|
<module>single-table-inheritance</module>
|
||||||
<module>dynamic-proxy</module>
|
<module>dynamic-proxy</module>
|
||||||
<module>gateway</module>
|
<module>gateway</module>
|
||||||
|
<module>slob</module>
|
||||||
</modules>
|
</modules>
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
|
|||||||
+354
@@ -0,0 +1,354 @@
|
|||||||
|
---
|
||||||
|
title: Serialized LOB
|
||||||
|
category: Structural
|
||||||
|
language: en
|
||||||
|
tag:
|
||||||
|
- Data access
|
||||||
|
---
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
|
||||||
|
* Object models often contain complicated graphs of small objects. Much of the information in these
|
||||||
|
structures isn’t in the objects but in the links between them.
|
||||||
|
* Objects don’t have to be persisted as table rows related to each other.
|
||||||
|
* Another form of persistence is serialization, where a whole graph of objects is written out as a
|
||||||
|
single large object (LOB) in a table.
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
**In plain words**
|
||||||
|
|
||||||
|
> The Forest here represents the object graph.
|
||||||
|
> A forest contains animals and plants. As is in real life the forest object contains plants and
|
||||||
|
> animals where some animals eat other animals and some eat only plants.
|
||||||
|
> * These relationships are maintained in the Forest Object.
|
||||||
|
> * There are 2 types of Serializers available ClobSerializer and BlobSerializer.
|
||||||
|
> * ClobSerializer uses character or textual based serialization, here the Object graph is converted
|
||||||
|
> * to XML and then stored as text in DB.
|
||||||
|
> * BlobSerializer uses binary data for serialization, here the Object graph is converted to Byte
|
||||||
|
> * Array and then stored as a blob in DB.
|
||||||
|
|
||||||
|
**Programmatic Example**
|
||||||
|
|
||||||
|
* Here is the `Animal` class. It represents the Animal object in the Forest. It contains the name of
|
||||||
|
the animals in the forest and details of what they eat from the forest plants/animals or both.
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* Creates an object Forest which contains animals and plants as its constituents. Animals may eat
|
||||||
|
* plants or other animals in the forest.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Forest implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private final Set<Animal> animals = new HashSet<>();
|
||||||
|
private final Set<Plant> plants = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the representation of Forest in XML form.
|
||||||
|
*
|
||||||
|
* @return XML Element
|
||||||
|
*/
|
||||||
|
public Element toXmlElement() throws ParserConfigurationException {
|
||||||
|
Document xmlDoc = getXmlDoc();
|
||||||
|
|
||||||
|
Element forestXml = xmlDoc.createElement("Forest");
|
||||||
|
forestXml.setAttribute("name", name);
|
||||||
|
|
||||||
|
Element animalsXml = xmlDoc.createElement("Animals");
|
||||||
|
for (Animal animal : animals) {
|
||||||
|
Element animalXml = animal.toXmlElement(xmlDoc);
|
||||||
|
animalsXml.appendChild(animalXml);
|
||||||
|
}
|
||||||
|
forestXml.appendChild(animalsXml);
|
||||||
|
|
||||||
|
Element plantsXml = xmlDoc.createElement("Plants");
|
||||||
|
for (Plant plant : plants) {
|
||||||
|
Element plantXml = plant.toXmlElement(xmlDoc);
|
||||||
|
plantsXml.appendChild(plantXml);
|
||||||
|
}
|
||||||
|
forestXml.appendChild(plantsXml);
|
||||||
|
return forestXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns XMLDoc to use for XML creation.
|
||||||
|
*
|
||||||
|
* @return XML DOC Object
|
||||||
|
* @throws ParserConfigurationException {@inheritDoc}
|
||||||
|
*/
|
||||||
|
private Document getXmlDoc() throws ParserConfigurationException {
|
||||||
|
return DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the Forest Object from the input XML Document.
|
||||||
|
*
|
||||||
|
* @param document the XML document from which the Forest is to be parsed
|
||||||
|
*/
|
||||||
|
public void createObjectFromXml(Document document) {
|
||||||
|
name = document.getDocumentElement().getAttribute("name");
|
||||||
|
NodeList nodeList = document.getElementsByTagName("*");
|
||||||
|
iterateXmlForAnimalAndPlants(nodeList, animals, plants);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("\n");
|
||||||
|
sb.append("Forest Name = ").append(name).append("\n");
|
||||||
|
sb.append("Animals found in the ").append(name).append(" Forest: \n");
|
||||||
|
for (Animal animal : animals) {
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
sb.append(animal.toString());
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("Plants in the ").append(name).append(" Forest: \n");
|
||||||
|
for (Plant plant : plants) {
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
sb.append(plant.toString());
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Here is the `LobSerializer` abstract class. It provides the specification to serialize and
|
||||||
|
deserialize the input object and persist and load that object into a DB.
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* A LobSerializer can be used to create an instance of a serializer which can serialize and
|
||||||
|
* deserialize an object and persist and load that object into a DB. from their Binary
|
||||||
|
* Representation.
|
||||||
|
*/
|
||||||
|
public abstract class LobSerializer implements Serializable, Closeable {
|
||||||
|
|
||||||
|
private final transient DatabaseService databaseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor initializes {@link LobSerializer#databaseService}.
|
||||||
|
*
|
||||||
|
* @param dataTypeDb Input provides type of Data to be stored by the Data Base Service
|
||||||
|
* @throws SQLException If any issue occurs during instantiation of DB Service or during startup.
|
||||||
|
*/
|
||||||
|
protected LobSerializer(String dataTypeDb) throws SQLException {
|
||||||
|
databaseService = new DatabaseService(dataTypeDb);
|
||||||
|
databaseService.startupService();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the specification to Serialize the input object.
|
||||||
|
*
|
||||||
|
* @param toSerialize Input Object to serialize
|
||||||
|
* @return Serialized Object
|
||||||
|
* @throws ParserConfigurationException if any issue occurs during parsing of input object
|
||||||
|
* @throws TransformerException if any issue occurs during Transformation
|
||||||
|
* @throws IOException if any issues occur during reading object
|
||||||
|
*/
|
||||||
|
public abstract Object serialize(Forest toSerialize)
|
||||||
|
throws ParserConfigurationException, TransformerException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the object to DB with the provided ID.
|
||||||
|
*
|
||||||
|
* @param id key to be sent to DB service
|
||||||
|
* @param name Object name to store in DB
|
||||||
|
* @param object Object to store in DB
|
||||||
|
* @return ID with which the object is stored in DB
|
||||||
|
* @throws SQLException if any issue occurs while saving to DB
|
||||||
|
*/
|
||||||
|
public int persistToDb(int id, String name, Object object) throws SQLException {
|
||||||
|
databaseService.insert(id, name, object);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the object from db using the ID and column name.
|
||||||
|
*
|
||||||
|
* @param id to query the DB
|
||||||
|
* @param columnName column from which object is to be extracted
|
||||||
|
* @return Object from DB
|
||||||
|
* @throws SQLException if any issue occurs while loading from DB
|
||||||
|
*/
|
||||||
|
public Object loadFromDb(int id, String columnName) throws SQLException {
|
||||||
|
return databaseService.select(id, columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the specification to Deserialize the input object.
|
||||||
|
*
|
||||||
|
* @param toDeserialize object to deserialize
|
||||||
|
* @return Deserialized Object
|
||||||
|
* @throws ParserConfigurationException If issue occurs during parsing of input object
|
||||||
|
* @throws IOException if any issues occur during reading object
|
||||||
|
* @throws SAXException if any issues occur during reading object for XML parsing
|
||||||
|
*/
|
||||||
|
public abstract Forest deSerialize(Object toDeserialize)
|
||||||
|
throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
databaseService.shutDownService();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Here is the `ClobSerializer` class. It extends the `LobSerializer` abstract class and provides the
|
||||||
|
implementation to serialize and deserialize the input object and persist and load that object into
|
||||||
|
a DB using ClobSerializer.
|
||||||
|
* Objects are serialized using character or textual based serialization
|
||||||
|
using XML to represent the object graph.
|
||||||
|
|
||||||
|
```java
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Serializer that uses Character based serialization and deserialization of objects graph
|
||||||
|
* to and from XML Representation.
|
||||||
|
*/
|
||||||
|
public class ClobSerializer extends LobSerializer {
|
||||||
|
|
||||||
|
public static final String TYPE_OF_DATA_FOR_DB = "TEXT";
|
||||||
|
|
||||||
|
public ClobSerializer() {
|
||||||
|
super(TYPE_OF_DATA_FOR_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the input node to its XML String Representation.
|
||||||
|
*
|
||||||
|
* @param node XML Node that is to be converted to string
|
||||||
|
* @return String representation of XML parsed from the Node
|
||||||
|
* @throws TransformerException If any issues occur in Transformation from Node to XML
|
||||||
|
*/
|
||||||
|
private static String elementToXmlString(Element node) throws TransformerException {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
Transformer t = TransformerFactory.newDefaultInstance().newTransformer();
|
||||||
|
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
||||||
|
t.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||||
|
t.transform(new DOMSource(node), new StreamResult(sw));
|
||||||
|
return sw.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the input object graph to its XML Representation using DOM Elements.
|
||||||
|
*
|
||||||
|
* @param forest Object which is to be serialized
|
||||||
|
* @return Serialized object
|
||||||
|
* @throws ParserConfigurationException If any issues occur in parsing input object
|
||||||
|
* @throws TransformerException If any issues occur in Transformation from Node to XML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object serialize(Forest forest) throws ParserConfigurationException, TransformerException {
|
||||||
|
Element xmlElement = forest.toXmlElement();
|
||||||
|
return elementToXmlString(xmlElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the input XML string using DOM Parser and return its Object Graph Representation.
|
||||||
|
*
|
||||||
|
* @param toDeserialize Input Object to De-serialize
|
||||||
|
* @return Deserialized Object
|
||||||
|
* @throws ParserConfigurationException If any issues occur in parsing input object
|
||||||
|
* @throws IOException if any issues occur during reading object
|
||||||
|
* @throws SAXException If any issues occur in Transformation from Node to XML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Forest deSerialize(Object toDeserialize)
|
||||||
|
throws ParserConfigurationException, IOException, SAXException {
|
||||||
|
DocumentBuilder documentBuilder = DocumentBuilderFactory.newDefaultInstance()
|
||||||
|
.newDocumentBuilder();
|
||||||
|
var stream = new ByteArrayInputStream(toDeserialize.toString().getBytes());
|
||||||
|
Document parsed = documentBuilder.parse(stream);
|
||||||
|
Forest forest = new Forest();
|
||||||
|
forest.createObjectFromXml(parsed);
|
||||||
|
return forest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Here is the `SlobSerializer` class. It extends the `LobSerializer` abstract class and provides the
|
||||||
|
implementation to serialize and deserialize the input object and persist and load that object into
|
||||||
|
a DB using ClobSerializer.
|
||||||
|
* Objects are serialized using binary data based serialization objects a persisted as a BINARY/BLOB
|
||||||
|
in DB.
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* Creates a Serializer that uses Binary serialization and deserialization of objects graph to and
|
||||||
|
* from their Binary Representation.
|
||||||
|
*/
|
||||||
|
public class BlobSerializer extends LobSerializer {
|
||||||
|
|
||||||
|
public static final String TYPE_OF_DATA_FOR_DB = "BINARY";
|
||||||
|
|
||||||
|
public BlobSerializer() throws SQLException {
|
||||||
|
super(TYPE_OF_DATA_FOR_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the input object graph to its Binary Representation using Object Stream.
|
||||||
|
*
|
||||||
|
* @param toSerialize Object which is to be serialized
|
||||||
|
* @return Serialized object
|
||||||
|
* @throws IOException {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object serialize(Forest toSerialize) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||||
|
oos.writeObject(toSerialize);
|
||||||
|
oos.close();
|
||||||
|
return new ByteArrayInputStream(baos.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the input Byte Array Stream using Object Stream and return its Object Graph
|
||||||
|
* Representation.
|
||||||
|
*
|
||||||
|
* @param toDeserialize Input Object to De-serialize
|
||||||
|
* @return Deserialized Object
|
||||||
|
* @throws ClassNotFoundException {@inheritDoc}
|
||||||
|
* @throws IOException {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Forest deSerialize(Object toDeserialize) throws IOException, ClassNotFoundException {
|
||||||
|
InputStream bis = (InputStream) toDeserialize;
|
||||||
|
Forest forest;
|
||||||
|
try (ObjectInput in = new ObjectInputStream(bis)) {
|
||||||
|
forest = (Forest) in.readObject();
|
||||||
|
}
|
||||||
|
return forest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class diagram
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Applicability
|
||||||
|
|
||||||
|
* This pattern works best when you can chop out a piece of the object model and use it to represent
|
||||||
|
the LOB.
|
||||||
|
* Think of a LOB as a way to take a bunch of objects that aren’t likely to be queried from any SQL
|
||||||
|
route outside the application.
|
||||||
|
* This graph can then be hooked into the SQL schema. Serialized LOB works poorly when you have
|
||||||
|
objects outside the LOB reference objects buried in it.
|
||||||
|
* Serialized LOB isn’t considered as often as it might be. XML makes it much more attractive since
|
||||||
|
it yields an easy-to-implement textual approach.
|
||||||
|
* Its main disadvantage is that you can’t query the structure using SQL.
|
||||||
|
* SQL extensions appear to get at XML data within a field, but that’s still not the same (or
|
||||||
|
portable).
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Serialized LOB](https://martinfowler.com/eaaCatalog/serializedLOB.html) by Martin Fowler
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,49 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
!theme plain
|
||||||
|
top to bottom direction
|
||||||
|
skinparam linetype ortho
|
||||||
|
|
||||||
|
class Animal {
|
||||||
|
- animalsEaten: Set<Animal>
|
||||||
|
- name: String
|
||||||
|
- plantsEaten: Set<Plant>
|
||||||
|
animalsEaten: Set<Animal>
|
||||||
|
name: String
|
||||||
|
plantsEaten: Set<Plant>
|
||||||
|
}
|
||||||
|
class App
|
||||||
|
class BlobSerializer
|
||||||
|
class ClobSerializer
|
||||||
|
class DatabaseService
|
||||||
|
class Forest {
|
||||||
|
- animals: Set<Animal>
|
||||||
|
- name: String
|
||||||
|
- plants: Set<Plant>
|
||||||
|
animals: Set<Animal>
|
||||||
|
name: String
|
||||||
|
plants: Set<Plant>
|
||||||
|
xmlDoc: Document
|
||||||
|
}
|
||||||
|
class LobSerializer
|
||||||
|
class Plant {
|
||||||
|
- name: String
|
||||||
|
- type: String
|
||||||
|
name: String
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
|
||||||
|
Animal -[#595959,dashed]-> Plant : "«create»"
|
||||||
|
Animal "1" *-[#595959,plain]-> "plantsEaten\n*" Plant
|
||||||
|
App -[#595959,dashed]-> Animal : "«create»"
|
||||||
|
App -[#595959,dashed]-> ClobSerializer : "«create»"
|
||||||
|
App -[#595959,dashed]-> Forest : "«create»"
|
||||||
|
App -[#595959,dashed]-> Plant : "«create»"
|
||||||
|
BlobSerializer -[#000082,plain]-^ LobSerializer
|
||||||
|
ClobSerializer -[#595959,dashed]-> Forest : "«create»"
|
||||||
|
ClobSerializer -[#000082,plain]-^ LobSerializer
|
||||||
|
Forest "1" *-[#595959,plain]-> "animals\n*" Animal
|
||||||
|
Forest "1" *-[#595959,plain]-> "plants\n*" Plant
|
||||||
|
LobSerializer "1" *-[#595959,plain]-> "databaseService\n1" DatabaseService
|
||||||
|
LobSerializer -[#595959,dashed]-> DatabaseService : "«create»"
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<artifactId>slob</artifactId>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>com.iluwatar.slob.App</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<version>2.2.220</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob;
|
||||||
|
|
||||||
|
import com.iluwatar.slob.lob.Animal;
|
||||||
|
import com.iluwatar.slob.lob.Forest;
|
||||||
|
import com.iluwatar.slob.lob.Plant;
|
||||||
|
import com.iluwatar.slob.serializers.BlobSerializer;
|
||||||
|
import com.iluwatar.slob.serializers.ClobSerializer;
|
||||||
|
import com.iluwatar.slob.serializers.LobSerializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SLOB Application using {@link LobSerializer} and H2 DB.
|
||||||
|
*/
|
||||||
|
public class App {
|
||||||
|
|
||||||
|
public static final String CLOB = "CLOB";
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point to program.
|
||||||
|
* <p>In the SLOB pattern, the object graph is serialized into a single large object (a BLOB or
|
||||||
|
* CLOB, for Binary Large Object or Character Large Object, respectively) and stored in the
|
||||||
|
* database. When the object graph needs to be retrieved, it is read from the database and
|
||||||
|
* deserialized back into the original object graph.</p>
|
||||||
|
*
|
||||||
|
* <p>A Forest is created using {@link #createForest()} with Animals and Plants along with their
|
||||||
|
* respective relationships.</p>
|
||||||
|
*
|
||||||
|
* <p>Creates a {@link LobSerializer} using the method
|
||||||
|
* {@link #createLobSerializer(String[])}.</p>
|
||||||
|
*
|
||||||
|
* <p>Once created the serializer is passed to the
|
||||||
|
* {@link #executeSerializer(Forest, LobSerializer)} which handles the serialization,
|
||||||
|
* deserialization and persisting and loading from DB.</p>
|
||||||
|
*
|
||||||
|
* @param args if first arg is CLOB then ClobSerializer is used else BlobSerializer is used.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws SQLException {
|
||||||
|
Forest forest = createForest();
|
||||||
|
LobSerializer serializer = createLobSerializer(args);
|
||||||
|
executeSerializer(forest, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a {@link LobSerializer} on the basis of input args. </p>
|
||||||
|
* <p>If input args are not empty and the value equals {@link App#CLOB} then a
|
||||||
|
* {@link ClobSerializer} is created else a {@link BlobSerializer} is created.</p>
|
||||||
|
*
|
||||||
|
* @param args if first arg is {@link App#CLOB} then ClobSerializer is instantiated else
|
||||||
|
* BlobSerializer is instantiated.
|
||||||
|
*/
|
||||||
|
private static LobSerializer createLobSerializer(String[] args) throws SQLException {
|
||||||
|
LobSerializer serializer;
|
||||||
|
if (args.length > 0 && Objects.equals(args[0], CLOB)) {
|
||||||
|
serializer = new ClobSerializer();
|
||||||
|
} else {
|
||||||
|
serializer = new BlobSerializer();
|
||||||
|
}
|
||||||
|
return serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Forest with {@link Animal} and {@link Plant} along with their respective
|
||||||
|
* relationships.
|
||||||
|
*
|
||||||
|
* <p> The method creates a {@link Forest} with 2 Plants Grass and Oak of type Herb and tree
|
||||||
|
* respectively.</p>
|
||||||
|
*
|
||||||
|
* <p> It also creates 3 animals Zebra and Buffalo which eat the plant grass. Lion consumes the
|
||||||
|
* Zebra and the Buffalo.</p>
|
||||||
|
*
|
||||||
|
* <p>With the above animals and plants and their relationships a forest
|
||||||
|
* object is created which represents the Object Graph.</p>
|
||||||
|
*
|
||||||
|
* @return Forest Object
|
||||||
|
*/
|
||||||
|
private static Forest createForest() {
|
||||||
|
Plant grass = new Plant("Grass", "Herb");
|
||||||
|
Plant oak = new Plant("Oak", "Tree");
|
||||||
|
|
||||||
|
Animal zebra = new Animal("Zebra", Set.of(grass), Collections.emptySet());
|
||||||
|
Animal buffalo = new Animal("Buffalo", Set.of(grass), Collections.emptySet());
|
||||||
|
Animal lion = new Animal("Lion", Collections.emptySet(), Set.of(zebra, buffalo));
|
||||||
|
|
||||||
|
return new Forest("Amazon", Set.of(lion, buffalo, zebra), Set.of(grass, oak));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the input object using the input serializer and persist to DB. After this it loads
|
||||||
|
* the same object back from DB and deserializes using the same serializer.
|
||||||
|
*
|
||||||
|
* @param forest Object to Serialize and Persist
|
||||||
|
* @param lobSerializer Serializer to Serialize and Deserialize Object
|
||||||
|
*/
|
||||||
|
private static void executeSerializer(Forest forest, LobSerializer lobSerializer) {
|
||||||
|
try (LobSerializer serializer = lobSerializer) {
|
||||||
|
|
||||||
|
Object serialized = serializer.serialize(forest);
|
||||||
|
int id = serializer.persistToDb(1, forest.getName(), serialized);
|
||||||
|
|
||||||
|
Object fromDb = serializer.loadFromDb(id, Forest.class.getSimpleName());
|
||||||
|
Forest forestFromDb = serializer.deSerialize(fromDb);
|
||||||
|
|
||||||
|
LOGGER.info(forestFromDb.toString());
|
||||||
|
} catch (SQLException | IOException | TransformerException | ParserConfigurationException
|
||||||
|
| SAXException
|
||||||
|
| ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.dbservice;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.h2.jdbcx.JdbcDataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to handle database operations.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DatabaseService {
|
||||||
|
|
||||||
|
public static final String CREATE_BINARY_SCHEMA_DDL =
|
||||||
|
"CREATE TABLE IF NOT EXISTS FORESTS (ID NUMBER UNIQUE, NAME VARCHAR(30),FOREST VARBINARY)";
|
||||||
|
public static final String CREATE_TEXT_SCHEMA_DDL =
|
||||||
|
"CREATE TABLE IF NOT EXISTS FORESTS (ID NUMBER UNIQUE, NAME VARCHAR(30),FOREST VARCHAR)";
|
||||||
|
public static final String DELETE_SCHEMA_SQL = "DROP TABLE FORESTS IF EXISTS";
|
||||||
|
public static final String BINARY_DATA = "BINARY";
|
||||||
|
private static final String DB_URL = "jdbc:h2:~/test";
|
||||||
|
private static final String INSERT = "insert into FORESTS (id,name, forest) values (?,?,?)";
|
||||||
|
private static final String SELECT = "select FOREST from FORESTS where id = ?";
|
||||||
|
private static final DataSource dataSource = createDataSource();
|
||||||
|
public String dataTypeDb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor initializes {@link DatabaseService#dataTypeDb}.
|
||||||
|
*
|
||||||
|
* @param dataTypeDb Type of data that is to be stored in DB can be 'TEXT' or 'BINARY'.
|
||||||
|
*/
|
||||||
|
public DatabaseService(String dataTypeDb) {
|
||||||
|
this.dataTypeDb = dataTypeDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates Data source.
|
||||||
|
*
|
||||||
|
* @return created data source
|
||||||
|
*/
|
||||||
|
private static DataSource createDataSource() {
|
||||||
|
var dataSource = new JdbcDataSource();
|
||||||
|
dataSource.setURL(DB_URL);
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown Sequence executes Query {@link DatabaseService#DELETE_SCHEMA_SQL}.
|
||||||
|
*
|
||||||
|
* @throws SQLException if any issue occurs while executing DROP Query
|
||||||
|
*/
|
||||||
|
public void shutDownService()
|
||||||
|
throws SQLException {
|
||||||
|
try (var connection = dataSource.getConnection();
|
||||||
|
var statement = connection.createStatement()) {
|
||||||
|
statement.execute(DELETE_SCHEMA_SQL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initaites startup sequence and executes the query
|
||||||
|
* {@link DatabaseService#CREATE_BINARY_SCHEMA_DDL} if {@link DatabaseService#dataTypeDb} is
|
||||||
|
* binary else will execute the query {@link DatabaseService#CREATE_TEXT_SCHEMA_DDL}.
|
||||||
|
*
|
||||||
|
* @throws SQLException if there are any issues during DDL execution
|
||||||
|
*/
|
||||||
|
public void startupService()
|
||||||
|
throws SQLException {
|
||||||
|
try (var connection = dataSource.getConnection();
|
||||||
|
var statement = connection.createStatement()) {
|
||||||
|
if (dataTypeDb.equals("BINARY")) {
|
||||||
|
statement.execute(CREATE_BINARY_SCHEMA_DDL);
|
||||||
|
} else {
|
||||||
|
statement.execute(CREATE_TEXT_SCHEMA_DDL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the insert query {@link DatabaseService#INSERT}.
|
||||||
|
*
|
||||||
|
* @param id with which row is to be inserted
|
||||||
|
* @param name name to be added in the row
|
||||||
|
* @param data object data to be saved in the row
|
||||||
|
* @throws SQLException if there are any issues in executing insert query
|
||||||
|
* {@link DatabaseService#INSERT}
|
||||||
|
*/
|
||||||
|
public void insert(int id, String name, Object data)
|
||||||
|
throws SQLException {
|
||||||
|
try (var connection = dataSource.getConnection();
|
||||||
|
var insert = connection.prepareStatement(INSERT)) {
|
||||||
|
insert.setInt(1, id);
|
||||||
|
insert.setString(2, name);
|
||||||
|
insert.setObject(3, data);
|
||||||
|
insert.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the select query {@link DatabaseService#SELECT} form the result set returns an
|
||||||
|
* {@link java.io.InputStream} if {@link DatabaseService#dataTypeDb} is 'binary' else will return
|
||||||
|
* the object as a {@link String}.
|
||||||
|
*
|
||||||
|
* @param id with which row is to be selected
|
||||||
|
* @param columnsName column in which the object is stored
|
||||||
|
* @return object found from DB
|
||||||
|
* @throws SQLException if there are any issues in executing insert query *
|
||||||
|
* {@link DatabaseService#SELECT}
|
||||||
|
*/
|
||||||
|
public Object select(final long id, String columnsName) throws SQLException {
|
||||||
|
ResultSet resultSet = null;
|
||||||
|
try (var connection = dataSource.getConnection();
|
||||||
|
var preparedStatement =
|
||||||
|
connection.prepareStatement(SELECT)
|
||||||
|
) {
|
||||||
|
Object result = null;
|
||||||
|
preparedStatement.setLong(1, id);
|
||||||
|
resultSet = preparedStatement.executeQuery();
|
||||||
|
while (resultSet.next()) {
|
||||||
|
if (dataTypeDb.equals(BINARY_DATA)) {
|
||||||
|
result = resultSet.getBinaryStream(columnsName);
|
||||||
|
} else {
|
||||||
|
result = resultSet.getString(columnsName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
if (resultSet != null) {
|
||||||
|
resultSet.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.lob;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object Animal with a list of animals and/or plants it consumes.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Animal implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Set<Plant> plantsEaten = new HashSet<>();
|
||||||
|
private Set<Animal> animalsEaten = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over the input nodes recursively and adds new plants to {@link Animal#plantsEaten} or
|
||||||
|
* animals to {@link Animal#animalsEaten} found to input sets respectively.
|
||||||
|
*
|
||||||
|
* @param childNodes contains the XML Node containing the Forest
|
||||||
|
* @param animalsEaten set of Animals eaten
|
||||||
|
* @param plantsEaten set of Plants eaten
|
||||||
|
*/
|
||||||
|
protected static void iterateXmlForAnimalAndPlants(NodeList childNodes, Set<Animal> animalsEaten,
|
||||||
|
Set<Plant> plantsEaten) {
|
||||||
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
|
Node child = childNodes.item(i);
|
||||||
|
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
if (child.getNodeName().equals(Animal.class.getSimpleName())) {
|
||||||
|
Animal animalEaten = new Animal();
|
||||||
|
animalEaten.createObjectFromXml(child);
|
||||||
|
animalsEaten.add(animalEaten);
|
||||||
|
} else if (child.getNodeName().equals(Plant.class.getSimpleName())) {
|
||||||
|
Plant plant = new Plant();
|
||||||
|
plant.createObjectFromXml(child);
|
||||||
|
plantsEaten.add(plant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides XML Representation of the Animal.
|
||||||
|
*
|
||||||
|
* @param xmlDoc object to which the XML representation is to be written to
|
||||||
|
* @return XML Element contain the Animal representation
|
||||||
|
*/
|
||||||
|
public Element toXmlElement(Document xmlDoc) {
|
||||||
|
Element root = xmlDoc.createElement(Animal.class.getSimpleName());
|
||||||
|
root.setAttribute("name", name);
|
||||||
|
for (Plant plant : plantsEaten) {
|
||||||
|
Element xmlElement = plant.toXmlElement(xmlDoc);
|
||||||
|
if (xmlElement != null) {
|
||||||
|
root.appendChild(xmlElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Animal animal : animalsEaten) {
|
||||||
|
Element xmlElement = animal.toXmlElement(xmlDoc);
|
||||||
|
if (xmlElement != null) {
|
||||||
|
root.appendChild(xmlElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xmlDoc.appendChild(root);
|
||||||
|
return (Element) xmlDoc.getFirstChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the Animal Object from the input XML Node.
|
||||||
|
*
|
||||||
|
* @param node the XML Node from which the Animal Object is to be parsed
|
||||||
|
*/
|
||||||
|
public void createObjectFromXml(Node node) {
|
||||||
|
name = node.getAttributes().getNamedItem("name").getNodeValue();
|
||||||
|
NodeList childNodes = node.getChildNodes();
|
||||||
|
iterateXmlForAnimalAndPlants(childNodes, animalsEaten, plantsEaten);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("\nAnimal Name = ").append(name);
|
||||||
|
if (!animalsEaten.isEmpty()) {
|
||||||
|
sb.append("\n\tAnimals Eaten by ").append(name).append(": ");
|
||||||
|
}
|
||||||
|
for (Animal animal : animalsEaten) {
|
||||||
|
sb.append("\n\t\t").append(animal);
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
if (!plantsEaten.isEmpty()) {
|
||||||
|
sb.append("\n\tPlants Eaten by ").append(name).append(": ");
|
||||||
|
}
|
||||||
|
for (Plant plant : plantsEaten) {
|
||||||
|
sb.append("\n\t\t").append(plant);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.lob;
|
||||||
|
|
||||||
|
import static com.iluwatar.slob.lob.Animal.iterateXmlForAnimalAndPlants;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object Forest which contains animals and plants as its constituents. Animals may eat
|
||||||
|
* plants or other animals in the forest.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Forest implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Set<Animal> animals = new HashSet<>();
|
||||||
|
private Set<Plant> plants = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the representation of Forest in XML form.
|
||||||
|
*
|
||||||
|
* @return XML Element
|
||||||
|
*/
|
||||||
|
public Element toXmlElement() throws ParserConfigurationException {
|
||||||
|
Document xmlDoc = getXmlDoc();
|
||||||
|
|
||||||
|
Element forestXml = xmlDoc.createElement("Forest");
|
||||||
|
forestXml.setAttribute("name", name);
|
||||||
|
|
||||||
|
Element animalsXml = xmlDoc.createElement("Animals");
|
||||||
|
for (Animal animal : animals) {
|
||||||
|
Element animalXml = animal.toXmlElement(xmlDoc);
|
||||||
|
animalsXml.appendChild(animalXml);
|
||||||
|
}
|
||||||
|
forestXml.appendChild(animalsXml);
|
||||||
|
|
||||||
|
Element plantsXml = xmlDoc.createElement("Plants");
|
||||||
|
for (Plant plant : plants) {
|
||||||
|
Element plantXml = plant.toXmlElement(xmlDoc);
|
||||||
|
plantsXml.appendChild(plantXml);
|
||||||
|
}
|
||||||
|
forestXml.appendChild(plantsXml);
|
||||||
|
return forestXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns XMLDoc to use for XML creation.
|
||||||
|
*
|
||||||
|
* @return XML DOC Object
|
||||||
|
* @throws ParserConfigurationException {@inheritDoc}
|
||||||
|
*/
|
||||||
|
private Document getXmlDoc() throws ParserConfigurationException {
|
||||||
|
return DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().newDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the Forest Object from the input XML Document.
|
||||||
|
*
|
||||||
|
* @param document the XML document from which the Forest is to be parsed
|
||||||
|
*/
|
||||||
|
public void createObjectFromXml(Document document) {
|
||||||
|
name = document.getDocumentElement().getAttribute("name");
|
||||||
|
NodeList nodeList = document.getElementsByTagName("*");
|
||||||
|
iterateXmlForAnimalAndPlants(nodeList, animals, plants);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("\n");
|
||||||
|
sb.append("Forest Name = ").append(name).append("\n");
|
||||||
|
sb.append("Animals found in the ").append(name).append(" Forest: \n");
|
||||||
|
for (Animal animal : animals) {
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
sb.append(animal.toString());
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("Plants in the ").append(name).append(" Forest: \n");
|
||||||
|
for (Plant plant : plants) {
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
sb.append(plant.toString());
|
||||||
|
sb.append("\n--------------------------\n");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.lob;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NamedNodeMap;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object Plant which contains its name and type.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Plant implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides XML Representation of the Plant.
|
||||||
|
*
|
||||||
|
* @param xmlDoc to which the XML representation is to be written to
|
||||||
|
* @return XML Element contain the Animal representation
|
||||||
|
*/
|
||||||
|
public Element toXmlElement(Document xmlDoc) {
|
||||||
|
Element root = xmlDoc.createElement(Plant.class.getSimpleName());
|
||||||
|
root.setAttribute("name", name);
|
||||||
|
root.setAttribute("type", type);
|
||||||
|
xmlDoc.appendChild(root);
|
||||||
|
return xmlDoc.getDocumentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the Plant Object from the input XML Node.
|
||||||
|
*
|
||||||
|
* @param node the XML Node from which the Animal Object is to be parsed
|
||||||
|
*/
|
||||||
|
public void createObjectFromXml(Node node) {
|
||||||
|
NamedNodeMap attributes = node.getAttributes();
|
||||||
|
name = attributes.getNamedItem("name").getNodeValue();
|
||||||
|
type = attributes.getNamedItem("type").getNodeValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringJoiner stringJoiner = new StringJoiner(",");
|
||||||
|
stringJoiner.add("Name = " + name);
|
||||||
|
stringJoiner.add("Type = " + type);
|
||||||
|
return stringJoiner.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.serializers;
|
||||||
|
|
||||||
|
import com.iluwatar.slob.lob.Forest;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Serializer that uses Binary serialization and deserialization of objects graph to and
|
||||||
|
* from their Binary Representation.
|
||||||
|
*/
|
||||||
|
public class BlobSerializer extends LobSerializer {
|
||||||
|
|
||||||
|
public static final String TYPE_OF_DATA_FOR_DB = "BINARY";
|
||||||
|
|
||||||
|
public BlobSerializer() throws SQLException {
|
||||||
|
super(TYPE_OF_DATA_FOR_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the input object graph to its Binary Representation using Object Stream.
|
||||||
|
*
|
||||||
|
* @param toSerialize Object which is to be serialized
|
||||||
|
* @return Serialized object
|
||||||
|
* @throws IOException {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object serialize(Forest toSerialize) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||||
|
oos.writeObject(toSerialize);
|
||||||
|
oos.close();
|
||||||
|
return new ByteArrayInputStream(baos.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the input Byte Array Stream using Object Stream and return its Object Graph
|
||||||
|
* Representation.
|
||||||
|
*
|
||||||
|
* @param toDeserialize Input Object to De-serialize
|
||||||
|
* @return Deserialized Object
|
||||||
|
* @throws ClassNotFoundException {@inheritDoc}
|
||||||
|
* @throws IOException {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Forest deSerialize(Object toDeserialize) throws IOException, ClassNotFoundException {
|
||||||
|
InputStream bis = (InputStream) toDeserialize;
|
||||||
|
Forest forest;
|
||||||
|
try (ObjectInput in = new ObjectInputStream(bis)) {
|
||||||
|
forest = (Forest) in.readObject();
|
||||||
|
}
|
||||||
|
return forest;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.serializers;
|
||||||
|
|
||||||
|
import com.iluwatar.slob.lob.Forest;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.transform.OutputKeys;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Serializer that uses Character based serialization and deserialization of objects graph
|
||||||
|
* to and from XML Representation.
|
||||||
|
*/
|
||||||
|
public class ClobSerializer extends LobSerializer {
|
||||||
|
|
||||||
|
public static final String TYPE_OF_DATA_FOR_DB = "TEXT";
|
||||||
|
|
||||||
|
public ClobSerializer() throws SQLException {
|
||||||
|
super(TYPE_OF_DATA_FOR_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the input node to its XML String Representation.
|
||||||
|
*
|
||||||
|
* @param node XML Node that is to be converted to string
|
||||||
|
* @return String representation of XML parsed from the Node
|
||||||
|
* @throws TransformerException If any issues occur in Transformation from Node to XML
|
||||||
|
*/
|
||||||
|
private static String elementToXmlString(Element node) throws TransformerException {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
Transformer t = TransformerFactory.newDefaultInstance().newTransformer();
|
||||||
|
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
||||||
|
t.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||||
|
t.transform(new DOMSource(node), new StreamResult(sw));
|
||||||
|
return sw.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the input object graph to its XML Representation using DOM Elements.
|
||||||
|
*
|
||||||
|
* @param forest Object which is to be serialized
|
||||||
|
* @return Serialized object
|
||||||
|
* @throws ParserConfigurationException If any issues occur in parsing input object
|
||||||
|
* @throws TransformerException If any issues occur in Transformation from Node to XML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object serialize(Forest forest) throws ParserConfigurationException, TransformerException {
|
||||||
|
Element xmlElement = forest.toXmlElement();
|
||||||
|
return elementToXmlString(xmlElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the input XML string using DOM Parser and return its Object Graph Representation.
|
||||||
|
*
|
||||||
|
* @param toDeserialize Input Object to De-serialize
|
||||||
|
* @return Deserialized Object
|
||||||
|
* @throws ParserConfigurationException If any issues occur in parsing input object
|
||||||
|
* @throws IOException if any issues occur during reading object
|
||||||
|
* @throws SAXException If any issues occur in Transformation from Node to XML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Forest deSerialize(Object toDeserialize)
|
||||||
|
throws ParserConfigurationException, IOException, SAXException {
|
||||||
|
DocumentBuilder documentBuilder = DocumentBuilderFactory.newDefaultInstance()
|
||||||
|
.newDocumentBuilder();
|
||||||
|
var stream = new ByteArrayInputStream(toDeserialize.toString().getBytes());
|
||||||
|
Document parsed = documentBuilder.parse(stream);
|
||||||
|
Forest forest = new Forest();
|
||||||
|
forest.createObjectFromXml(parsed);
|
||||||
|
return forest;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob.serializers;
|
||||||
|
|
||||||
|
import com.iluwatar.slob.dbservice.DatabaseService;
|
||||||
|
import com.iluwatar.slob.lob.Forest;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A LobSerializer can be used to create an instance of a serializer which can serialize and
|
||||||
|
* deserialize an object and persist and load that object into a DB. from their Binary
|
||||||
|
* Representation.
|
||||||
|
*/
|
||||||
|
public abstract class LobSerializer implements Serializable, Closeable {
|
||||||
|
|
||||||
|
private final transient DatabaseService databaseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor initializes {@link LobSerializer#databaseService}.
|
||||||
|
*
|
||||||
|
* @param dataTypeDb Input provides type of Data to be stored by the Data Base Service
|
||||||
|
* @throws SQLException If any issue occurs during instantiation of DB Service or during startup.
|
||||||
|
*/
|
||||||
|
protected LobSerializer(String dataTypeDb) throws SQLException {
|
||||||
|
databaseService = new DatabaseService(dataTypeDb);
|
||||||
|
databaseService.startupService();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the specification to Serialize the input object.
|
||||||
|
*
|
||||||
|
* @param toSerialize Input Object to serialize
|
||||||
|
* @return Serialized Object
|
||||||
|
* @throws ParserConfigurationException if any issue occurs during parsing of input object
|
||||||
|
* @throws TransformerException if any issue occurs during Transformation
|
||||||
|
* @throws IOException if any issues occur during reading object
|
||||||
|
*/
|
||||||
|
public abstract Object serialize(Forest toSerialize)
|
||||||
|
throws ParserConfigurationException, TransformerException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the object to DB with the provided ID.
|
||||||
|
*
|
||||||
|
* @param id key to be sent to DB service
|
||||||
|
* @param name Object name to store in DB
|
||||||
|
* @param object Object to store in DB
|
||||||
|
* @return ID with which the object is stored in DB
|
||||||
|
* @throws SQLException if any issue occurs while saving to DB
|
||||||
|
*/
|
||||||
|
public int persistToDb(int id, String name, Object object) throws SQLException {
|
||||||
|
databaseService.insert(id, name, object);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the object from db using the ID and column name.
|
||||||
|
*
|
||||||
|
* @param id to query the DB
|
||||||
|
* @param columnName column from which object is to be extracted
|
||||||
|
* @return Object from DB
|
||||||
|
* @throws SQLException if any issue occurs while loading from DB
|
||||||
|
*/
|
||||||
|
public Object loadFromDb(int id, String columnName) throws SQLException {
|
||||||
|
return databaseService.select(id, columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the specification to Deserialize the input object.
|
||||||
|
*
|
||||||
|
* @param toDeserialize object to deserialize
|
||||||
|
* @return Deserialized Object
|
||||||
|
* @throws ParserConfigurationException If issue occurs during parsing of input object
|
||||||
|
* @throws IOException if any issues occur during reading object
|
||||||
|
* @throws SAXException if any issues occur during reading object for XML parsing
|
||||||
|
*/
|
||||||
|
public abstract Forest deSerialize(Object toDeserialize)
|
||||||
|
throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
databaseService.shutDownService();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2022 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.slob;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
|
import com.iluwatar.slob.lob.Animal;
|
||||||
|
import com.iluwatar.slob.lob.Forest;
|
||||||
|
import com.iluwatar.slob.lob.Plant;
|
||||||
|
import com.iluwatar.slob.serializers.BlobSerializer;
|
||||||
|
import com.iluwatar.slob.serializers.ClobSerializer;
|
||||||
|
import com.iluwatar.slob.serializers.LobSerializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SLOB Application test
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
class AppTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Forest with Animals and Plants along with their respective relationships.
|
||||||
|
* <p> The method creates a forest with 2 Plants Grass and Oak of type Herb and tree
|
||||||
|
* respectively.</p>
|
||||||
|
* <p> It also creates 3 animals Zebra and Buffalo which eat the plant grass. Lion consumes the
|
||||||
|
* Zebra and the Buffalo.</p>
|
||||||
|
* <p>With the above animals and plants and their relationships a forest
|
||||||
|
* object is created which represents the Object Graph.</p>
|
||||||
|
*
|
||||||
|
* @return Forest Object
|
||||||
|
*/
|
||||||
|
private static Forest createForest() {
|
||||||
|
Plant grass = new Plant("Grass", "Herb");
|
||||||
|
Plant oak = new Plant("Oak", "Tree");
|
||||||
|
|
||||||
|
Animal zebra = new Animal("Zebra", Set.of(grass), Collections.emptySet());
|
||||||
|
Animal buffalo = new Animal("Buffalo", Set.of(grass), Collections.emptySet());
|
||||||
|
Animal lion = new Animal("Lion", Collections.emptySet(), Set.of(zebra, buffalo));
|
||||||
|
|
||||||
|
return new Forest("Amazon", Set.of(lion, buffalo, zebra), Set.of(grass, oak));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link App} without passing any argument in the args to test the
|
||||||
|
* {@link ClobSerializer}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void shouldExecuteWithoutExceptionClob() {
|
||||||
|
assertDoesNotThrow(() -> App.main(new String[]{"CLOB"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link App} without passing any argument in the args to test the
|
||||||
|
* {@link BlobSerializer}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void shouldExecuteWithoutExceptionBlob() {
|
||||||
|
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the serialization of the input object using the {@link ClobSerializer} and persists the
|
||||||
|
* serialized object to DB, then load the object back from DB and deserializes it using the
|
||||||
|
* provided {@link ClobSerializer}.<p>After loading the object back from DB the test matches the
|
||||||
|
* hash of the input object with the hash of the object that was loaded from DB and deserialized.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void clobSerializerTest() {
|
||||||
|
Forest forest = createForest();
|
||||||
|
try (LobSerializer serializer = new ClobSerializer()) {
|
||||||
|
|
||||||
|
Object serialized = serializer.serialize(forest);
|
||||||
|
int id = serializer.persistToDb(1, forest.getName(), serialized);
|
||||||
|
|
||||||
|
Object fromDb = serializer.loadFromDb(id, Forest.class.getSimpleName());
|
||||||
|
Forest forestFromDb = serializer.deSerialize(fromDb);
|
||||||
|
|
||||||
|
Assertions.assertEquals(forest.hashCode(), forestFromDb.hashCode(),
|
||||||
|
"Hashes of objects after Serializing and Deserializing are the same");
|
||||||
|
} catch (SQLException | IOException | TransformerException | ParserConfigurationException |
|
||||||
|
SAXException |
|
||||||
|
ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the serialization of the input object using the {@link BlobSerializer} and persists the
|
||||||
|
* serialized object to DB, then loads the object back from DB and deserializes it using the
|
||||||
|
* {@link BlobSerializer}.<p>After loading the object back from DB the test matches the hash of
|
||||||
|
* the input object with the hash of the object that was loaded from DB and deserialized.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void blobSerializerTest() {
|
||||||
|
Forest forest = createForest();
|
||||||
|
try (LobSerializer serializer = new BlobSerializer()) {
|
||||||
|
|
||||||
|
Object serialized = serializer.serialize(forest);
|
||||||
|
int id = serializer.persistToDb(1, forest.getName(), serialized);
|
||||||
|
|
||||||
|
Object fromDb = serializer.loadFromDb(id, Forest.class.getSimpleName());
|
||||||
|
Forest forestFromDb = serializer.deSerialize(fromDb);
|
||||||
|
|
||||||
|
Assertions.assertEquals(forest.hashCode(), forestFromDb.hashCode(),
|
||||||
|
"Hashes of objects after Serializing and Deserializing are the same");
|
||||||
|
} catch (SQLException | IOException | TransformerException | ParserConfigurationException |
|
||||||
|
SAXException |
|
||||||
|
ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user