blob: 3913a9f91bd56a5e73d551e8059b892b872d13a1 [file] [log] [blame]
///////////////////////////////////////////////////////////////
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
///////////////////////////////////////////////////////////////
[[thirty-minutes-intro,Zest™ in 30 minutes]]
= Zest™ in 30 minutes =
TIP: Theses tutorials are based on actual code found in the `tutorials/` directory of the
https://zest.apache.org/download.html[Zest™ SDK sources]. You should start your favorite editor and find the code related to
this tutorial, run it and play with it.
This introduction will deepen your understanding of Zest™, as we touches on a couple of the common features of Zest™. It
is expected that you have gone through and understood the "Zest™ in 10 minutes" introduction.
If you want to reproduce what's explained in this tutorial, remember to depend on the Core Runtime artifact that depends
on Core API, Core SPI, Core Bootstrap and Core Functional & I/O APIs:
include::../../../../../core/runtime/build/docs/buildinfo/artifact.txt[]
Moreover, you'll need an EntityStore for persistence and an Indexing engine for querying. Choose among the available
implementations listed in the <<extensions>> section.
See the <<howto-depend-on-zest>> tutorial for details.
We will go back to the OrderEntity example;
[snippet,java]
-----------
source=tutorials/introduction/tenminutes/src/main/java/org/qi4j/demo/tenminute/OrderEntity.java
tag=mainClass
-----------
Let's say that this is an existing Composite, perhaps found in a library or used in a previous object, but we want to
add that it tracks all the changes to the order and the confirmation of such order.
First we need to create (or also find in a library) the mechanics of the audit trail. It could be something like this;
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=2
-----------
Quite a lot of Zest™ features are leveraged above;
[1] Property is a first class citizen in Zest™, instead of getters/setters naming convention to declare properties.
[2] ValueComposite for Action means that it is among other things Immutable.
[3] The Action extends a Property. We call that Property subtyping and highly recommended.
[4] The CompositeBuilder creates Immutable Action instances.
We also need a Concern to hang into the methods of the Order interface.
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=3
-----------
In this case, we have chosen to make an Order specific Concern for the more generic AuditTrail subsystem, and would
belong in the client (Order) code and not with the library (AuditTrail).
Pay attention to the @This annotation for a type that is not present in the Composite type interface. This is called a
private Mixin, meaning the Mixin is only reachable from Fragments within the same Composite instance.
But the AuditTrail subsystem could provide a Generic Concern, that operates on a naming pattern (for instance). In this
case, we would move the coding of the concern from the application developer to the library developer, again increasing
the re-use value. It could look like this;
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=4
-----------
The above construct is called a Generic Concern, since it implements java.lang.reflect.InvocationHandler instead of the
interface of the domain model. The ConcernOf baseclass will also need to be of InvocationHandler type, and the
Zest™ Runtime will handle the chaining between domain model style and this generic style of interceptor call chain.
Finally, we need to declare the Concern in the OrderEntity;
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=5
-----------
We also place it first, so that the AuditTrailConcern will be the first Concern in the interceptor chain
(a.k.a InvocationStack), so that in case any of the other Concerns throws an Exception, the AuditTrail is not updated
(In fact, the AuditTrail should perhaps be a SideEffect rather than a Concern. It is largely depending on how we define
SideEffect, since the side effect in this case is within the composite instance it is a border case.).
So let's move on to something more complicated. As we have mentioned, EntityComposite is automatically persisted to an
underlying store (provided the Runtime is setup with one at bootstrap initialization), but how do I locate an Order?
Glad you asked. It is done via the Query API. It is important to understand that Indexing and Query are separated from
the persistence concern of storage and retrieval. This enables many performance optimization opportunities as well as a
more flexible Indexing strategy. The other thing to understand is that the Query API is using the domain model, in Java,
and not some String based query language. We have made this choice to ensure refactoring safety. In rare cases, the
Query API is not capable enough, in which case Zest™ still provides the ability to look up and execute native queries.
Let's say that we want to find a particular Order from its SequenceNumber.
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=6
-----------
The important bits are;
* The QueryExpressions.templateFor() method is used to define the template used in the query upon execution. In
this case, we choose to template only the HasSequenceNumber, an interface used in OrderEntity, but is not part of
Order (may or may not be a good design choice).
* The where() clause, which has a statically imported method eq(), which builds up the expression logic, and will
be translated into the underlying query language upon execution, which happens in the iterator() method.
Another example,
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=7
-----------
In the above case, we find the Orders that has been created in the last 90 days, and add them to a report to be
generated. This example assumes that the Order type has a Property<Date> createdDate() method.
Now, Orders has a relation to the CustomerComposite which is also an Entity. Let's create a query for all customers
that has made an Order in the last 30 days;
[snippet,java]
-----------
source=tutorials/introduction/thirtyminutes/src/main/java/org/qi4j/demo/thirtyminutes/ThirtyMinutesDocs.java
tag=8
-----------
This covers the most basic Query capabilities and how to use it. For Querying to work, an Indexing subsystem must be
assembled during bootstrap. At the time of this writing, only an RDF indexing subsystem exist, and is added most easily
by assembly.addAssembler( new RdfNativeSesameStoreAssembler() ).
It can be a bit confusing to see Zest™ use Java itself as a Query language, but since we have practically killed the
classes and only operate with interfaces, it is possible to do a lot of seemingly magic stuff. Just keep in mind that
it is pure Java, albeit heavy use of dynamic proxies to capture the intent of the query.
== Conclusion ==
We have now explored a couple more intricate features of Zest™, hopefully without being overwhelmed with details on how
to create applications from scratch, how to structure applications, and how the entire Zest™ Extension system works.
We have looked at how to add a Concern that uses a private Mixin, we have touched a bit on Generic Concerns, and
finally a short introduction to the Query API.