Dolphin Guide

39
1 Dolphin - where Enterprise Java meets Desktop Java Dolphin - Reference Documentation Authors: The Dolphin team Version: PRE-1.0 Table of contents

Transcript of Dolphin Guide

Page 1: Dolphin Guide

1

Dolphin - where Enterprise Java meets Desktop Java

Dolphin - Reference DocumentationAuthors: The Dolphin team

Version: PRE-1.0

Table of contents

Page 2: Dolphin Guide

2

Table of Contents

1 Introduction

1.1 Latest Changes

1.2 Credits

2 Architecture

2.1 The OpenDolphin structure

2.2 The concept of presentation models

2.3 The purpose of attributes

2.4 Stable bindings

2.5 Collections of presentation models and the model store

2.6 Understanding the client-server split and threading model

2.7 Communicating via Commands

2.8 Relying on the command sequence

2.9 Discussion

3 How to get started with OpenDolphin

3.1 Adding nodes to a stage, registering an onAction handler

3.2 Introducing a presentation model with one attribute and bind the value

3.3 Logical separation between client and server

3.4 Bind the "dirty" of presentation models to the view

3.5 Split into modules/projects

3.6 Enhanced view, let the "director" wire all application actions

3.7 Remote setup

4 Use Cases and Demos

4.1 The general approach of OpenDolphin with a Login dialog

4.2 Immediate and deferred binding

4.3 The container yard monitoring application

5 tbd Configuration and Setup

5.1 Standalone in-memory usage

5.2 Remote setup

6 tbd Developer Zone

6.1 How to build

Page 3: Dolphin Guide

3

1 Introduction initiated the Dolphin project since we had recognized that many of our customersCanoo

share a common quest:Keeping the on the but it with all the capabilities of the application logic server presenting

device.client

Dolphin is a remoting solution that bridges the world of Enterprise Java and Desktop Java.Unlike most REST approaches, it doesn't confine the server to be a data source only.

Instead, with Dolphin your server-side business logic controls a shared presentation model.The client displays the Dolphin state in all its beauty.

The figure below shows how client and server connect via Dolphin's shared presentationmodel followed by some introductory slides on the topic.

In terms of Model-View-Controller (MVC) one can see in the figure above that Dolphin putsthe responsibility on the client but leaves the logic on the server. The View controlling model(here in the sense of a model, not a domain model) is shared.presentation

This is shown in even more detail below.

Page 4: Dolphin Guide

4

Page 5: Dolphin Guide

5

Throughout this documentation, we will refer to this introduction and especially the section on expands on the value of clean structures.architecture

1.1 Latest Changes

Release 0.8

25 issues have been addressed in this release where the most important ones come from ourusers that already have OpenDolphin-based applications in production. This ranges fromperformance-oriented topics like the support for client-side models, command batching andcompressing down to operational support to e.g. detect unwanted breaches of the sessionaffinity.

The full list of closed issues is at the project JIRA

Release 0.7

There are no changes in this release. It has only been built to set the maven group id to to comply with the conventions on maven central.org.open-dolphin

The project artefacts are now available for download from MavenCentral

Please refer to for an example on how to use OpenDolphin with eitherDolphinJumpStartMaven or Gradle.

Release 0.6 : Notable Changes since 0.5

The 0.6 release has only one change, which is a rather big and breaking one:

The package com.canoo.dolphin has been renamed to org.opendolphin.

Page 6: Dolphin Guide

6

See also DOL-34

Release 0.5 : Notable Changes since 0.4

The project artefacts are now available for download from MavenCentral

Release 0.5 introduced support for asynchronous server-side events, which are distributedthrough an event bus. Typical use cases for this event bus are:

instant visualization of server side events (think JMS or hibernate events)

consistent visualization of transient data across many clients

cross-client notifications

Along with this capability comes a series of demos, which live directly in the OpenDolphincode base. Some of these demos also have a video presentation on :YouTube

ManyEventsDemo where 10'000 alarm events happen on the server and 6 clients updateinstantly and consistently

TrainControlDemo where a dash board changes the train speed and other clients arenotified

LazyLoadingDemo with 100'000 rows in a table

PortfolioDemo where the management of a portfolio serves as a typical example of abusiness application

Version 0.5 upgraded dependencies to Groovy 2.1.1. and GPars 1.0. We make use of thenew @CompileStatic where appropriate and enhanced execution speed by 60%.

Release Notes - Dolphin - Version 0.5

Release 0.4 : Notable Changes since 0.3

Page 7: Dolphin Guide

7

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

1.

2.

3.

4.

5.

DOL-25 - provide a demo that measures dolphin response times

DOL-33 - allow more meta-infos per Attribute

DOL-19 - Provide a code example that shows how to use dolphin for lazy loading

DOL-21 - Build does not automatically discover javafx in JDK when JAVAFX_HOME isset

DOL-37 - align server and client side API for CRUD operations on presentation modelsand attributes

DOL-38 - nicer API for server side value change

DOL-39 - API enhancement: server actions should work transparently on the response

DOL-40 - API enhancement: allow finding presentation models and attributes without theneed for casting in both Java and Groovy friendly manner

DOL-41 - start a user guide

DOL-42 - provide combined apidoc: javadoc / groovydoc

DOL-43 - Provide a DataCommand

DOL-44 - Provide a more complex demo (dependencies, crud operations, remoting)

DOL-17 - provide a 'gradlew run' to start any demo

DOL-18 - link the website to the latest version of the documentation

DOL-32 - remove the "linking" feature

Release Notes - Dolphin - Version 0.4

Breaking Changes

The "linking" feature is no longer available. See e.g. the Crud demo for alternatives.

Deprecations

In the future, we will disallow direct access to the connector or model store.

Please prepare by using the dolphin ( and ) facadeClientDolphin ServerDolphinwhenever possible. This also true for all the cases where application code on the server sidedirectly invokes constructors of commands, presentation models or attributes. Please usefactory methods instead.

Solved Bugs

DOL-15 - Basic build fails

DOL-22 - exception in reference table demo and search demo

DOL-26 - Binding the text property of a Swing component to a ClientPresentationModelthrows Exception

DOL-31 - ServerDolphin.createPresentationModel has an "optional" parameter, which isrequired

DOL-35 - com.canoo.dolphin.demo.startCreatePresentationModelDemo.groovy still triesto invoke serverDolphin.createPresentationModel

