The Ujorm tutorial.
Content
What is UJO ?
The Ujorm core (original name was UJO Framework)
is an open source Java small library which providing non-traditional objects based on the key‑value architecture
to open up new exciting opportunities for writing efficient code.
The original idea was a toy with generic data types of Java 5.0 however over
time it appears, that the architecture has many interesting features:
- an easy object introspection without a heavy PropertyDescriptor implementation
- value type safe solution
- a transfer of the UJO object Keys (original clas same was Property) by a collection
- the core of the building has two interfaces for an easy implementation
- the object-relation mapping (ORM) support
These keys open up new possibilities for the use in the J2EE mainly in a production
of generic operations over objects. Framework contains some tools for managing the UJO object
in addition. So what are the main advantages?
- the possibility to copy all or selected attributes from source to new object by a one statement (method)
- clone the object by a selected depth
- use a generic comparator
- find a property by its text name
- write a value by the found property to an another object or a collection of objects
- restore a default value of the property
- easy text conversions of the properties
- simplified work with an arrays (you can't initialize array before add method e.g.)
- generic toString() method for easy debugging
- easy visualization of UJO object(s) by JTable
- easy persistence of UJO to different formats including XML
- criteria tool for searching the Ujo objects in list
- the object itself recommends the availability of its attributes
- there is possible to implement a listener
to only one place
for each keys including children
- support of JavaBeans
- checking by unit tests
- tiny framework without further dependencies
- open source
UJO is an abbreviation for Unified Java Object
and they are all objects,
which implements an interface Ujo.
You can imagine the object UJO like a map (an object that maps keys to values)
with data access by methods of the key for example.
This key is made up of the implementation of the Key
including (among others) two type-safe methods:
There is possible to send the keys to another objects simply contrary of the methods of JavaBean object.
An example of usage can be a list of table columns sent to the data model type of TableModel
or references to the methods of the bean in a JSP page.
The core of the project are two interfaces
Ujo a
Key,
which have got three abstract implementation:
- AbstractUjo - great performance objects implemented by the object array
- MapUjo - a memory safe solution for the case of the sparse attribute values, the solution is based on the object HashMap
- BeanUjo - the implementation maps the keys directly to the methods of JavaBean object using Java reflection
I took inspiration for my work from project Cayenne (a solution for a persistence BO
ORM)
Simplified Key
The interface
WeakKey is a simplified
Key interface without the generic domain type parameter
and it is intended for a common use.
The WeakKey need not to use Ujo objects,
because it works with objects of type Map
and List
.
Note, the default implementation does not have the full support of several methods:
, and some more methods are not fully type-safe.
To create new keys use an instance of the factory class WeakKeyFactory.
The sample of use
public class MyService {
private static final WeakKeyFactory f = new WeakKeyFactory(MyService.class);
public static final WeakKey<String> NAME = f.newKey();
public static final WeakKey<Date> BORN = f.newKey();
public static final WeakKey<Boolean> WIFE = f.newKeyDefault(Boolean.TRUE);
public static final WeakKey<BigDecimal> CASH = f.newKeyDefault(BigDecimal.ZERO);
static {
f.lock();
}
public void testWeakKeys2Map() {
Map<String,Object> map = new HashMap<String, Object>();
assert NAME.of(map) == null;
assert BORN.of(map) == null;
assert WIFE.of(map) == Boolean.TRUE;
assert CASH.of(map) == BigDecimal.ZERO;
final String name = "Lucy";
final Boolean wife = true;
final Date today = new Date();
final BigDecimal cash = BigDecimal.TEN;
NAME.setValue(map, name);
BORN.setValue(map, today);
WIFE.setValue(map, wife);
CASH.setValue(map, cash);
assert NAME.of(map).equals(name);
assert BORN.of(map).equals(today);
assert WIFE.of(map).equals(wife);
assert CASH.of(map).equals(cash);
}
public void testWeakKeys2List() {
List<Object> list = new ArrayList<Object>();
assert NAME.of(list) == null;
assert BORN.of(list) == null;
assert WIFE.of(list) == Boolean.TRUE;
assert CASH.of(list) == BigDecimal.ZERO;
final String name = "Lucy";
final Boolean wife = true;
final Date today = new Date();
final BigDecimal cash = BigDecimal.TEN;
NAME.setValue(list, name);
BORN.setValue(list, today);
WIFE.setValue(list, wife);
CASH.setValue(list, cash);
assert NAME.of(list).equals(name);
assert BORN.of(list).equals(today);
assert WIFE.of(list).equals(wife);
assert CASH.of(list).equals(cash);
}
}
The next example shows some interesting features of the Key object.
public void testWeakKeyAttributes() {
assert NAME.getIndex()==0;
assert BORN.getIndex()==1;
assert WIFE.getIndex()==2;
assert CASH.getIndex()==3;
assert NAME.getName().equals("name");
assert BORN.getName().equals("born");
assert WIFE.getName().equals("wife");
assert CASH.getName().equals("cash");
assert NAME.isTypeOf(CharSequence.class);
assert BORN.isTypeOf(Date.class);
assert WIFE.isTypeOf(Boolean.class);
assert CASH.isTypeOf(BigDecimal.class);
}
Where to use the WeakKey?
- the WeakKey can be useful on support any Map with a String key for better type-safe features
- a Map implementation can be simply replaced by the List if you like
- support to building URL parameters
- Spring module provides a special class
AbstractAplicationContextAdapter
to get Spring services by the WeakKey, see the example
- understanding the WeakKey serves as a great introduction to the Ujo architecture.
My first Ujo object
Ujo interface is an envelope for any data source. Ujo object have got your own related Keys always.
So, how to write the first Ujo object? The fastest way is to use some abstract UJO implementation from this project and via few code lines we can make quickly
and easily our own implementation, too. Here is an example - the implementation of the Person class by an abstract AbstractUjo class.
The class code
The typical implementation of the Person class based on the AbstractUjo:
public class Person extends AbstractUjo {
private static final KeyFactory<Person> f = newFactory(Person.class);
public static final Key<Person,String > NAME = f.newKey();
public static final Key<Person,Boolean> MALE = f.newKey();
public static final Key<Person,Double > CASH = f.newKey();
@Override public KeyList<?> readKeys() {
return f.getKeys();
}
}
or more easy implementation without the class KeyFactory for a child of the class QuickUjo:
public class Person extends QuickUjo {
public static final Key<Person,String > NAME = newKey();
public static final Key<Person,Boolean> MALE = newKey();
public static final Key<Person,Double > CASH = newKey();
}
Now, when the Person class is done, we can make its instance and enter or fetch some data. It´s possible to use one of two API methods for writing the object attributes:
- Person.writeValue(Key, Object), or
- Key.setValue(person, Object).
These methods are equivalent from a point of result, but the second solution offers the type safe access during the writing and reading of a value.
A show of the second (2) method use up:
import static org.Person.*;
Person person = new Person();
NAME.setValue(person, "Pavel Ponec");
MALE.setValue(person, true);
CASH.setValue(person, 34.5);
String name = NAME.of(person);
boolean male = MALE.of(person);
double cash = CASH.of(person);
There is possible to use an extended API with a more obvious source code
to the property access since the UJO release 0.80.
The new solution allows to you a chaining more keys according to the model of a language
Groovy.
The new API extends the old one so that you can use an combination of both types.
import static org.Person.*;
Person person = new Person();
person.set(NAME, "Pavel Ponec");
person.set(MALE, true);
person.set(CASH, 34.5);
String name = person.get(NAME);
boolean male = person.get(MALE);
double cash = person.get(CASH);
A note:
If you use a compilation parameter -Xlint:unchecked, an assembler can warn you of absent declaration of generic data types in the Key initialization sometimes.
There are three possibilities now:
- to complement the generic data types to the right side of the expression according to the left side, or
- to switch off the -Xlint:unchecked parameter. The switched off parameter is implied to the IDE NetBeans setting.
- the class can be equipped an annotation @SuppressWarnings("unchecked")
I've chosen this possibility myself, the code is getting clearer and in the case
of collision in the same line it´s possible to find it out easily.
- since version 0.72 is possible to use a type safe method newKey(name, type) pro building a Property object.
The implementation of the equals() method
Our Person class has the equals() function implemented already, which inherited from parental class AbstractUjo.
But if you want by some reason to write your own Ujo implementation with this method, it´s enough to write into the class this code:
public boolean equals(Object obj) {
return UjoManager.getInstanceManager().equals(this, (Ujo) obj );
}
Implementation of the clone() method
Analogous to the equals method you can make the clone() method :
public Object clone() {
return UjoManager.getInstanceManager().clone(this, 1, null);
}
Implementation of the hash() method
A similar example of the hash() method implementation:
public int hash() {
return UjoManager.getInstanceManager().getHash(this);
}
All of these implementations are generally applicable to all the Ujo objects posterity.
Using the KeyFactory in interfaces
In some cases can be useful to define Ujo Keys in an interface. See the next simple example how to design the inteface using the class KeyFactory:
public interface MyUjoInterface extends Ujo {
public static final KeyFactory<MyUjoInterface> $factory
= KeyFactory.CamelBuilder.get(MyUjoInterface.class);
public static final Key<MyUjoInterface,Long> PRO_P0 = $factory.newKey();
public static final Key<MyUjoInterface,Integer> PRO_P1 = $factory.newKey();
public static final Key<MyUjoInterface,String> PRO_P2 = $factory.newKey();
public static final Key<MyUjoInterface,Date> PRO_P3 = $factory.newKey();
public static final ListKey<MyUjoInterface,Float> PRO_P4 = $factory.newListKey();
public static final int KEY_SIZE = $factory.lockAndSize();
}
The interface MyUjoInterface
can be a child of another Ujo interface optionally,
however an implementation of multiple interfaces is not recommended due to the risk of duplicate Key indexes.
The next example shows a sample of a real implementation:
public class MyUjoImpl implements MyUjoInterface {
private Object[] data = new Object[KEY_SIZE];
public KeyList<?> readKeys() {
return $factory.getKeys();
}
public Object readValue(Key property) {
return data[property.getIndex()];
}
public void writeValue(Key property, Object value) {
data[property.getIndex()] = value;
}
public boolean readAuthorization(UjoAction action, Key property, Object value) {
return true;
}
public UjoPropertyList readProperties() {
return new UjoPropertyListImpl(readKeys());
}
}
Note, that a method readProperties()
can be removed in a next Ujorm release.
Interface UjoExt
The most of information in this tutorial are related to a basic Ujo interface however since a version Ujorm 0.80 is available an extended interface
called UjoExt for an easier utilization in a source code. The extended features are described by a slide show format in a different document
or you can try an UjoExt JavaDoc description.
Default values
A JavaBean property can have got a default value by the sample:
{
private Double cash = 10.0;
}
Also the UJO property can have got a default value. All the implementations of method Ujo.readValue(...) from this frameworks
replaces an undefined value (NULL) by the default value from the Key.
{
public static final Key<Person, Double> CASH = newKey("Cash", 10.0);
}
Note, you can change a default key name also using the first text parameter.
The default property name have got
- the same value as its property field in the QuickUjo implementation
- key name created by the KeyFactory depemds on its KeyFactory implementations, you can choice a "cammelCase" key name name builder for example
Special features:
- the default value is available even if the original property value is changed
- if any property value is changed to a NULL then the property method returns the default value (the feature can be suppressd by overriding method: Ujo.readProperty)
- an implementation if the interface Key provides some methods to use the default value
How to create the JavaBean?
It´s easy to make the JavaBean from the UJO object. It´s enough to implement a setter and a getter for each attribute. An example of usage:
public class Person extends QuickUjo {
public static final Key<Person, String> NAME = newKey();
public static final Key<Person, Double> CASH = newKey();
public void setName(String name) {
NAME.setValue(this, name);
}
public String getName() {
return NAME.getValue(this);
}
public void setCash(Double cash) {
CASH.setValue(this, cash);
}
public Double getCash() {
return CASH.getValue(this);
}
}
There is possible to use an special implementation BeanUjo to an implementation UJO features to your complete JavaBeans.
This BeanUjo implementation calls JavaBean setters and getters by a Java reflection for writing and reading values (fields).
A name of Ujo property must accord with a related name of the JavaBean attribute (methods) in the implementation.
There is allowed to use a primitive types of the object fields if you can (e.g. Double -> double).
import org.ujorm.implementation.bean.*;
public class Person extends BeanUjo {
public static final BeanProperty<Person, String> NAME = newKey();
public static final BeanProperty<Person, Double> CASH = newKey();
private String myname;
private Double mycash;
public void setName(String name) {
this.myname = name;
}
public String getName() {
return myname;
}
public void setCash(Double cash) {
this.mycash = cash;
}
public Double getCash() {
return mycash;
}
}
Keep the mind that getters of implementation BeanUjo don't call a method Ujo.readProperty(..) similar like setters don't call Ujo.writeProperty().
For example this is the reason why method getCash() can't return a default value from Key for example.
The way NAME.getValue(...) works fine.
XML serialization
Usage of an UJO serialization is very simple, that is how the code looks like:
Person person = somePersonProvider().getPerson();
UjoManagerXML.getInstance().saveXML(writer, person, null, "My Export");
person = UjoManagerXML.getInstance().parseXML(inputStream, Person.class, "My Import");
A content of created XML is now:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<NANE>Pavel Ponec</NAME>
<MALE>true</MALE>
<CASH>34.5</CASH>
</body>
List property
It´s often very useful some attribute to contain a list of another UJO objects.
It´s possible to make the ArrayList<UjoItem> type object though, but preferable is to use for an attribute the ListProperty class.
The first reason is a clearer content of an export to XML, because an export doesn't contain the information about the data type items of the list.
I recommend to read up API for more detailed information.
Recommended solution for the implementation of a link 1:N for the Person - Child objects.
public class Person extends AbstractUjo {
public static final Key <Person,String> NAME = newKey();
public static final ListProperty<Person,Child> CHILDS = newListKey();
}
Hidden elements
What to do, when you need to export only some of the UJO object attributes and to ignore the rest?
There is the readAuthorization(...) method for this case.
This method can allow a participation of the chosen attributes in dependence on:
- the action type (XML export, Resource Bundle export, ...) include an action context
- the attribute
- his value
On the following example we will illustrate, how the suppression of the NAME attribute by an export into XML looks like:
public boolean readAuthorization(UjoAction action, Key property, Object value) {
switch(action.getType()) {
case ACTION_XML_EXPORT:
return property!=NAME;
default: {
return super.readAuthorization(action, property, value);
}
}
}
Notice: because all the Keys of the UJO object are the final type, it is not necessary
to compare by the equals() method, but it is possible to use a quicker operator ==.
The attributes
The Ujorm enables to write some keys like a XML attribute of the element contrary of the XML child element (it is a default feature).
The solution is to use an annotation @XmlAttribute by next sample:
@XmlAttribute
public static final Key<Ujo,String> NAME = newKey();
Body value
There is possible print the a one property value per object like an element body text.
For the purpose is designed an annotation @XmlElementBody by next sample:
@@XmlElementBody
public static final Key<Ujo,String> MALE = newKey();
A content of the new XML file using both annotations @XmlAttribute and @XmlElementBody is next:
<?xml version="1.0" encoding="UTF-8"?>
<body Name="Pavel Ponec">
true
<CASH>34.5</CASH>
</body>
Some more features:
- there is recommended that only one property was signed by the annotation XmlElementBody in the class
- if more XmlElementBody annotated keys are identified than the framework will be considered the valid property with the highest index
- if a property has an annotation XmlAttribute so the XmlElementBody is ignored.
Support of the JTable component
The Ujorm contains a support for the UJO object list displaying in the JTable. For simple editable table creating it´s not necessary
to create any new class, you can only create the UjoTableModel class instance and to set it to the JTable object using the setModel() method.
Example of simple usage:
UjoTableModel<Person> model = new UjoTableModel(Person.NAME, Person.MALE, Person.BIRTH);
jTable.setModel(model);
List<Person> persons = new ArrayList();
model.setRows(persons);
Other possibilities of the UjoTableModel class::
model.addRow(new Person());
model.setValueAt("Prokop", 0, Person.NAME );
Person person = model.getRowLast();
model.sort(Person.NAME);
model.sort(Person.NAME.descending());
Confrontation with the AbstractTableModel
For creating a new editable table data model you have to implement or overwrite a few methods of the AbstractTableModel abstract class.
- getColumnName(int column)
- getColumnClass(int column)
- getColumnCount()
- getRowCount()
- getValueAt(int rowIndex, int columnIndex)
- setValueAt(Object value, int rowIndex, int columnIndex)
All of these methods in the UjoTableModel class you´ll get implemented yet.
A name of the column and corresponding class are taken straightly from the Key object, a column list inputs in constructor.
Object-relation mapping (ORM)
The object-relation mapping (ORM) is a bridge between the object word
and the relation database. Some features:
- framework has a type safe query language which allows the java compiler find some syntax errors
- no confusing proxy business objects
- easy to configure the ORM model by java source code
- great performance
See a special page for more information.
Ujo inheritance
An inheritance of the UJO clasess are supported along the the next examples:
public class PersonExt extends Person {
private static final KeyFactory<Person> f = newFactory(PersonExt.class);
public static final Key<PersonExt,Float> WEIGHT = f.newKey();
public static final Key<PersonExt,Date > BORN = f.newKey();
@Override public KeyList<?> readKeys() {
return f.getKeys();
}
}
Or the similar example without the class KeyFactory for a child of the class QuickUjo:
public class PersonExt extends Person {
public static final Key<PersonExt,Float> WEIGHT = newKey();
public static final Key<PersonExt,Date > BORN = newKey();
}
The processing performance of the UJO objects is dependent on the implementation. During the processing of the AbstractUjo type object
is performance (writing/reading) approximately consistent with the operations writing/reading of the HashMap object.
The ArrayUjo solution provides the quicker implementation, the JavaBean object is the speediest.
There are the results of a simple mensuration in the following table.
Environment: Windows XP system, Intel DuoCore 1.66 MHz processor, 1GB RAM, JRE 6.0, Ujorm 0.80.
The test description:
- a number of the test iterations is 5 million
- for each iteration a new instance of the Ujo interface will be created
- there is written the 10 attribute values into the object
- all the values will be read
- the object will be released for a Garbage collector.
- the result values are an arithmetic average from four tests
- executing clasess: ArrayUjoTest, MapUjoTest, BeanUjoTest, PojoTest from JUnit tests
Implementation |
Performance [sec per 5M loops] |
Ratio [%] |
ArrayUjo |
1.972 |
177 |
MapUjo |
6.651 |
597 |
BeanUjo |
20.995 |
1883 |
JavaBean |
1.115 |
100 |
It´s evidently from the table, that the performance of the ArrayUjo implementation is high and it gets near to the JavaBeans object performance.
The results can be affected in some cases by costs of making an instance of a primitive values.
However the speed of JavaBeans dramatically drops near to BeanUjo value
in case a property access via Java reflection tools.
The XML serialization of UJO objects is based on a quick SAX parser, performance tests are very favorable,
mainly a comparison to XML serialization implemented in JER (classes XMLEncoder, XMLDecoder).
A measurement was provided on the same PC like the last test (UJO release 0.84), all results are shown in next table.
A test description:
- all business object are the same type (in scope one test)
- every one business object contains a11 attributes including the one for a recursion type of ArrayList
- count of total instances is 6.000 objects
- maximal deep is 4 object levels
- test was performed by a class XmlSpeedTest2 (second pass) from JUnit tests
- the result values are an arithmetic average from four tests
Implementation |
Time of serialization [sec] |
Time of deserialization [sec] |
Total ratio [%] |
ArrayUjo |
0.332 |
0.305 |
93 |
MapUjo |
0.340 |
0.340 |
99 |
BeanUjo |
0.375 |
0.336 |
104 |
XMLEncoder |
2.797 |
1.770 |
668 |
JAXB |
0.211 |
0.473 |
100 |
Conclusion: The XML serialization implemented in Ujorm is approximately six times faster
in comparison with the XMLEncoder persistence implemented in JRE 6.0.
The UJO speed of serialization is slightly smaller than the speed of the great JAXB framework,
however all UJO implementations got a the shortest time of deserialization.
Other possibilities of usage?
What are the other possibilities of usage?
Commons
- UjoCriteria allows to search an Ujo objects from the list of Ujo objects.
- PathProperty class is an composite of a Key objects.
The PathProperty class can be used wherever is used Key - with a one important exception:
do not send the PathProperty object to methods Ujo.readValue(...) and Ujo.writeValue(...).
- FactoryUjo is a generic object maker.
Persistence
- you can save all the hierarchy of UJO objects in XML format by the help of the UjoManagerXML class. For another information see the UjoXml class.
- UJO object can be saved in the PropertyResourceBundle format.
- UJO object will be possible to save in CSV format in some future version.
- The usage of UJO objects in the framework Cayenne (solution for the ORM).
The Cayenne works with persistent classes analogous to implementation MapUjo. Because the persistent objects are created by pattern,
the pattern can be change easily in that way, it will implement the Ujo interface.
Servlet
The UJO usage in servlets is not described in detail yet, but the basic idea for the text characteristics processing of the ServletRequest object is circa here:
ServletRequest request;
UjoTextable person;
Key personName;
...
person.writeValueString(personName, request.getParameters(personName.getName());
Referential Application
The jWorkSheet is a referential implementation of the framework.
This Swing project is powered by the Ujorm. It is published under an open license on a home page http://jworksheet.ponec.net/.
The jWorkSheet is an application for time tracking different projects.
You can study the usage of the UJO object persistence and its collaboration with a graphics component JTable on the source codes.
The jWorkSheet application characteristics::
- all the persistence is realized by the Ujorm. It is used the saving into XML format in project,
parameters are saved into the Resource Bundle format.
- all the data models of the JTable object are straight the instance or a child of the UjoTable class.
- the small size of jWorkSheet application is interesting, it takes up about 170 kB including the UJO library.
The jWorkSheet project is written in Java 5.0, the development was realized by the NetBeans IDE.
PekiLine - the desktop application manages your personal (English) vocabulary to learn.
- the application can get an translation word from a Google dictionary including a pronunciation in case you are on-line
- you can modify all items of vocabulary, sort columns or mark word to a print
- personal data are stored in a XML file located in the user home subdirectory
Where ?
The project home page: http://ujorm.org/
Referential implementation: http://jworksheet.ponec.net/.
Another implementations: (here can be referenced your project).
License
The code was released under the Apache License, Version 2.0.
Copyright 2007-2010 Pavel Ponec
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
FAQ
- Q: Is the list of the Keys objects ordered by a natural order?
- A: The result depends on the implementation. All keys are sorted by a property index by default.
All implementations from the Ujorm returns keys in a natural order on condition that the keys
where created by a factory method newKey(..). The ArrayUjo has assigned all indexes explicitly.
If the index numers have got the same value then the natural ordering is no guarantee by a Sun Microsystems specificaton.
- Q: What will be the retrieved UJO values, which were not initiated yet?
- A: The result depends on UJO object implementation. The default implementation (AbstractUjo) returns a default value from Key.
- Q: Are somewhere any other UJO usage?
- A: You find another examples in the jWorkSheet application and in the JUnit tests too.
- Q: Isn't the UJO work slow?
- A: Some implementations are very quick, see the chapter Performance.
See the Wiki tutorial for more information.
Release notes
- 2012/10 - introduction the interface Key with the factory KeyFactory
- 2011/10 - many small ORM improvements
- 2010/10 - the production version 1.00 released
- 2009/09 - the version 0.90 supports the ORM
- 2008/09 - the version 0.80 brings an extended interface - UjoExt
- 2007/10 - the first public version 0.70 is released under Apache License, Version 2.0
See a detail release notes in a text format for more information.