Return-Path: X-Original-To: apmail-zest-dev-archive@minotaur.apache.org Delivered-To: apmail-zest-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 554CA18477 for ; Sat, 1 Aug 2015 03:52:41 +0000 (UTC) Received: (qmail 69012 invoked by uid 500); 1 Aug 2015 03:52:41 -0000 Delivered-To: apmail-zest-dev-archive@zest.apache.org Received: (qmail 68976 invoked by uid 500); 1 Aug 2015 03:52:41 -0000 Mailing-List: contact dev-help@zest.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zest.apache.org Delivered-To: mailing list dev@zest.apache.org Received: (qmail 68964 invoked by uid 99); 1 Aug 2015 03:52:41 -0000 Received: from mail-relay.apache.org (HELO mail-relay.apache.org) (140.211.11.15) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 01 Aug 2015 03:52:41 +0000 Received: from mail-la0-f43.google.com (mail-la0-f43.google.com [209.85.215.43]) by mail-relay.apache.org (ASF Mail Server at mail-relay.apache.org) with ESMTPSA id 873651A022D for ; Sat, 1 Aug 2015 03:52:40 +0000 (UTC) Received: by labiq1 with SMTP id iq1so20141060lab.3 for ; Fri, 31 Jul 2015 20:52:39 -0700 (PDT) X-Received: by 10.152.206.41 with SMTP id ll9mr6681887lac.103.1438401159138; Fri, 31 Jul 2015 20:52:39 -0700 (PDT) MIME-Version: 1.0 Received: by 10.25.148.84 with HTTP; Fri, 31 Jul 2015 20:52:19 -0700 (PDT) In-Reply-To: References: From: Niclas Hedhman Date: Sat, 1 Aug 2015 11:52:19 +0800 Message-ID: Subject: Re: [jira] [Commented] (ZEST-108) Package renaming to org.apache.zest To: Roman Shaposhnik , dev Content-Type: multipart/alternative; boundary=001a1133af6a229952051c37de96 --001a1133af6a229952051c37de96 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Roman, (I am taking the liberty of posting this on-list, in case someone else might find it useful, such as Jeff and James) Thanks... I have learned one thing when speaking about Qi4j in the past, and that is to skip a lot of the theory, because there is soooo much of it, and it simply confuses people. Instead, showing code and what it does, seems to better blow people away. The =F0=9D=9B=95 of Zest is the 'control of the method invocation', i.e. As= pects. The difference with AOP a la AspectJ, is that Zest can do it much more type-safe, and it is ON by default, and then we have added all kinds of things around it, like; * Composition of fragments into Composites (enhance decoupling) * Enforcement of layered applications (enforce cohesion, avoid spaghetti= ) * Constraint validation on method arguments (reduction of defensive programming) * Wrap Concerns (around advice) around the method invocation (some behavior, cross-cutting concerns, etc) * 'null' not allowed by default (reduction of defensive programming) * Automatic State handling (Property methods don't need an implementation) * UnitOfWork to define boundaries (persistence, indexing/query, soon messaging, events) Example; public interface Order { void addItem( String inventoryId, int quantity, Currency price ); } So, for instance, I want to make sure that the quantity is a positive value= ; void addItem( String inventoryId, *@Positive* int quantity, Currency price ); where the @Positive annotation is linked to a Constrain implementation, i.e. totally custom. @ConstraintDeclaration @Retention( RetentionPolicy.RUNTIME ) @Constraints( PositiveConstraint.class ) public @interface Positive {} public class PositiveConstraint implements Constraint { @Override public boolean isValid( Positive range, Number argument ) { return argument.doubleValue() >=3D 0; } } The implementation of addItem then doesn't need to check for an illegal quantity. We would need to store the OrderItem somewhere. Let's say that an OrderItem is another entity, and that we want to keep the Order state private, i.e. can't be accessed directly from the outside world; public interface Order { void addItem( String inventoryId, int quantity, Currency price ); interface State { Property totalPrice(); @UseDefaults ManyAssociation items(); } } And then we can implement the addItem() method, and let's choose to put it in its own implementation class; public abstract class AddItemImpl implements Order { @This private Order.State state; // injected reference to the State above. public void addItem( String inventoryId, int quantity, Currency price ) { OrderItem item =3D createItem( inventoryId, quantity, price ); state.items().add( item ); state .totalPrince().set( state.totalPrice().get().add( price.multiply( quantity ) ) ) ); } } The @This annotation knows how to reach the internal interface State, and without reflection anything outside the Composite can't get to it. Even with reflection, it is pretty hard to reach it. But all of the above is persistence stuff, and it must be executing within a UnitOfWork. But we have ready-made Concern for handling that for you; public interface Order { @UnitOfWorkPropagation void addItem( String inventoryId, int quantity, Currency price ); } The default @UnitOfWorkPropagation will start a UnitOfWork if none is already executing, and will complete it if it started it, and discard it if an exception occurred. We also have a @UnitOfWorkRetry so the entire method call will be retried the number of times declared if a ConcurrentEntityModificationException occurs. @UnitOfWorkPropagation @UnitOfWorkRetry( retries=3D3, initialDelay=3D5, delayFactor=3D20 ) void addItem( String inventoryId, int quantity, Currency price ); I think this covers 10% of the =F0=9D=9B=95 basics, and should wet the appe= tite quite a bit, bit let's look at one more advanced example; public abstract class MyPutMethod implements java.util.Map { public V put( K key, V value ) { // do my magic } } During assembly of the application, I can create a new Map composite type, like this; module.transients( Map.class ).withMixins( MyPutMethod.class, HashMap.class ); This means that the put() method will be taken from MyPutMethod implementation, but all other methods will be from the HashMap. And more importantly, when the HashMap is calling its own put() method, it will actually call MyPutMethod.put(). However, it is not super generic, so one must call the factory methods to create all these composites. @Structure private TransientBuilderFactory tbf; Map m =3D tbd.newTransient( Map.class ); At the end of the day, Zest allows loads of boilerplate to be removed, it has advanced reflection capabilities, thanks to a strong accessible composite model that is available to users and by using convention over configuration, one can reuse much more code than conventional programming. Hope that helps Niclas On Sat, Aug 1, 2015 at 10:20 AM, Roman Shaposhnik wrote: > On Thu, Jul 30, 2015 at 8:28 PM, Niclas Hedhman wrote= : > > > > It is probably the 4 largest commits in our history. Idea wasn't capabl= e > of > > doing the refactoring of package names (only has 768M heap) in one go. > But > > that was the easy part... Changing the 3000 or so references to "qi4j" = in > > class names, variable names, method names and documentation, was the ha= rd > > part... > > Great story! And I really do think it belongs to your ApacheCON preso. > > Thanks, > Roman. > > P.S. Speaking of ApacheCON: I'm really looking forward to you presentatio= n > I've been curious about Zest ever since you proposed its move into ASF, b= ut > I still can't quite grasp the Tao of it so to speak. > --=20 Niclas Hedhman, Software Developer http://zest.apache.org - New Energy for Java --001a1133af6a229952051c37de96--