Page 8: Dolphin Guide

8

1.2 CreditsA big goes to all our early-adopting users that have provided us with muchthank youappreciated feedback, inspiration, and other support, particularly

Oracle

Navis

Knappschaft Bahn-See

Siemens

Inventage

and other companies that prefer to remain unnamed.

Page 9: Dolphin Guide

9

1.

2.

1.

2.

3.

2 ArchitectureThis user guide first explains the general approach of OpenDolphin and its architecture beforegoing into details and examples.

Reading through the user guide in this sequence is for those who like to first understandthings on a general level.

Others prefer to start with concrete demos and use cases, which is just as legitimate. In thiscase, you are welcome to skip the architecture section and directly jump to the .demos

The Dolphin architecture is made from two parts:

the structure

concepts and rules

2.1 The OpenDolphin structure

Modules define the vertical structure

Dolphin consists of three parts:

the shared module

the client module

the server module

Both, client and server depend on the shared module.

There is an additional fourth module called "combined", which depends on all three modulesabove and allows combining them in one single Java virtual machine for automatic testing,debugging, profiling and demo purposes.

All modules live in their own subproject with a separate source tree and the Gradle buildsystem is used to build them according to their dependency structure.

Each project that uses Dolphin will most likely resemble this structure where the sharedmodule may be empty (in case they is no shared knowledge between client and server).

The Dolphin team has produced a that suggests a best-practice projectJumpStartProjectsetup for Dolphin-enabled applications, either with

a Maven build or

a Gradle build.

Dolphin itself comes with a substantial list of demos in the subproject "demo-javafx". It ishighly recommended to have a look at the demos in order to understand how we expectDolphin to be used.

The horizontal layering

Page 10: Dolphin Guide

10

Orthogonally to the vertical structure of modules, there is a simple horizontal layering thatspans across all modules. In consists of:

the common infrastructure for build automation, configurations, etc.

the communication layer that provides the basic implementation

the facade layer that provides the API

the demo layer that shows all Dolphin features in small examples

The layering is visible through the respective package structure.

The application programmer should only use the facade layer.

The figure below summarizes the vertical and horizontal structuring.

While the structural part of the Dolphin architecture is rather obvious and straight-forward, theconcepts and rules that stand behind its inception require a bit of explanation, which is thetopic of the next section.

2.2 The concept of presentation modelsPresentation models are probably best known because of the presentation model design

by Martin Fowler.pattern

They also come under different names like or .Application Model View Model

There is a lot of information about presentation models on the web, e.g.

Page 11: Dolphin Guide

11

from Microsoft Developer Network

from Canoo

from JGoodies

Dolphin doesn't claim to have "the best" implementation of this pattern in any sense of theword.

In fact, we do not even claim to implement the "presentation model design" at all! pattern

We would like to avoid any discussion about whether we meet all requirements of any pattern or structure as proposed by any authority. We

are happy to give them all due credit, though, since we are building on theirwork.

It just happens that the Dolphin approach has a lot of similarities with the patterns above,which led us to calling our abstraction a presentation model.

What is common

Dolphin presentation models capture should be visualized (as opposed to ).what how

They are different from domain models.

They allow multiple consistent views on the same information.

They know neither views nor controllers (only their listeners).

They are independent of any GUI toolkit or widget set.

They make replacing the GUI toolkit easier.

They decouple business logic from view logic.

They make UI code easily testable.

They consist of s (like many but not all other implementations).Attribute

They support declarative binding between attributes and GUI elements.

What is special

Page 12: Dolphin Guide

12

Dolphin presentation models are . You never implement a new class for a newgenerictype of presentation model.

