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:
Kishalay Pandey
2024-03-26 23:57:17 +05:30
committed by GitHub
parent be3a184445
commit b5ddd62145
14 changed files with 1563 additions and 0 deletions
+1
View File
@@ -215,6 +215,7 @@
<module>single-table-inheritance</module>
<module>dynamic-proxy</module>
<module>gateway</module>
<module>slob</module>
</modules>
<repositories>
<repository>
+354
View File
@@ -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 isnt in the objects but in the links between them.
* Objects dont 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
![alt text](./etc/slob.urm.png "Serialized LOB")
## 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 arent 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 isnt 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 cant query the structure using SQL.
* SQL extensions appear to get at XML data within a field, but thats 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

+49
View File
@@ -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
+78
View File
@@ -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);
}
}
}