/
thirty-minutes-intro.html
292 lines (245 loc) · 17.7 KB
/
thirty-minutes-intro.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Qi4j in 30 minutes</title><link rel="stylesheet" href="css/style.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.75.2" /><link rel="home" href="index.html" title="" /><link rel="up" href="tutorials.html" title="Tutorials" /><link rel="prev" href="ten-minutes-intro.html" title="Qi4j in 10 minutes" /><link rel="next" href="two-hours-intro.html" title="Qi4j in 2 hours" />
<!-- favicon -->
<link rel="shortcut icon" href="http://qi4j.org/favicon.ico" type="image/vnd.microsoft.icon" />
<link rel="icon" href="http://qi4j.org/favicon.ico" type="image/x-icon" />
<!-- style -->
<link href="css/shCore.css" rel="stylesheet" type="text/css" />
<link href="css/shCoreEclipse.css" rel="stylesheet" type="text/css" />
<link href="css/shThemeEclipse.css" rel="stylesheet" type="text/css" />
<link href="css/qi4j.css" rel="stylesheet" type="text/css" />
<!-- Syntax Highlighter -->
<script type="text/javascript" src="js/shCore.js"></script>
<script type="text/javascript" src="js/shBrushJava.js"></script>
<script type="text/javascript" src="js/shBrushScala.js"></script>
<script type="text/javascript" src="js/shBrushJScript.js"></script>
<script type="text/javascript" src="js/shBrushBash.js"></script>
<script type="text/javascript" src="js/shBrushPlain.js"></script>
<script type="text/javascript" src="js/shBrushXml.js"></script>
<script type="text/javascript" src="js/shBrushGroovy.js"></script>
<script type="text/javascript" src="js/shBrushPython.js"></script>
<script type="text/javascript" src="js/shBrushRuby.js"></script>
<script type="text/javascript" src="js/shBrushCSharp.js"></script>
<script type="text/javascript">
SyntaxHighlighter.defaults['tab-size'] = 4;
SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all()
</script>
<!-- JQuery -->
<script type="text/javascript" src="js/jquery-1.6.4.min.js"></script>
<!-- Image Scaler -->
<script type="text/javascript" src="js/imagescaler.js"></script>
<!-- Table Styler -->
<script type="text/javascript" src="js/tablestyler.js"></script>
<!-- Version Switcher -->
<script type="text/javascript" src="js/versionswitcher.js"></script>
<!-- Qi4j WebSite Progressive Enhancement -->
<link href="css/progressive-enhancement.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="js/jquery.scrollTo-1.4.2.js"></script>
<script type="text/javascript" src="js/progressive-enhancement.js"></script>
<!-- Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-3118496-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head><body><div xmlns="" xmlns:exsl="http://exslt.org/common" class="logo"><a href="index.html"><img src="images/logo-standard.png" /></a></div><div xmlns="" xmlns:exsl="http://exslt.org/common" class="top-nav"><div xmlns="http://www.w3.org/1999/xhtml" class="toc"><dl><dt><span class="section"><a href="index.html#home">Qi4j</a></span></dt><dt><span class="section"><a href="intro.html">Introduction</a></span></dt><dt><span class="section"><span xmlns="" href="tutorials.html">Tutorials</span></span></dt><dt><span class="section"><a href="javadocs.html">Javadoc</a></span></dt><dt><span class="section"><a href="samples.html">Samples</a></span></dt><dt><span class="section"><a href="core.html">Core</a></span></dt><dt><span class="section"><a href="libraries.html">Libraries</a></span></dt><dt><span class="section"><a href="extensions.html">Extensions</a></span></dt><dt><span class="section"><a href="tools.html">Tools</a></span></dt><dt><span class="section"><a href="glossary.html">Glossary </a></span></dt></dl></div></div><div xmlns="" xmlns:exsl="http://exslt.org/common" class="sub-nav"><div xmlns="http://www.w3.org/1999/xhtml" class="toc"><dl><dt><span class="section"><a href="tutorials.html#_overview">Overview</a></span></dt><dt><span class="section"><a href="two-minutes-intro.html">Qi4j in 2 minutes</a></span></dt><dt><span class="section"><a href="ten-minutes-intro.html">Qi4j in 10 minutes</a></span></dt><dt><span class="section"><span xmlns="" href="thirty-minutes-intro.html">Qi4j in 30 minutes</span></span></dt><dt><span class="section"><a href="two-hours-intro.html">Qi4j in 2 hours</a></span></dt><dt><span class="section"><a href="howto-depend-on-qi4j.html">Depend on Qi4j in your build</a></span></dt><dt><span class="section"><a href="howto-assemble-application.html">Assemble an Application</a></span></dt><dt><span class="section"><a href="tut-composites.html">Transient Composites Tutorial</a></span></dt><dt><span class="section"><a href="tut-services.html">Services Composites Tutorial</a></span></dt><dt><span class="section"><a href="howto-contextual-fragments.html">Use contextual fragments</a></span></dt><dt><span class="section"><a href="howto-leverage-properties.html">Leverage Properties</a></span></dt><dt><span class="section"><a href="howto-create-constraint.html">Create a Constraint</a></span></dt><dt><span class="section"><a href="howto-create-concern.html">Create a Concern</a></span></dt><dt><span class="section"><a href="howto-create-sideeffect.html">Create a SideEffect</a></span></dt><dt><span class="section"><a href="howto-create-entity.html">Create an Entity</a></span></dt><dt><span class="section"><a href="howto-configure-service.html">Configure a Service</a></span></dt><dt><span class="section"><a href="howto-use-io.html">Use I/O API</a></span></dt><dt><span class="section"><a href="build-system.html">Build System</a></span></dt><dt><span class="section"><a href="community-docs.html">Writing Qi4j Documentation</a></span></dt></dl></div></div><div class="section" title="Qi4j in 30 minutes"><div class="titlepage"><div><div><h3 class="title"><a id="thirty-minutes-intro"></a>Qi4j in 30 minutes</h3></div></div></div><div class="tip" title="Tip" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Tip</h3><p>Theses tutorials are based on actual code found in the <code class="literal">tutorials/</code> directory of the
<a class="ulink" href="http://qi4j.org/downloads.html" target="_top">Qi4j SDK sources</a>. You should start your favorite editor and find the code related to
this tutorial, run it and play with it.</p></div><p>This introduction will deepen your understanding of Qi4j, as we touches on a couple of the common features of Qi4j. It
is expected that you have gone through and understood the "Qi4j in 10 minutes" introduction.</p><p>We will go back to the OrderEntity example;</p><pre class="programlisting brush: java">@Concerns({PurchaseLimitConcern.class, InventoryConcern.class})
public interface OrderEntity
extends Order, HasSequenceNumber, HasCustomer,
HasLineItems, Confirmable, EntityComposite
{
}</pre><p>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.</p><p>First we need to create (or also find in a library) the mechanics of the audit trail. It could be something like this;</p><pre class="programlisting brush: java">public interface HasAuditTrail<M>
{
AuditTrail<M> auditTrail();
}
public interface AuditTrail<M> extends Property<List<Action<M>>>
{}
public interface Action<T> extends ValueComposite [2][3]
{
enum Type added, removed, completed;
Property<T> item(); [1]
Property<Type> action(); [1]
}
public interface Trailable<M>
{
void itemAdded( M item );
void itemRemoved( M item );
void completed();
}
public class TrailableMixin<M>
implements Trailable<M>
{
private @This HasAuditTrail hasTrail;
@Override
public void itemAdded( M item )
{
addAction( item, Action.Type.added );
}
@Override
public void itemRemoved( M item )
{
addAction( item, Action.Type.removed );
}
@Override
public void completed( M item )
{
addAction( item, Action.Type.completed );
}
private Action<M> addAction( M item, Action.Type type )
{
CompositeBuilder<Action> builder =
factory.newCompositeBuilder( Action.class ); [4]
Action prototype = builder.stateFor( Action.class );
prototype.item().set( item );
prototype.action().set( action );
Action instance = builder.newInstance();
hasTrail.auditTrail().get().add( instance );
}
}</pre><p>Quite a lot of Qi4j features are leveraged above;
[1] Property is a first class citizen in Qi4j, 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.</p><p>We also need a Concern to hang into the methods of the Order interface.</p><pre class="programlisting brush: java">public abstract class OrderAuditTrailConcern
extends ConcernOf<Order>
implements Order
{
@This Trailable<LineItem> trail;
@Override
public void addLineItem( LineItem item )
{
next.addLineItem( item );
trail.itemAdded( item );
}
@Override
public void removeLineItem( LineItem item )
{
next.removeLineItem( item );
trail.itemRemoved( item );
}
@Override
public void completed()
{
next.completed();
trail.completed();
}
}</pre><p>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.</p><p>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;</p><pre class="programlisting brush: java">public class AuditTrailConcern
extends ConcernFor<InvocationHandler>
implements InvocationHandler
{
@This Trailable trail;
@Override
public void invoke( Object proxy, Method m, Object[] args )
throws Exception
{
next.invoke( proxy, m, args );
String methodName = m.getName();
if( methodName.startsWith( "add" ) )
{
trail.itemAdded( args[0] );
}
else if( methodName.startsWith( "remove" ) )
{
trail.itemRemoved( args[0] );
}
else if( methodName.startsWith( "complete" ) ||
methodName.startsWith( "commit" ) )
{
trail.completed();
}
}
}</pre><p>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
Qi4j Runtime will handle the chaining between domain model style and this generic style of interceptor call chain.</p><p>Finally, we need to declare the Concern in the OrderEntity;</p><pre class="programlisting brush: java">@Concerns({
AuditTrailConcern.class,
PurchaseLimitConcern.class,
InventoryConcern.class
})
@Mixins( TrailableMixin.class )
public interface OrderEntity
extends Order, Confirmable,
HasSequenceNumber, HasCustomer, HasLineItems,
EntityComposite
{
}</pre><p>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.).</p><p>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?</p><p>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 Qi4j still provides the ability to look up and execute native queries.</p><p>Let’s say that we want to find a particular Order from its SequenceNumber.</p><pre class="programlisting brush: java">import static org.qi4j.api.query.QueryExpressions.eq;
import static org.qi4j.api.query.QueryExpressions.templateFor;
import org.qi4j.api.query.QueryBuilder;
import org.qi4j.api.query.QueryBuilderFactory;
:
@Structure private UnitOfWorkFactory uowFactory; //Injected
:
UnitOfWork uow = uowFactory.currentUnitOfWork();
QueryBuilderFactory qbf = uow.queryBuilderFactory();
QueryBuilder<Order> builder = qbf.newQueryBuilder( Order.class );
String orderNumber = "12345";
HasSequenceNumber template = templateFor( HasSequenceNumber.class );
builder.where( eq( template.number(), orderNumber ) );
Query<Order> query = builder.newQuery();
Iterator<Order> result = query.iterator();
if( result.hasNext() )
{
Order order = result.next();
}
else
{
// Deal with it wasn't found.
}</pre><p>The important bits are;</p><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem">
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).
</li><li class="listitem">
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.
</li></ul></div><p>Another example,</p><pre class="programlisting brush: java">QueryBuilder<Order> builder = qbf.newQueryBuilder( Order.class );
Calendar cal = Calendar.getInstance();
cal.setTime( new Date() );
Date last90days = cal.roll( Calendar.DAY_OF_MONTH, -90 );
Order template = templateFor( Order.class );
builder.where( gt( template.createdDate(), last90days ) );
Query<Order> query = builder.newQuery();
for( Order order : query )
{
report.addOrderToReport( order );
}</pre><p>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.</p><p>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;</p><pre class="programlisting brush: java">QueryBuilder<HasCustomer> builder = qbf.newQueryBuilder( HasCustomer.class );
Calendar cal = Calendar.getInstance();
cal.setTime( new Date() );
Date lastMonth = cal.roll( Calendar.MONTH, -1 );
Order template1 = templateFor( Order.class );
builder.where( gt( template1.createdDate(), lastMonth ) );
Query<HasCustomer> query = builder.newQuery();
for( HasCustomer hasCustomer : query )
{
report.addCustomerToReport( hasCustomer.customer().get() );
}</pre><p>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() ).</p><p>It can be a bit confusing to see Qi4j 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.</p><div class="section" title="Conclusion"><div class="titlepage"><div><div><h4 class="title"><a id="_conclusion_2"></a>Conclusion</h4></div></div></div><p>We have now explored a couple more intricate features of Qi4j, hopefully without being overwhelmed with details on how
to create applications from scratch, how to structure applications, and how the entire Qi4j 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.</p></div></div><div xmlns="" xmlns:exsl="http://exslt.org/common" class="footer">(c) 2012 The Qi4j Community</div></body></html>