They do not contain any custom behavior (only what's provided by Dolphin for bindingand remoting).

They allow .stable bindings

They are Their attributes contain no object references, only primitive values. There isflat.no object graph.

In particular, they do not maintain an object reference to a domain model (but maybe akey).

They are between a client and a server. Dolphin cares for consistentsharedsynchronization.

They live in a managed object space. All instances are known and can be retrieved bytheir characteristics.

They have a unique , which either the application programmer provides or isidautomatically assigned otherwise.

They optionally have a parameter for logical grouping.type

See also: and .Usage API doc

Where the logic goes

When there is the least bit of business logic, it goes into an action. Actions live in controllerclasses of the server module.

There is a small fraction of logic that resides on the client, which is logic like e.g. forviewcalculating component bounds when layouting.

2.3 The purpose of attributesEach presentation model can refer to many attributes.

Attributes maintain no back-reference. Technically, they can be shared between presentationmodels.

Attributes are You never need implement your own attribute type.generic.

Each attribute has a unique and a that is unique per presentationid propertyNamemodel.

Attributes encapsulate a primitive value and allow listeners to be notified about valuechanges. Technically, this is done via the standard Java PropertyChangeListenertype. In other words, attributes are .Observable

In addition to the value, attributes provide more information about the value, e.g. whether thevalue is (i.e. it differs from the ). The dirty state is observable such that youdirty base valuecan bind against it. As soon as at least one attribute of a presentation model is dirty, thatpresentation model is also considered dirty.

Page 13: Dolphin Guide

13

Attributes have an optional property to capture the fact that this attribute represents aqualifierqualified feature in the domain world. A qualifier like "person.4711.firstName" may be used toexplain that this attribute represents the value of the property of firstName Persondomain class with id 4711.

Whenever the value of an Attribute changes, all other attributes with the same qualifier areautomatically updated.

Qualifiers are a prerequisite for .stable bindings

Since version 0.4 Dolphin attributes can be tagged in order to describe the meaning of thevalue. The enumeration provides all possible tags with being the default. TagsTag VALUEcan be used to e.g. describe whether the person firstname is a attribute,MANDATORYwhether it is , , and so on. Attributes with a tag are observable in theENABLED VISIBLEusual way.

See also and .Usage API doc

2.4 Stable bindings is just another word for an event listener listening to an event source.Binding

OpenDolphin supports binding between attributes and arbitrary other objects where most ofthe time, these other objects are views - with the notable exception of the dolphininfrastructure itself, which automatically listens for value changes in order to notify the server.

While OpenDolphin is independent on any specific UI toolkit and works out-of-the-box withevery technology that understands PropertyChangeListeners (e.g. SWT and Eclipse RCP), itprovides special binding facilities for

JavaFx and

Swing.

Bindings can work in both directions such that a view automatically reflects any change in anattribute value or such that an attribute value is automatically updated with every user input toa UI component.

An important design goal of OpenDolphin is to keep bindings stable.

For the typical use cases it should not be necessary to "unbind" or "rebind", not even for usecases where a selection changes the to-be-displayed information, commonly known asmaster-detail views.

Master-detail views come in many flavors:

a list or table row selection with an associated detail view (often forms) for the row info

a map or chart item selection with details per selected item

a tabbed pane with the individual details (often forms) per tab

OpenDolphin allows to bind the detail view against a stable presentation modelplaceholderthat is automatically synchronized (both read and write) with the selection.

Page 14: Dolphin Guide

14

The detail view has to change its binding!never

In fact, this feature goes beyond master-detail views and applies to all referential structures,which otherwise are a very difficult to handle consistently in a user interface.

See also .Usage

2.5 Collections of presentation models and the model storeAny reasonably sized UI will have not only single presentation models but many of the sametype.

Let's assume that the application displays a list of vehicles where each vehicle is apresentation model instance of type "vehicle".

The list view must add a new row whenever a new vehicle becomes available and mustremove the row when the vehicle becomes unavailable.

To this end, the list view can register itself as a and will beModelStoreListenernotified whenever a presentation model is added to or removed from the store.

The model store defines the managed object space for presentation models.

There are two such stores: one on the client and one on the server. Both are automaticallysynchronized by OpenDolphin.

Conceptually, one can see the model store as

a distributed, in-memory, no-sql database with only two tables (presentation model andattribute)

a specialized event bus (event provider is only the store itself and only ModelStoreEventsare issued)

The application programmer should not access the model store directly but only through thefacade layer, see .Usage

The model store is used internally as a value change listener to all known attributes in order to

consistently update all attributes of the same qualifier

notify the server about changes

Now, let's have a look how the client-server split is designed in OpenDolphin.

2.6 Understanding the client-server split and threading modelA dolphin application has a client and a server module. As we will see in and remote config

this does not necessarily mean that client and server live in different VMs,standalone configbut they can.

With OpenDolphin, the client is responsible to visualize presentation models, the server isresponsible to manage the presentation model data.

In order to keep client and server consistent, one must be chosen as the mastermodel storeand the dolphin team has chosen the client to be that master.

Page 15: Dolphin Guide

15

All changes happen . Then the server is notified, and he updatesfirst on the clientautomatically.

Any server-side changes happen by the server sending a to the client instructingcommandhim which change to apply.

All communication between client and server happens no matter whether anasynchronouslyin-memory or remote configuration is used.

Despite the asynchronous communication, OpenDolphin guarantees notification delivery inthe same sequence as they originated.

OpenDolphin assumes the client to have an event model and the server to live in arequest-response model. OpenDolphin bridges these two worlds.

Despite the asynchronous communication between client (view) and server (controller), allprocessing that takes place as a result of this communication is automatically executed insidethe client's UI thread. Especially all changes to the client model store, client presentationmodels, and attributes are subject to this thread-confined approach. Thus, all binding eventswill be fired in the UI thread and the views are automatically updated correctly.

With the clear structure and division of responsibilities that OpenDolphin imposes onto theapplication a lot of otherwise common headaches around proper thread handling disappear.

2.7 Communicating via CommandsAll communication in OpenDolphin happens with the help of the Command Pattern.

Notifications from the client to the server are sent as commands.

Instructions from the server to the client are sent as commands.

The client can request additional data (beyond presentation models) with the help of a .DataCommand

Commands are simple POJOs and there is a limited set of commands available forsynchronization between client and server.

On the application level only the following commands are used:

NamedCommand is a generic command to trigger a server side action that wasregistered for this name

DataCommand requests arbitrary data that is unrelated to presentation models

There is no need to implement any application specific command classes.

In remote scenarios, commands are encoded and decoded via a (e.g. JSONCoDec).codec

A (e.g. HttpTransport or InMemoryTransport) can be configured to use such a transport codec.

Commands are used as a unit of transport since they are simple to encode/decode but alsosince they can be optimized in a number ways (buffering when the server is temporarilyunavailable, batching, elimination of duplicates) and they open the opportunity for laterinclusion of undo/redo capabilities.

Page 16: Dolphin Guide

16

Since commands are sent asynchronously, one cannot wait for a command to complete.When sending a command, though, one can provide an that isOnFinishedHandlercalled back as soon as the command has returned. The list of all presentation models thatwere affected by that command is passed as argument into the callback methods.

See also .Usage

2.8 Relying on the command sequenceWe have seen that OpenDolphin uses commands for the communication between views andcontrollers, that commands are sent in strict sequence even though they are executedasynchronously, and that commands may have an attached to it.onFinishedHandler

It goes without saying that any onFinished handler is only executed after the controller actionis finished. Therefore the name.

But despite the sequence guarantees that OpenDolphin gives you, there are a few things toconsider about the asynchronous programming model where it is inherently different fromprocedural, UI-blocking code.

Let us first have a look at a typical chain of events.

The sequence of independent commands

The figure below depicts the chain of events that happens when three independentcommands A, B, and C are sent in immediate sequence. Time goes from top to bottom andthe three columns represent three concurrent processing units: the command queue, thecontroller actions (can even be remote), and the onFinished activity after the action returned.

A, B, and C sit in the command queue (column 1).1)

A is removed from the queue and its actions are processed outside the UI thread, maybe2)even remotely on the server (column 2). B and C remain in the queue (column 1).

All A actions are finished and the onFinished handler is called (column 3). 3) While B was removed from the queue and the BA.onFinished is executed inside the UI thread

actions are processed concurrently outside the UI thread (column 2).

Page 17: Dolphin Guide

17

The execution of A.onFinished often triggers further commands (A1 and A2) that are fed intothe queue (column 1) just like any other ordinary command.

At this point the B actions are processed without the effects of A beingvisible.B must not depend on the effects of A!

Emptying out the command queue.4, 5, 6, 7)

Let's summarize the behavior so far

commands are always processed in the strict sequence in which they appear in thecommand queue

the onFinished handler is always executed after the respective action is finished

a command is only processed after the preceding one has finished its actions (but notnecessarily its onFinished handlers)

an onFinished handler is only executed after the preceding onFinished handler is finished

