My wife and I are going to attend a wedding on July 5th. I am going to wear black dress shoes. A week or so ago while at Nordstrom’s at the MOA I was going to get a pair of shoes for the occasion. My wife mentioned that I have a pair of never worn black shoes still in their box. This morning, my wife gave me the shoes. I decided to wear them at home for a few days in order to make them mold to my feet. Seems like most of the times I wear new shoes I get blisters on my feet. I am the type of person that wears thick socks even when wearing sandals. Will see how things went after the wedding.
When you read the word “castor” what do you think it refers to? No, it is not castor oil. I always like to name variables, functions / methods, and products in ways they reflect their intended use. I tried looking for the etymology of the word and how could I associate it to the framework (spoil alert). Castor is an old French word for beaver. If you find out why Apache named the Castor framework as they did, please let me know.
Wikipedia describes Castor as a data binding framework for the Java language. Take a look at that they have to say about this topic here.
The best way to learn a topic / subject is to read and experiment with it. I will use Maven and Eclipse Oxygen to experiment with Castor. I will use a Windows 10 machine. I could have done it on Linux but I am waiting for a test to complete on this machine.
Let’s create a simple Maven project. To determine which version of Maven is installed, from a command prompt:
> mvn --version C:\ Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T10:41:47-06:00) Maven home: C:\apache-maven-3.3.9 Java version: 1.8.0_112, vendor: Oracle Corporation Java home: C:\ProgramData\Java\jdk1.8.0_112\jre Default locale: en_US, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "dos" >
I like to keep my projects in specific directories. To create this project I will first get to the selected (workspace4) target folder:
C:\>cd C:\Users\John\workspace4 >
To create a simple Java project in which to experiment with Castor:
> mvn archetype:generate -DgroupId=com.canessa.app -DartifactId=castor -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] >>> maven-archetype-plugin:2.4:generate (default-cli) > generate-sources @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:2.4:generate (default-cli) < generate-sources @ standalone-pom <<< [INFO] [INFO] --- maven-archetype-plugin:2.4:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Batch mode [INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.0 [INFO] ---------------------------------------------------------------------------- [INFO] Parameter: basedir, Value: C:\Users\John\workspace4 [INFO] Parameter: package, Value: com.canessa.app [INFO] Parameter: groupId, Value: com.canessa.app [INFO] Parameter: artifactId, Value: castor [INFO] Parameter: packageName, Value: com.canessa.app [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] project created from Old (1.x) Archetype in dir: C:\Users\John\workspace4\castor [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 13.198 s [INFO] Finished at: 2018-07-08T15:35:27-05:00 [INFO] Final Memory: 14M/223M [INFO] ------------------------------------------------------------------------ >
If all went as expected, we should have a new simple Java project named castor. Let’s check if the project was created:
> dir /S castor Volume in drive C is OS Volume Serial Number is 26E8-87B0 Directory of C:\Temp\maven\castor 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM 658 pom.xml 07/08/2018 03:35 PM <DIR> src 1 File(s) 658 bytes Directory of C:\Temp\maven\castor\src 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> main 07/08/2018 03:35 PM <DIR> test 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\main 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> java 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\main\java 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> com 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\main\java\com 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> canessa 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\main\java\com\canessa 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> app 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\main\java\com\canessa\app 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM 191 App.java 1 File(s) 191 bytes Directory of C:\Temp\maven\castor\src\test 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> java 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\test\java 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> com 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\test\java\com 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> canessa 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\test\java\com\canessa 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM <DIR> app 0 File(s) 0 bytes Directory of C:\Temp\maven\castor\src\test\java\com\canessa\app 07/08/2018 03:35 PM <DIR> . 07/08/2018 03:35 PM <DIR> .. 07/08/2018 03:35 PM 681 AppTest.java 1 File(s) 681 bytes Total Files Listed: 3 File(s) 1,530 bytes 35 Dir(s) 636,273,283,072 bytes free >
Now let’s open the new project using Eclipse. We could have used IntelliJ or any other Java IDE with similar results.
File -> Import…
From the Import window:
Maven -> Existing Maven Projects
<Next>
From “Import Maven Projects” window:
Click on <Browse…>
On the “Select Root Folder” window:
Navigate to C:\Users\John\workspace4 and select workspace4
Click on the <OK> button.
Back on the “Import Maven Projects” window select:
/castor/pom.xml
Click on the <Finish> button.
To check the source code generated by Maven, on the Eclipse “Package Explorer” window navigate to:
castor-> src/main/java -> com.canessa.app -> App.java
The following source code is displayed:
package com.canessa.app; /** * Hello world **/ public class App { public static void main( String[] args ) { System.out.println( "Hello World!" ); } }
To verify it builds, click on the green arrow on the Eclipse toolbar and run the project. On the IDE console the following is displayed:
Hello World!
So far, so good; it seems that we are ready to start experimenting with Castor. I was initially going to use the code samples found in the Castor Wikipedia article. After further research I decided to go with the code found here.
The code is quite simple and educational. First I spent experimenting converting a Java object to XML and back without the use of a mapping.xml file. After a few passes I added code to experiment using a mapping file.
The project ended up with two Java files; App.java and Article.java. The App.java file is used to convert the object back and forth. The article file is used to define the object used for the conversion.
Let’s take a look at the class for the article object:
package com.canessa.app; import java.util.LinkedList; import java.util.List; public class Article { // **** members **** private String title; private String url; private List<String> categories; private List<String> tags; // **** getters and setters **** public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public List<String> getCategories() { return categories; } public void setCategories(List<String> categories) { this.categories = categories; } public List<String> getTags() { return tags; } public void setTags(List<String> tags) { this.tags = tags; } // **** other methods **** public void addCategory(String category) { if(this.categories == null) { this.categories = new LinkedList<String>(); } this.categories.add(category); } public void addTag(String tag) { if(this.tags == null) { this.tags = new LinkedList<String>(); } this.tags.add(tag); } @Override public String toString() { return "[title: " + title + " - url: " + url + " - categories: " + categories + " - tags: " + tags + "]"; } }
The Article object has four fields. Following the members the getter and setters are implemented. There are two additional methods to directly add a category and to add a tag to an article. The toString() method is overridden so we can verify that the object being encoded is properly returned. This is achieved by printing the object before and after encoding.
The App.java file follows:
package com.canessa.app; import java.util.LinkedList; import java.util.List; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; //**** needed by castor **** import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.ValidationException; import org.exolab.castor.xml.XMLContext; /** * */ public class App { /* * */ public static Article createArticle() { // **** instantiate an article **** Article article = new Article(); // **** populate the article **** article.setTitle("Deep Work"); article.setUrl("http://www.johncanessa.com/2018/06/06/deep-work/"); List<String> categories = new LinkedList<String>(); categories.add("Books"); article.setCategories(categories); article.addCategory("Other"); List<String> tags = new LinkedList<String>(); tags.add("Cal Newport"); tags.add("Deep Work"); article.setTags(tags); article.addTag("Drop Social Networks"); article.addTag("To Do List"); // **** display the article **** System.out.println("createArticle <<< article ==>" + article.toString() + "<=="); // **** return the article **** return article; } /* * main entry point */ public static void main(String[] args) { // **** **** final String fileName = "c:\\temp\\castor\\article.xml"; final String mappedFileName = "c:\\temp\\castor\\mapped_article.xml"; final String mappingFile = "c:\\temp\\castor\\mapping.xml"; // **** create a new article **** Article article = createArticle(); // **** (1) object ==> XML **** try { FileWriter writer = new FileWriter(fileName); Marshaller.marshal(article, writer); writer.close(); } catch (IOException ex) { ex.printStackTrace(); System.exit(-1); } catch (MarshalException ex) { ex.printStackTrace(); System.exit(-1); } catch (ValidationException ex) { ex.printStackTrace(); System.exit(-1); } finally {} // **** (2) XML ==> object **** try { FileReader reader = new FileReader(fileName); article = (Article)Unmarshaller.unmarshal(Article.class, reader); } catch (FileNotFoundException ex) { ex.printStackTrace(); System.exit(-1); } catch (MarshalException ex) { ex.printStackTrace(); System.exit(-1); } catch (ValidationException ex) { ex.printStackTrace(); System.exit(-1); } finally {} // **** display the returned article **** System.out.println("main <<< article ==>" + article.toString() + "<=="); // **** load mapping information **** Mapping mapping = new Mapping(); XMLContext context = null; try { mapping.loadMapping(mappingFile); context = new XMLContext(); context.addMapping(mapping); } catch (FileNotFoundException ex) { ex.printStackTrace(); System.exit(-1); } catch (MappingException ex) { ex.printStackTrace(); System.exit(-1); } catch (IOException ex) { ex.printStackTrace(); System.exit(-1); } finally {} // **** (3) object ==> XML **** try { Writer writer = new FileWriter(mappedFileName); Marshaller marshaller = context.createMarshaller(); marshaller.setWriter(writer); marshaller.marshal(createArticle()); writer.close(); } catch (IOException ex) { ex.printStackTrace(); System.exit(-1); } catch (MarshalException ex) { ex.printStackTrace(); System.exit(-1); } catch (ValidationException ex) { ex.printStackTrace(); System.exit(-1); } finally {} // **** (4) XML ==> object **** try { Reader reader = new FileReader(mappedFileName); Unmarshaller unmarshaller = context.createUnmarshaller(); unmarshaller.setClass(Article.class); article = (Article)unmarshaller.unmarshal(reader); reader.close(); } catch (FileNotFoundException ex) { ex.printStackTrace(); System.exit(-1); } catch (ValidationException ex) { ex.printStackTrace(); System.exit(-1); } catch (MarshalException ex) { ex.printStackTrace(); System.exit(-1); } catch (IOException ex) { ex.printStackTrace(); System.exit(-1); } finally {} // **** display the returned article **** System.out.println("main <<< article ==>" + article.toString() + "<=="); } }
It contains the createArticle() method used to populate an object. I populated the object with information from a previous article in my blog. The main() method is used for the following:
Convert an object to XML no mapping used. |
Convert the XML back to an object. |
Convert an object to XML using a mapping. |
Convert the XML back to an object. |
Following is the console output during the execution of the code:
createArticle <<< article ==>[title: Deep Work - url: http://www.johncanessa.com/2018/06/06/deep-work/ - categories: [Books, Other] - tags: [Cal Newport, Deep Work, Drop Social Networks, To Do List]]<== Jul 08, 2018 9:37:27 AM org.exolab.castor.xml.Marshaller staticMarshal INFO: Marshaller called using one of the *static* marshal(Object, *) methods. This will ignore any mapping files as specified. Please consider switching to using Marshaller instances and calling one of the marshal(*) methods. main <<< article ==>[title: Deep Work - url: http://www.johncanessa.com/2018/06/06/deep-work/ - categories: [Books, Other] - tags: [Cal Newport, Deep Work, Drop Social Networks, To Do List]]<== Jul 08, 2018 9:37:27 AM org.exolab.castor.mapping.Mapping setBaseURL INFO: c:\temp\castor is not a URL, trying to convert it to a file URL Jul 08, 2018 9:37:27 AM org.exolab.castor.mapping.Mapping loadMapping INFO: Loading mapping descriptors from mapping.xml createArticle <<< article ==>[title: Deep Work - url: http://www.johncanessa.com/2018/06/06/deep-work/ - categories: [Books, Other] - tags: [Cal Newport, Deep Work, Drop Social Networks, To Do List]]<== main <<< article ==>[title: Deep Work - url: http://www.johncanessa.com/2018/06/06/deep-work/ - categories: [Books, Other] - tags: [Cal Newport, Deep Work, Drop Social Networks, To Do List]]<==
Note that the same object is displayed before the conversion and after the conversion. The first time without using a mapping file and the second using a mapping file. The display of the objects is identical.
Following is the first XML file created for the object:
<?xml version="1.0" encoding="UTF-8"?> <article> <categories xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:java.lang.String">Books</categories> <categories xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:java.lang.String">Other</categories> <tags xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:java.lang.String">Cal Newport</tags> <tags xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:java.lang.String">Deep Work</tags> <tags xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:java.lang.String">Drop Social Networks</tags> <tags xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" xsi:type="java:java.lang.String">To Do List</tags> <url>http://www.johncanessa.com/2018/06/06/deep-work/</url> <title>Deep Work</title> </article>
Following is the second XML file created for the same object. In this case using a mapping XML file:
<?xml version="1.0" encoding="UTF-8"?> <article title="Deep Work"> <url>http://www.johncanessa.com/2018/06/06/deep-work/</url> <category>Books</category> <category>Other</category> <tag>Cal Newport</tag> <tag>Deep Work</tag> <tag>Drop Social Networks</tag> <tag>To Do List</tag> </article>
As you can see the second one is smaller and cleaner.
Hope you enjoyed this post. Depending on the reason for using the Castor framework, you might want to explore, depending on use, an ORM for a specific database. All the details seem to be addressed with a single product resulting in simpler code and less moving parts. Always try to limit the amount of code written. Code that has not been written does not have bugs and does not need to be maintained or learned.
Enjoy;
John