Chapter 4
A Zero-Code Contact Manager
Let's write our own version of the contact manager. The point of this
chapter is to lay the foundation for writing JMatter applications.
You will learn how to create and setup a project, what information
goes where, some of the configuration files and their role in a JMatter
application. As the title of this chapter indicates, we will not have
to write a single line of code to implement our first application!
4.1 Creating a New Project
To create a new project in JMatter, invoke the new-project-ui
ant target:
-
$ cd jmatter
$ ant new-project-ui
We need to decide on a name for our new project. Let's call it CM
(short for ContactMgr), so as not to conflict with the existing demo
version of the contact manager application.
Figure 4.1: New Project User Interface
We have the option of creating a standalone or dependent project.
It doesn't really matter at this point. Let's go with dependent15. Go ahead and click on the Create Project button. This action
will launch an ant target that takes our input and creates a complete
directory structure for us. If you didn't specify a base directory,
the new project is created by default in the jmatter directory's parent
directory.
Go ahead and quit the new-project-ui "applet." Let's navigate
to our new project's base directory:
-
$ cd ../CM
$ ls
build.xml doc/ resources/ src/ test/
JMatter creates the directory structure for your project, along with
its own ant build file.
4.2 The Schema
We would normally proceed by modeling our application: identifying
the entities, and implementing them as Java classes. With JMatter
however, things are a little different. JMatter was designed to be
a framework for building business applications. As such, JMatter predefines
a number of common types of business objects, including persons, their
contact information, addresses, and businesses. In this chapter we'll
reuse the predefined implementations of Person and Business, whose
fully-qualified class names are com.u2d.type.composite.Person
and com.u2d.type.composite.Business, respectively.
Before we generate our schema, we need a way to communicate to JMatter
the various types of objects we would like to persist. We typically
do this by adding an annotation (@Entity) directly on the model
classes that we define. However in this case, since we're using pre-existing
model classes, we do this by editing the underlying template, src/persistClasses.st,
directly:
-
..
<value>com.u2d.type.composite.Person</value>
<value>com.u2d.type.composite.Business</value>
..
We simply add the fully qualified names of the two classes Person
and Business to the listing. We're now ready to generate the
database schema:
-
$ ant schema-export
Behind the scenes, JMatter introspects the classes in question and
for each it generates a Hibernate database mapping file, and finally
asks hibernate to generate the DDL (data definition language) instructions
and send them to the database.
We should now be able to inspect the contents of our database and
verify that it contains these tables. By default, JMatter uses the
H2 database, which is lightweight and requires no work on your part
16. By default, JMatter created the database CMh2db in the db/
subdirectory of your project. To view its contents, you can use the
H2 web-based console. First you must start the H2 server, with this
command:
-
$ java -cp ../jmatter/lib/runtime/jdbc/h2.jar org.h2.tools.Server
Then point your web browser to http://localhost:8082/ and login
to the database (default username is sa, and the password is
blank). These details are much better documented by H2's own documentation,
of course.
4.3 The Class Bar
Similar to the task of specifying which types to persist to the database,
we also need to specify which types to expose to the user interface
via the Class Bar. This step is not really necessary, because we can
edit (in this case, add types to) the classbar directly from the user
interface. Feel free to skip this section if you wish.
The way to predefine the default classbar items is to edit src/class-list.json:
-
{
"type": "com.u2d.type.composite.Folder",
"items": {
"item-type": "com.u2d.type.composite.Folder",
"items": [
{
"name": "Model",
"items": {
"item-type": "com.u2d.model.ComplexType",
"items": [
{ "value": "com.u2d.contactmgr.Person" },
{ "value": "com.u2d.type.composite.Business" },
{ "value": "com.u2d.type.composite.Folder" },
...
This file is nothing but a marshalled Folder object (in JSON format17). When the application starts up, the file will be unmarshalled back
as a Folder object. JMatter will detect that our database is empty
(it contains no classbar) and will automatically persist the folder.
Each user has his or her own class list. The xml file serves as a
template. This is useful since many applications have multiple users,
with different roles. So different classbars can be defined for each
role.
4.4 Running Your Contact Manager
Let's do one more thing before we run our application: let's give
it a splash screen. Do this by dropping a file named splash.png
(or splash.jpg or splash.gif) into the resources/images
folder. If you like, just copy the one from the demo application.
Ok, let's run our app:
-
$ ant run
That's it. We now have a working contact manager application. Go ahead
and exercise it: create new person objects, etc.. (see figure 4.2).
Figure 4.2:
Classes organized into two subfolders:
Manager and Administrative
4.5 Custom Views Revisited
In subsection 3.2.5.1 I had mentioned that the Class
Bar can be customized directly from the User Interface. We're at a
good place to show you how this works. Follow these instructions:
- Launch the application and log in
- Click on the "Administrative" folder in the Class Bar
- Browse Types (right-click Types, select Browse)
We've just asked our application to show us all the types defined
in our system (Note that Contact Methods is among the list
of defined types, though it doesn't have an icon, and thus shows up
with a question mark icon).
- Click on the Manager folder in the Class Bar
- Drag Contact Methods to the empty area inside the Manager
folder in the class bar
We have just added a type to the class list. Our class bar should
now resemble figure 4.3.
Figure 4.3:
Class Bar Customized with an Additional
Type: Contact Methods
4.6 Project Structure
Let's take a closer look at the files in our project.
JMatter defines certain conventions for where to place files. These
conventions are probably very similar to your existing project conventions.
Here is a summary of these conventions:
- Source code resides in the src/ directory
- You will find certain resources, such as images, in the resources
directory
- The ant build file for your project uses the default name build.xml
and resides in your project's base directory
- Your source code is compiled to the target directory build/classes
- JMatter generates Hibernate mapping files for persisting your
domain objects; these files by convention bear the suffix ".hbm.xml"
and also are written to the build/classes directory
- When deploying your application with Java Web Start (see chapter 18),
the distribution file is placed in the dist directory
- The doc directory contains a simple README file, serving
as a quick reference or as simple setup instructions to help you get
started on a new project. You are also urged to use this folder to
place any relevant documentation for your project.
- The test directory is created for you to place JUnit tests
you write to ensure that your code and business logic is correct and
bug-free
4.6.2 Ant Targets
You don't need to memorize the names of the ant targets that we invoke
(e.g. ant run, ant schema-export); simply type:
-
ant -projecthelp
to view a list of all the defined targets: their name and description
(Of course, if you're running GNU/Linux and have bash smart completion
turned on, simply pressing the tab key will display a list of valid
ant targets).
4.6.3 A Peek at the Source Code
Open the file Person.java in a text editor or IDE18 (it is located in jmatter/src/com/u2d/type/composite). We're
not going to attempt to understand every line of code just yet.
What I want you to notice is that a Person defines two fields: name,
and contact:
-
protected final Name _name = new Name();
protected final Contact _contact = new Contact();
The Java classes Name and Contact are also defined in
the framework. The Name class is composed of the parts that
make up a name: prefix, first, middle, last, and suffix.
If you dig a little deeper, you will see that Contact.java
defines these fields:
-
private final USPhone _workPhone = new USPhone();
private final USPhone _mobilePhone = new USPhone();
private final USPhone _fax = new USPhone();
private final Email _email = new Email();
private final USAddress _address = new USAddress();
private ContactMethod _preferredContactMethod = new ContactMethod();
Here we see that JMatter has a built-in understanding of types such
as a US Phone number, an Email address, a US Address and a Contact
Method.
If you inspect the generated database schema for our application,
you will not find a database table to hold contact information or
addresses.
The reason is that with JMatter, you have the choice of modeling a
field either as an association or as an aggregation. In the context
of the class Person, Contact and USAddress are
modeled as aggregates, and so are treated in a manner similar to value
fields such as the work phone or email address. The fields that make
up the contact information and address are aggregated into the parent
type's table. Hibernate calls these Components. If you're familiar
with the concepts in the book Domain-Driven Design [5],
then you're already familiar with both Aggregates and Value Objects.
We'll learn more about ways of modeling relationships between objects
in the next chapter. Chapter 8 provides a
more complete reference.
4.7 Summary
Here's a quick recap of what we learned in this chapter:
- We learned how to create a new project skeleton with the ant
new-project-ui command;
- We are aware of two configuration files src/persistClasses.st
and src/class-list.json and know a little about how to configure
them;
- Also, we learned that images go in the resources/images folder;
- We learned the basic commands for creating our schema and running
our application: ant schema-export and ant run
- We studied the structure of a JMatter child project
With the basics for setting up, configuring, and running projects
under our belt, we're now ready to do some real work. In the next
chapter, we're finally going to get a chance to write some code in
the MyTunes music application.