Wouldn't it all be simpler if we waited for A to completely finish including all actions that it maypossibly spawn before processing B like in procedural programming? . This would meanNothat we have to block the queue during that time, which in turn would block the UI - andblocking the UI is the worst you can do.

" ?" This is when we need the onFinishedHandler asBut what if B depends on the effects of Aexplained in the next section.

When commands depend on previous ones

Let's assume that A creates a presentation model and B changes a value in that model. ThenB depends on A and we have to make sure that at the time when the B action is processed alleffects of A are visible, e.g. the presentation model is available in the model store.

This is when we place the sending of the B command in the onFinished handler of the Acommand. So instead of

clientDolphin.send "A"clientDolphin.send "B"clientDolphin.send "C"

we do

clientDolphin.send , {"A" println "Hey, we are inside the onFinished handler!" clientDolphin.send "B"}clientDolphin.send "C"

Page 18: Dolphin Guide

18

The figure below shows the resulting command sequence:

Note row number . Only in the course of A.onFinished being executed (column 3) is the B3command added to the command queue (column 1) and A1 and A2, whichguaranteed afternotify the server of the created presentation model. So all relevant state is properly updatedwhen B's actions ( ) and onFinished handler ( ) are called.6 7

Practical considerations

Asynchronous programming models all have in common that when some logic is dependenton some asynchronous task there is a callback like the onFinished handler involved.

This leads to the question of what to do with a chain of dependencies. Does that automaticallylead to deeply nested callback structures that are difficult to write and understand? Notnecessarily.

Various solution are on the market (e.g. "promises") but OpenDolphin has a simple waywhere we do not need to understand another concept. We simply send an extra command.

This command doesn't need to do anything, only provide us with an onFinished handler suchthat we can add us to the command queue at the appropriate time when all is ready. Youguessed it: such a command already exits. It is the EmptyCommand and you can send it viathe ClientDolphin's method.sync

So even if you have a dependency chain like A <- B <- C there is no need to write

clientDolphin.send , {"A" clientDolphin.send , {"B" clientDolphin.send "C" }}

but you can issue the command in this context-free fashion:

clientDolphin.send "A"clientDolphin.sync { clientDolphin.send }"B"clientDolphin.sync { clientDolphin.send }"C"

Page 19: Dolphin Guide

19

Likewise, when B and C depend on A but not on each other (A <- B, A <- C) you can code thisas

clientDolphin.send "A"clientDolphin.sync { clientDolphin.send "B" clientDolphin.send "C"}

The above has the additional effect that when B has finished, C will immediately follow withoutany other command possibly sneeking in between the two, no matter whether it originatesfrom user input or preceding commands. In that sense you can see the as enclosingsyncan atomic operation.

See also .Usage

2.9 DiscussionNo architecture documentation is complete without explaining the rationale behind thedecisions - which alternatives were considered and why other approaches were not chosen.

To that end, we would like to go through a number of questions that we had to answer whendesigning OpenDolphin and we will do so in the style of a discussion.

Why choosing presentation model as the main pattern?

There would have been other candidates: Model View Presenter, Passive View, SupervisingController, Event Bus, and many more.

All such patterns have their benefits and particularly a distributed event system would haveplayed well with the intended remoting capabilities.

It were mainly the good experiences we made with the presentation model pattern in anumber of large event-based systems that it became our first choice. The distinction between

and turned out to be easier to explain than other approaches. It always gave thewhat howteam good guidance when developing the system. Also we had the honor to have thegrandmaster of this pattern Dr. Dieter Holz in the team.

Following the was always easy enough even though it required writing the respectivepatternclasses all over again with each new project. As soon as we found out how to generalize thepattern and combine it with stable bindings and reliable remoting, we never looked back.

Why so much attention on stable bindings?

Our first attempt of a canonical implementation of the presentation model pattern (which is stillaround under the name ) turned out to become really complex and not reliableGRASPenough to build solid remoting upon because of the intricacies of "rebinding" when switchingreferences to attributes.

Let's say a text field should bind against the first name of person A. Then you switch toperson B. Now try to think through all the implications that this has in terms of consistentlyrebinding all views and other listeners and informing the server. We lost many a night's sleepover this seemingly simple issue. And it gets worse when you don't have a genericimplementation.

Page 20: Dolphin Guide

20

As soon as Andres Almiray discovered how to do stable bindings, everything seemed to justfall in place.

Why generic implementations?

Presentation models, attributes, and commands are all generic.

The alternative would have been to provide superclasses that are to be extended with customstate and behavior.

With generic implementations, we not only have the benefit of fewer classes in the system butmore importantly less structural duplication.

We also avoid all the versioning problems that inevitably arise with shared application classesbetween client and server.

But most of all, with generic implementations it is much easier to control the system. Withsubclasses, you never quite know what they are doing in extend and whether e.g. it is nowsafe to delete them. Generic implementations are much easier to keep cohesive.

By the same train of thought, generic implementations are easier to build upon. Since youexactly know what they are and what they do, it is much easier to build convenience methodsin the facade, e.g. to provide new bindings to yet unknown UI toolkits.

Generic commands provide the option to later extend the system to non-Java clients (weband mobile).

Why not simply using REST?

