blob: 7f06ec7186603922820931487a048994127ca399 [file] [log] [blame]
//////////////////////
* Copyright (c) 2007-2012, Niclas Hedhman. All Rights Reserved.
* Copyright (c) 2013, Paul Merlin. All Rights Reserved.
*
* 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.
//////////////////////
[[core-api-value,ValueComposite]]
= ValueComposite =
Usage of value objects is one of the most ignored and best return-on-investment the programmer can do. Values are
immutable and can be compared by value instead of memory reference. Concurrency is suddenly not an issue, since either
the value exists or it doesn't, no need for synchronization. Values are typically very easy to test and very robust to
refactoring.
Zestâ„¢ defines values as a primary meta type through the ValueComposite, as we think the benefits of values are great.
The ValueComposite is very light-weight compared to the EntityComposite, and its value can still be persisted as part
of an EntityComposite via a Property.
The characteristics of a ValueComposite compared to other Composite meta types are;
* It is Immutable.
* Its equals/hashCode works on both the descriptor and the values of the ValueComposite.
* Can be used as Property types.
* Can be serialized and deserialized.
== Value Serialization ==
Value objects can be serialized and deserialized using the ValueSerialization API which is a Service API implemented
by SPI and extensions.
TIP: +ValueSerialization extends ValueSerializer, ValueDeserializer+. See the <<javadocs>> for interfaces detail.
The ValueSerialization mechanism apply to the following object types :
* ValueComposite,
* EntityReference,
* Iterable,
* Map,
* Plain Value.
Nested Plain Values, EntityReferences, Iterables, Maps, ValueComposites are supported.
EntityComposites and EntityReferences are serialized as their identity string.
Plain Values can be one of :
* String,
* Character or char,
* Boolean or boolean,
* Integer or int,
* Long or long,
* Short or short,
* Byte or byte,
* Float or float,
* Double or double,
* BigInteger,
* BigDecimal,
* Date,
* DateTime (JodaTime),
* LocalDateTime (JodaTime),
* LocalDate (JodaTime).
TIP: Serialization behaviour can be tuned with options.
Every +ValueSerializer+ methods can take a +ValueSerializer.Options+ object that contains flags to change how some
values are serialized. See the <<javadocs>> for more details.
Values of unknown types and all arrays are considered as +java.io.Serializable+ and by so are (de)serialized to (from)
base64 encoded bytes using pure Java serialization. If it happens that the value is not Serializable or the input to
deserialize is invalid, a +ValueSerializationException+ is thrown.
Methods of +ValueSerializer+ allow to specify if the serialized state should contain extra type information about the
serialized value. Having type information in the serialized payload allows to keep actual ValueComposite types and by so
circumvent +AmbiguousTypeException+ when deserializing.
Core Runtime provides a default ValueSerialization system based on the
https://github.com/douglascrockford/JSON-java[org.json] Java library producing and consuming JSON.
Let's see how it works in practice.
[snippet,java]
----
source=core/api/src/test/java/org/qi4j/api/value/DocumentationSupport.java
tag=default
----
Reading this first example step by step we ;
. declare a ValueComposite,
. assemble it,
. create a new Value instance,
. use the +ValueComposite#toString()+ method to get a JSON representation of the Value,
. and finally, use the +Module#newValueFromSerializedState()+ method to create a new Value instance from the JSON
state.
+ValueComposite#toString()+ method leverage Value Serialization and by so provide JSON based representation. The Module
API allows to create new Value instances from serialized state.
On top of that, Application assemblies can register different implementation of ValueSerialization as Services to
support more formats, see the <<extensions>> section. Note that the default behaviour described above is overriden if a
ValueSerialization Service is visible.
Let's see how to use the ValueSerialization Services.
[snippet,java]
----
source=core/api/src/test/java/org/qi4j/api/value/DocumentationSupport.java
tag=service
----
In this second example, we ;
. declare a ValueComposite,
. assemble it,
. assemble a ValueSerialization Service backed by the +org.json+ package,
. get the +ValueSerializer+ and +ValueDeserializer+ Services injected,
. create a new Value instance,
. use the +ValueSerializer#serialize()+ method to get a JSON representation of the Value,
. and finally, use the +ValueDeserializer#eserialize()+ method to create a new Value instance from the JSON state.
Many applications need to stream data. The ValueSerialization API support such use cases in two ways.
The first one use classic streams.
[snippet,java]
----
source=core/api/src/test/java/org/qi4j/api/value/DocumentationSupport.java
tag=stream
----
. get a handle on a source of values and an +OutputStream+,
. serialize data into the +OutputStream+,
. get a handle on an +InputStream+,
. deserialize data from the +InputStream+.
The second one use the <<core-io>>:
[snippet,java]
----
source=core/api/src/test/java/org/qi4j/api/value/DocumentationSupport.java
tag=io
----
. get a handle on a source of values and a +Writer+,
. prepare the serialization +Function+,
. serialize a collection of values, one serialized value per line,
. get a handle on a serialized values +Reader+ and create a new empty +List+ of values,
. prepare the deserialization +Function+,
. deserialize a collection of values from read lines.