OpenDolphin is actually free to use a REST if so configured (while the currenttransportHttpTransport is actually using only the POST method and therefore doesn't really qualify aspure REST).

Even though these are not necessarily constituent features of REST, people often understandREST as

a stateless server

message-passing communication (all required info is transferred with every request)

This has obvious benefits and may be the right choice in many scenarios but it doesn't allowto have application logic on the server (only data-access logic, possibly wrapped in services).You end up with a "fat" client that contains the major part of the application logic.

With OpenDolphin the server always knows the exact state of the client and can actaccordingly, sending the least amount of data.

As compared to REST, OpenDolphin users typically have to wait less, because

data sending is done most often without the user noticing (asynchronously)

when the user really has to wait for the server, the package size is as small as possible

since client and server know the same state, they only have to send diffs

small packages (most often only one tcp/ip package) have the lowest latency

Page 21: Dolphin Guide

21

With the application running on the server, OpenDolphin is in full control what the clientdisplays - just like with traditional HTML applications (but with richer visualisation capabilities).When a new server version is deployed, all clients are instantly controlled by the newbehavior.

Running the application on the server can also have a very positive impact on security,privacy, consistency, and legal issues.

All the benefits of OpenDolphin over pure REST come at the expense of maintaining state onthe server side, having a bigger memory footprint and affecting horizontal scaling with theneed for sticky sessions.

Those who would like to enjoy the binding, presentation model structuring, testing capabilities, toolkit independence, and all the other benefits of OpenDolphin, but prefer REST (or other)

remoting for data access, can use OpenDolphin with the in-memory configuration.

Page 22: Dolphin Guide

22

3 How to get started with OpenDolphinFor an easy entry into OpenDolphin, we will follow the steps of the project.DolphinJumpStart

We implement a very simple application that contains only one text field and two buttons to'save' or 'reset' the value. 'Saving' will do nothing but printing the current field value on theserver side.

Both buttons are only enabled if there is really something to save/reset, i.e. the field value isdirty. The dirty state is also visualized via a CSS class (background color changes). Resettingtriggers a 'shake' animation.

Steps 0 to 4 solely live in the "combined" module for a simple jumpstart before we properlysplit client and server in step 5 and only keep a starter class in "combined".

Step 7 produces a war file that you can deploy on e.g. tomcat and the client starter moves tothe "client" module.

Setup and start of a basic JavaFX view

Let's start with the setup.

Please make sure you have visited the project and have looked at theDolphinJumpStartreadme.

You can either choose to clone the repo for following each step (recommended) or use theprovided zip files for a Maven or Gradle build of your own application.

The root directory contains a pom.xml that you may want to point your IDE to for creating aproject. All major IDEs should understand this.

In case you are not using any IDE, follow the readme for how to build and run the varioussteps.

We start our development in the "combined" module with the simplest possible JavaFX view . The class has a main method such that you can start it from inside the IDE.step0.JumpStart

Otherwise use the command line launcher as described in the readme.

When your setup is correct, it should appear on your screen like

The gist of the code shows a simple call into the JavaFX API.

Page 23: Dolphin Guide

23

public class JumpStart Application {extends

@Override void start(Stage primaryStage) Exception {public throws primaryStage.setScene( Scene( Pane(), 300, 100));new new primaryStage.setTitle( );"Dolphin Jump Start" primaryStage.show(); }

void main( [] args) {public static String launch(JumpStart.class); }}

You are free to also use any other Java-based widget toolkit at this point: Swing, AWT, SWT,Eclipse RCP, and else. That makes no difference to OpenDolphin.

Of course, the application needs some sensible content, which we will add right away - stillwithout any OpenDolphin specifics.

3.1 Adding nodes to a stage, registering an onAction handlerWe stay in the "combined" module and enhance the JavaFX view . juststep1.JumpStartslightly with a text field and a button that prints the content of the textfield when clicked.

The application should appear on your screen like

The code now contains references to the widgets

private TextField field; Button button;private

and an action handler

button.setOnAction( EventHandler<ActionEvent>() {new void handle(ActionEvent actionEvent) {public .out.println( +field.getText());System "text field contains: " }});

The printing of the field content is our "stand-in" for a real business logic. You can easilyassume some persistence action at this point or "service" calls in general.

Now it is time to introduce OpenDolphin.

Page 24: Dolphin Guide

24

3.2 Introducing a presentation model with one attribute and bindthe valueIn step 2 we refactor the JavaFX application into to make use ofstep2.JumpStartOpenDolphin.

The visual appearance and the behavior has not changed

but the code has.

As an intermediate step, we have put the OpenDolphin setup and the usage in the sameplace. Don't worry if that looks ugly. We will clean this up in a minute.

Focus on these lines in the code:

We create a presentation model with the distinctive name "input" and an attribute for the "text"property.

PresentationModel input = clientDolphin.presentationModel( , ClientAttribute( ));"input" new "text"

Note that we define a "JumpStartPresentationModel" or so since presentation models innotOpenDolphin are totally generic.

Behind the scenes (no pun intended) happens quite a lot:

the input presentation model is added to the client model store (with indexes beingupdated)

the client dolphin registers itself as a property change listener to the value of the "text"attribute

the server dolphin is asynchronously notified about the creation, which you can observein the logs

the server dolphin asynchronously updates its model store accordingly.

While this happens, we bind the text property of the text field (this is a JavaFX property) to the"text" attribute of the input presentation model

JFXBinder.bind( ).of(field).to( ).of(input);"text" "text"

Page 25: Dolphin Guide

25

Note the fluent API for setting up the binding.

The above is plain Java. When you use Groovy, you can make use ofGroovy's command chain syntax that allows writing the exact same codeas

bind of field to of input"text" "text"

Finally, the action handler that was part of the (client) view before now moves to the (server)controller. We register it as an "action" on the server-dolphin.

config.getServerDolphin().action( , NamedCommandHandler() {"PrintText" new void handleCommand(NamedCommand namedCommand, List<Command> commands) {public text = serverDolphin.getAt( ).getAt( ).getValue();Object "input" "text" .out.println( + text);System "server text field contains: " }});

Note that the (client) view and the (server) controller do not share anyobjects!

The dolphin server action must therefore ask the server-dolphin for the "text" value of the"input" presentation model before he can print it.

Triggering the server action becomes the remaining statement in the button's onActionhandler.

button.setOnAction( EventHandler<ActionEvent>() {new void handle(ActionEvent actionEvent) {public clientDolphin.send( );"PrintText" }});

When we now start the application we see in the log:

[INFO] C: transmitting Command: CreatePresentationModel pmId input pmType attributesnull[[propertyName:text, id:761947653, qualifier: , value: , tag:VALUE]]null null[INFO] S: received Command: CreatePresentationModel pmId input pmType attributesnull[[propertyName:text, id:761947653, qualifier: , value: , tag:VALUE]]null null[INFO] C: transmitting Command: ValueChanged attr:761947653, ->null[INFO] S: received Command: ValueChanged attr:761947653, ->null[INFO] C: server responded with 0 command(s): [][INFO] C: server responded with

telling us that the presentation model has been created and the value changed from null to anempty String, the JavaFX default value for text fields.

Let's enter "abcd":

Page 26: Dolphin Guide

26

[INFO] C: transmitting Command: ValueChanged attr:761947653, -> a[INFO] S: received Command: ValueChanged attr:761947653, -> a[INFO] C: server responded with 0 command(s): [][INFO] C: transmitting Command: ValueChanged attr:761947653, a -> ab[INFO] S: received Command: ValueChanged attr:761947653, a -> ab[INFO] C: server responded with 0 command(s): [][INFO] C: transmitting Command: ValueChanged attr:761947653, ab -> abc[INFO] S: received Command: ValueChanged attr:761947653, ab -> abc[INFO] C: server responded with 0 command(s): [][INFO] C: transmitting Command: ValueChanged attr:761947653, abc -> abcd[INFO] S: received Command: ValueChanged attr:761947653, abc -> abcd[INFO] C: server responded with 0 command(s): []

Every single change is asynchronously sent to the server dolphin. Note that the user interface.does not block

Finally, we hit the button

[INFO] C: transmitting Command: PrintTextserver text field contains: abcd[INFO] S: received Command: PrintText[INFO] C: server responded with 0 command(s): []

Our server action does it's printing action , particularly not in the UI thread!asynchronouslyYou can see the asynchronous behavior by the line ordering in the log above. If it weresynchronous, lines 2 and 3 would never be in this order.

Note that even though all the logic runs in-memory, we have the first benefits fromOpenDolphin:

All actions are executed .asynchronously outside the UI threadWe cannot accidentally block it by long-running or failed operations, whichis a common error in UI development.

With the first dolphinized application running, let's clean up and add a bit more OpenDolphingoodness.

3.3 Logical separation between client and serverIn we first cleanup the code such that it becomes more obvious, which partstep3.JumpStartbelongs to the (client) view and the (server) controller. In the first place, OpenDolphin leads toa view-controller distinction and client-server split. The only thing that is optionallylogicalshared are constants.

It is always a good idea to refactor literal values into constants, especially if they are used inmore than one place for a unique purpose. Therefore, pull out our String literals into staticreferences:

private MODEL_ID = ;static final String "modelId" MODEL_ATTRIBUTE_ID = ;private static final String "attrId" COMMAND_ID = ;private static final String "LogOnServer"

The configuration setup should move into the constructor:

Page 27: Dolphin Guide

27

public JumpStart() { config = DefaultInMemoryConfig();new textAttributeModel = config.getClientDolphin().presentationModel(MODEL_ID, newClientAttribute(MODEL_ATTRIBUTE_ID, ));"" config.getClientDolphin().getClientConnector().setUiThreadHandler( JavaFXUiThreadHandler());new config.registerDefaultActions();}

This leaves the "start" method with "view" responsibilities only: the initial contruction andseparate method call for binding and registering actions.

@Override void start(Stage stage) Exception {public throws

Pane root = PaneBuilder.create().children( VBoxBuilder.create().children( textField = TextFieldBuilder.create().build(), button = ButtonBuilder.create().text( ).build(),"press me" HBoxBuilder.create().children( LabelBuilder.create().text( ).build(),"IsDirty ?" status = CheckBoxBuilder.create().disable( ).build()true ).build()

).build() ).build();

addServerSideAction(); addClientSideAction(); setupBinding();

stage.setScene( Scene(root, 300, 100));new stage.show();}

We add an additional labeled checkbox to visualize the status: whether the text field - orbetter say the dolphin attribute that stands behind it - is considered "dirty".

As soon as you change the content of the text field, this checkbox should become selected. Ifyou remove your edits, it should become unselected again!

Here is how the binding for that requirement looks like:

JFXBinder.bind( ).of(textField).to(MODEL_ATTRIBUTE_ID).of(textAttributeModel);"text"JFXBinder.bindInfo( ).of(textAttributeModel.getAt(MODEL_ATTRIBUTE_ID)).to( ).of(status);"dirty" "selected"

At this point we see the next benefit of presentation model and attribute abstractions: they canprovide more information about themselves and can carry additional state that is automaticallyupdated and available for binding.

Each attribute has a "base" value. When the current value differs from that base value, it isconsidered "dirty". A presentation model is dirty, if and only if any of it's attributes is dirty.

Page 28: Dolphin Guide

28

With this knowledge, we can even do a little more.

3.4 Bind the "dirty" of presentation models to the viewIn we make even further use of the bindable dirty state.step4.JumpStart

First, we are binding not against the dirty state of an attribute, but against the wholepresentation model behind it. This simplifies the binding:

JFXBinder.bindInfo( ).of(textAttributeModel).to( ).of(status);"dirty" "selected"

Second, we also want the button to only be enabled when there is something reasonable todo, i.e. when there is some value change in the form. This is a very common requirement inbusiness applications.

Now, JavaFX buttons do not have an "enabled" state, only a "disabled" state with the oppositelogic. Luckily, our binding facilities are perfectly able to handle this with a converter:

JFXBinder.bindInfo( ).of(textAttributeModel).to( ).of(button, Closure( ) {"dirty" "disabled" new null doCall( dirtyState) {protected Object boolean !dirtyState;return }});

The converter is a Closure (coming from Groovy). But no worries. We can perfectly use it inJava. Future API extensions may offer a more specific converter type here.

You probably guessed that this code looks nicer in Groovy. Yes, it does:

bindInfo of textAttributeModel to of button, { state -> !state }"dirty" "disabled"

Once the code is so nicely broken into independent parts we can put the various parts inseparate modules for better dependency management.

3.5 Split into modules/projectsStep 5 distributes the code into multiple modules (IntelliJ IDEA parlance) orprojects/subprojects (Gradle, Maven, Eclipse parlance). We use the more generic word"module".

The module depends on both client and server and is used for starting thecombinedapplication with the configuration. The sole class that lives in this module is thein-memorystarter class . It sets up the configuration, registers thestep5.TutorialStarterapplication-specific actions on the (server) controller, and starts the view. This is the class tostart from inside the IDE.

The module (or "view" module if you wish) contains the view.client step5.TutorialApplication

Page 29: Dolphin Guide

29

You can see that the view code is pretty much the same as our old application code butcontains the view specific parts only. There is one additional change, though. When thebutton has been pressed and the command has been executed on the server we would like tointerpret the current content of the text field as the new base value just as if the error-freeexecution of the command would imply a correct "save". The "disabled" state of the button willreflect the new non-dirty state.

To this end, we make use of an handler:onFinished

public void handle(ActionEvent actionEvent) { clientDolphin.send(CMD_LOG, OnFinishedHandlerAdapter() {new @Override void onFinished(List<ClientPresentationModel> presentationModels) {public textAttributeModel.getAt(ATT_FIRSTNAME).rebase(); } });}

Please note that the onFinished handler will be called the UI thread. Itasynchronously insidemay trigger changes in the model store, which may lead to changes in the display and anysuch changes occur in the UI thread.must

The module (or "controller" module if you wish) contains the server step5.TutorialActioncontroller.

Both, client and server depend on the module, which makes the shared known to both parties. The shared module itself does not depend onstep5.TutorialConstants

anything.

The code that contains the shared constants now also cares for the uniqueness of certainStrings, particularly of IDs used to retrieve presentation models and named commands.

public PM_PERSON = unique( );static final String "modelId" ATT_FIRSTNAME = ;public static final String "attrId" CMD_LOG = unique( );public static final String "LogOnServer"

unique( key) {private static String String TutorialConstants.class.getName() + + key;return "."}

Splitting four classes into four different modules may look a bit over-engineered at this pointbut it is an indispensible step before we can go into true remoting and before we can instantlyswitch between in-memory- and client-server-mode.

If you fear that this is too much work for setting up the directory structure or the build-timedependencies: simply unzip one of the and you are good to go.project templates

We get the following benefits:

Page 30: Dolphin Guide

30

ability to start the code with different configurations (in-memory orunmodifiedclient-server)

clear and minimal dependencies when building

a minimum of shared code (only the constants) to express semantic dependencies assyntatic dependencies

actions cannot "accidentally" reach out to view code. The widget set is not even on theclasspath!

actions cannot possibly block the UI thread

view changes are displayed correctly since they happen in the UI threadalways

the separation of responsibilities is enforced by the dependency structure

3.6 Enhanced view, let the "director" wire all application actionsWe finish the application with some more refactorings in and somestep6.TutorialApplicationtweaks to the view such that it appears like

The true value of the change is not visible in a screenshot, though, since it is in the behavior.The modified background color of the text field appears as soon as it becomes dirty and is setback to the original state when the dirty state is set back.

To make this happen, we enhance the binding with a little trick in the converter that adds the"dirty" style class to the text field when needed and removes it otherwise.

JFXBinder.bindInfo( ).of(textAttributeModel).to( ).of(textField, Closure( ) {"dirty" "style" new null call( dirty) {public String Boolean (dirty) {if textField.getStyleClass().add( );"dirty" } {else textField.getStyleClass().remove( );"dirty" } ;return "" }});

The tutorial.css contains the definition of that style, which makes the code very flexible shouldwe later decide to visualize the dirty state differently.

Page 31: Dolphin Guide

31

.root { -fx-background-color: linear-gradient(to bottom, transparent 30%, rgba(0, 0, 0, 0.15) 100%);}#content { -fx-padding : 20; -fx-spacing : 10;}.dirty { -fx-background-color: papayawhip;}

Once we have the view code so nicely refactored to be free of any other responsibility, we canspend some extra brain cycles on improving the look and feel, both when visualizing state butalso for emphasizing state transitions.

Reset by shaking the field

When we click the "reset" button, the dirty value is replaced by the last known base value anda "shake" animation is played on the text field.

A shake is a rotation of the field around its center by an angle from -3 to +3 degrees. This isdone 3 times during 100 ms each. It makes for a funny effect.

final Transition fadeIn =RotateTransitionBuilder.create().node(textField).toAngle(0).duration(Duration.millis(200)).build();

Transition fadeOut =finalRotateTransitionBuilder.create().node(textField).fromAngle(-3).interpolator(Interpolator.LINEAR). toAngle(3).cycleCount(3).duration(Duration.millis(100)). onFinished( EventHandler<ActionEvent>() {new @Override void handle(ActionEvent actionEvent) {public textAttributeModel.getAt(ATT_FIRSTNAME).reset(); fadeIn.playFromStart(); } }).build();

reset.setOnAction( EventHandler<ActionEvent>() {new @Override void handle(ActionEvent actionEvent) {public fadeOut.playFromStart(); }});

Note that the transition is only created once but played as often as you click the button.

Yes, director!

The server (controller) part has been divided in two classes: the thatstep6.TutorialActioncontains only one application-specific action and the who selects whichstep6.TutorialDirectoractors should appear in the play, i.e. registers actions with the server dolphin.

This distinction makes it easier to evolve the application when new actions come into playsince the server adapter (servlet) doesn't have to change when the list of actions changes aswe will see in a minute.

3.7 Remote setupWith the application being properly structured in its modules, we can now finally distribute it asa true client-server application without any of the application code being touched at all. Onlythe the server adapter needs to be in place and the client starter needs to connect to thecorrect URL.

Page 32: Dolphin Guide

32

The is a plain-old Servlet such that the code can run in any servlet container. Itserver adapteris as small as can be:

public class TutorialServlet DolphinServlet{extends @Override void registerApplicationActions(ServerDolphin serverDolphin) {protected serverDolphin.register( TutorialDirector());new }}

As with any servlet, you need to register it in the :web.xml

<servlet> <display-name>TutorialServlet</display-name> <servlet-name>tutorial</servlet-name> <servlet-class>step_7.servlet.TutorialServlet</servlet-class></servlet>

<servlet-mapping> <servlet-name>tutorial</servlet-name> <url-pattern>/tutorial/</url-pattern></servlet-mapping>

The can now move to the "client" module since it is no longer dependentstep7.TutorialStarteron the combination of client and server. It can be cleaned from setting up the in-memoryserver and must of course point to the server URL:

public void main( [] args) Exception {static String throws ClientDolphin clientDolphin = ClientDolphin();new clientDolphin.setClientModelStore( ClientModelStore(clientDolphin));new HttpClientConnector connector = HttpClientConnector(clientDolphin, new

);"http://localhost:8080/myFirstDolphin/tutorial/" connector.setCodec( JsonCodec());new connector.setUiThreadHandler( JavaFXUiThreadHandler());new clientDolphin.setClientConnector(connector); TutorialApplication.clientDolphin = clientDolphin; Application.launch(TutorialApplication.class); }

That was it!

You can now start the provided jetty server

./gradlew :server-app:jettyRun

and the as many TutorialStarter clients as you want.

Alternatively, ou can now create a war file via Maven or Gradle and deploy it on any serveryou fancy.

Some extra flexibility

You may have observed that we refactored the actual server-side printing into a service classwith a service interface. This allows some extra flexibility when the server-side action dependson any technology that is only available on the server - say JEE, JPA, Spring, GORM, or so.

Refactoring that access into an interface allows to still use the same code with in-memorymode for testing, debugging, profiling, and so on with a stub or mock implementation of theservice interface.

Page 33: Dolphin Guide

33

Final considerations

This has been a very small application to start with but we have touched all relevant basesfrom starting with a standalone view, through proper modularization, up to a remoteclient-server setup.

We have used a "bare-bones" setup with 100% pure Java and a no dependencies beyondJava 7+ and OpenDolphin.

This is to show that OpenDolphin is as "un-opinionated" as can be.

In real life and in most of the demos that ship with OpenDolphin, we make additional use ofGroovy, GroovyFX, and Grails. Note that you can use any client- and server-side frameworkand technology with OpenDolphin: Griffon, Eclipse RCP, Netbeans - JEE, Spring, Grails,Glassfish, JBoss, Hibernate, WebLogic, WebSphere, you name it.

Remember: OpenDolphin is a library, not a framework.We don't lock you in, we are open.

Of course, a full application has more use cases than managing a single text field.

The chapter leads you through the typical use cases of master-detailuse cases and demosviews, form-based pages, collections of data, lazy loading, shared attributes, CRUDoperations, and much more by describing the use case, explaining the OpenDolphin approachof solving it, and pointing to the respective demos.

Page 34: Dolphin Guide

34

4 Use Cases and DemosOpenDolphin has been created from use cases.

This has always been very important to us.

Each functionality in OpenDolphin is motivated by a use case that justifies it's existence in thecode base. Likewise, each use case comes with a demo in the OpenDolphin distribution -from simple, basic attribute bindings to complex feature combinations.

This chapter leads through the various use cases from simple to increasingly complex ones.

We explain

the use case

OpenDolphin's approach of solving it

how it is visible in the respective demos

4.1 The general approach of OpenDolphin with a Login dialogThe login demo is a short intro that explains the general approach of OpenDolphin withoutany code.

Open this short video in a separate window: login demo

When watching the video, please pay attention to the following parts:

first, an action is registered on the server side

a presentation model is created on the client side with attributes for username, password,and loggedIn status

the presentation model sent to the serverautomatically

the client creates a view for the login

the view binds against the attributes

when the user enters a name, this changes the attribute on client andautomatically bothserver side

the communication happens asynchronously; the user beforedoes not have to waitentering the password

in the very same manner, the password gets transferred

the user clicks on the login button

a command is sent to the server which triggers the login action

this reads username and password and sets the loggedIn status to TRUE (*)

the status change is automatically mirrored on the client

the login view disappears since its visible property is bound against the not-loggedInattribute

Page 35: Dolphin Guide

35

(*) technically, the server sends a ValueChangedCommand to the client but this is animplementation detail.

4.2 Immediate and deferred binding

Use Case

Sometimes a value change in lets say a text field should immediately update all dependentviews (a label, a frame title) - but sometimes only when a certain event occurs: button clicked,enter pressed, focus change and the likes.

Sometimes the binding is unidirectional, sometimes bidirectional.

Approach

OpenDolphin bindings are always immediate. When the source value changes the targetvalue is updated immediately.

If you need a deferred update, you do not use binding at all but provide the respective eventhandler to copy the value from the source to the target, usually by setting the value of anattribute.

Bindings are always unidirectional. If you need bidirectional binding, you use two bindingstatements, one for either direction.

Demo

The demo looks like

When you edit the text field, the header is immediately updated.

Label and frame title are only updated when hitting enter in the text field or when clicking thebutton.

Please see the full .demo sources

OpenDolphin bindings always go from source to target. This code makes sure that wheneverthe title attribute of the presentation model changes, the title of the frame is updated:

Page 36: Dolphin Guide

36

bind TITLE of pm to FX.TITLE of primaryStage // groovy style

The same is true for the label, but here we are more Java-stylish:

bind(TITLE).of(pm).to(FX.TEXT).of(label) // java fluent- styleinterface

The input text field shall always show the value of the title attribute:

bind TITLE of pm to FX.TEXT of input

Hitting enter or clicking the button shall copy the value of the text input field to the attributeand thus trigger update of all bound views. We share the same event handler for the onActionof the text input field and the submit button:

def copyFieldToPm = { pm[TITLE].value = sgb.input.text } as EventHandlersgb.input.onAction = copyFieldToPmsgb.submit.onAction = copyFieldToPm

:Corner caseIf we need a mixture of both immediate and deferred update, then we have a bit of a problem.We can resolve it by directly binding the views (ignoring the attribute):

// auto-update the header with every keystrokebind FX.TEXT of input to FX.TEXT of header

The views now act as one "combined" view, but this situation should be avoided since we donot want the views to know each other. They shall only know their attributes.

:VariantA second way of approaching the above is binding the text field back to the title attribute,which will automatically trigger updates of all dependent views with every keystroke. This iseffectively a bidirectional binding:

// the below is an alternative that updates the pm with every keystroke and thus all bound listeners// bind TEXT of input to TITLE of pm

4.3 The container yard monitoring applicationPlease have a look at this short video in a separate window: container yard demo

You see a monitoring application that has been implemented in a joint effort by

Navis

Oracle

Canoo

and was demonstrated at the JavaOne 2012 strategy keynote.

Page 37: Dolphin Guide

37

This application uses JavaFX on the client side and builds upon an existing server-sideprogramming model.

OpenDolphin combines these two worlds.

The application was first developed in a purely 2-D fashion and later extended to 3-D.

Despite the rather dramatic effect that this extension had to the user interface, thepresentation models did not change by a single bit!

This example demonstrates:

the value of having the full "fidelity" of a Java desktop client

how to leverage the power of Java server-side applications

how OpenDolphin helps to stay independent of changes in visualization and protects theinvestment in application logic

Page 38: Dolphin Guide

38

5 tbd Configuration and Setup

5.1 Standalone in-memory usage

5.2 Remote setup

Page 39: Dolphin Guide

39

6 tbd Developer Zone

6.1 How to build

Copies of this document may be made for your own use and for distribution to others, providedthat you do not charge any fee for such copies and further provided that each copy contains this

Copyright Notice, whether distributed in print or electronically.