1. Introduction

The Dolphin Platform is a framework that implements the remote presentation model pattern and provides a modern way to create enterprise applications. As you might know there are several of these frameworks out there. So let us start by describing what makes the Dolphin Platform special and why we decided to create it.

1.1. Architecture overview

In the Dolphin Platform, all models will automatically be synchronized between client and server. Thus, you don’t need to think about any specific endpoints or requests.

model sync
Figure 1. Synchronization of the model

Based on this, the Dolphin Platform defines server side controllers that contain all the controller logic for a specific view. The lifecycle of these controllers is automatically synchronized with the view lifecycle. With this approach you have a MVC group for each client view with a synchronized model and a managed controller.

getting started 1
Figure 2. Different clients

The Dolphin Platform provides a server and client framework that let you easily write applications based on the described pattern. To do so the platform contains support for well known sever framework like JavaEE or Spring and several implementations to create clients by using for example JavaFX or Polymer.

1.2. Sources

The Dolphin Platform provides implementations fo several platforms. Therefore the APIs and implementations of the Dolphin Platform can be found in several GitHub repositories:

  • The Java based server API and the basic client API can be found in the main repository of the Dolphin Platform. This repository contains the JavaFX client API, this documentation and some samples, too. The repository can be find here.

  • The Android client API can be found in a separate repository.

  • For JavaScript the Dolphin Platform provides a basic framework independend implementation that can be found here.

  • For Google Polymer the Dolphin Platform provides a client API that is based on the basic JavaScript cient API. The Polymer client API can be found in this repository.

  • The Dolphin Platform provides a Maven archetype to create the initial structure of a Dolphin Platform based project. The sources of the archetype can be found here.

  • The Dolphin Platform provides a Lazybones template to create the initial structure of a Dolphin Platform based project. The sources of the template can be found here.

2. The Remote Presentation Model

Remote Presentation Model (RPM) is a software design pattern for implementing user interfaces. Like Model View Controller (MVC) it divides a module into three parts:

  • Model

  • View

  • Controller

The main difference to MVC is that the Controller part of the RPM pattern is speretated from the view. In a client server architecture the controller part is mostly defined on the server. By doing so several benefits of server site programming can be directly used in the controller. In addition the view part is mostly extreme lightweight and can easily be replaced. Both controller and view know the model and can interact with the model. Since the components RPM pattern can be seperated on client and server the model must be synchronized between client and server.

rpm1
Figure 3. Remote Presentation Model

Since view and controller can interact with the model it’s a best practice to support the observer pattern for the model. By doing so the view can simply bind its components to properties of the model and the server can react on changes by simply adding a lister to an observable property.

workflow
Figure 4. Basic RPM workflow

The Dolphin Platform provides an implementation of the Remote Presentation Model pattern. This documentation provides descriptions and samples of the public model, controller and view APIs of the Dolphin Platform.

3. Dolphin Platform Jumpstart

To get started with Dolphin Platform and create your first client-server-project several Maven archetypes are provided by Dolphin Platform. Based on this archetypes it’s quite easy to create a full functional client-server-application as a perfect base for your own project or to learn the different APIs of Dolphin Platform by simply experimenting with the generated project.

3.1. Creating a project with Maven

To create a new project only Maven must be installed on your system. Once Maven is installed you can create a complete project with only one shell command:

$ mvn archetype:generate -Dfilter=com.canoo.dolphin-platform:
The archetypes are deployed to (Maven Central) and (JCenter). If you have configured your Maven installation to use a different repository that do not mirror Maven Central or JCenter your Maven installation might not find the archetypes to generate a jumpstart.

The command will start a Maven process that creates the new project. To do so you can choose between different options in a shell wizard:

jumpstart
Figure 5. The shell wizard

As a first step you need to choose on what archetype your project should depend. Dolphin Platform provides several archetypes to create for example projects that use Spring Boot or JavaEE on the server. After you have defined the archetype you need to select a version of the archetype. This version is related to the Dolphin Platform version the archetype depends on. Here it’s best practice to simply select the last version. As a last step you need to specify groupId, artifactId and the version of your new project. Once this is done the generation might tackle some time on the first call since all needed dependencies and plugins are downloaded to your computer. You can simply import the generated project in any IDE of your choice that supports Maven projects. The generated project contains a readme.md file that describes how the client and server parts of the project can be started.

3.2. Creating a project with Lazybones

Next to the Maven archetype we provide a Lazybones template for application that are based on the Dolphin Platform. The project and a getting-started documentation can be found at github.

3.3. Creating a project by hand

Maybe some developers don’t want to use the Maven archetype to create a new Dolphin Platform based application or want to integrate the Dolphin Platform in an existing application. Even if the Maven archetype is the fasted way to set up a startable project it’s quite easy to create such an application from scratch.

The API and implementation of the Dolphin Platform is splitted in several modules. Depending on the application infrastructure a developer needs to add different modules to the application dependencies. The following diagramm gives an overview of all Dolphin Platform modules:

modules
Figure 6. Dolphin Platform modules

3.3.1. Dolphin Platform on the server

On the server side you can choose between a JavaEE or a Spring integration. Each of this modules depends on the basic server module of the Dolphin Platform that contains the public API for the server part. Based on this you only need to add one dependency to your server application. All modules on the server are full compatible to Java 7 and above.

You can simply integrate Dolphin Platform in a Spring based application. To do so you only need to add the Spring module. All Java modules of the Dolphin Platform are available at Maven Central and JCenter. The following snippet shows the spring module as a Maven dependency:

<dependency>
    <groupId>com.canoo.dolphin-platform</groupId>
    <artifactId>dolphin-platform-server-spring</artifactId>
    <version>0.8.9</version>
</dependency>

Next to this the Dolphin Platform provides support for JavaEE. The following snippet shows the JavaEE module of the Dolphin Platform as a Maven dependency:

<dependency>
    <groupId>com.canoo.dolphin-platform</groupId>
    <artifactId>dolphin-platform-server-javaee</artifactId>
    <version>0.8.9</version>
</dependency>
Additional information can be found in the JavaEE and Spring chapter of this documentation.

3.3.2. Dolphin Platform on the client

Since all Dolphin Platform modules on the server a written in Java the client modules are written in multiple programming languages to support as many platforms as possible. Currently you can choose between a Java implementation for Desktop and mobile clients (JavaFX and Android) and a JavaScript implementation with special API for several JavaScript frameworks (currently Polymer and AngularJS).

Java based clients

For a desktop or Android client a developer only need to add the specific Java modules of the Dolphin Platform as dependencies to the client project.

The following snippet shows the dependency for a JavaFX based client as a Maven dependency:

<dependency>
    <groupId>com.canoo.dolphin-platform</groupId>
    <artifactId>dolphin-platform-client-javafx</artifactId>
    <version>0.8.9</version>
</dependency>

If you want to create a Android application you need to add the following dependency to your application:

<dependency>
    <groupId>com.canoo.dolphin-platform</groupId>
    <artifactId>dolphin-platform-client-android</artifactId>
    <version>0.8.9</version>
</dependency>
JavaScript based clients

TODO

4. Dolphin Platform IntelliJ Plugin

We provide a plugin for IntelliJ that creates getter & setter methods for the bean model of Dolphin Platform. Like the JavaFX properties you need more than a simple getter and setter to get the full power out of the Dolphin Platform properties and collections. To simplify the access we created a plugin for IntelliJ that creates all needed methods for you.

4.1. Installing the Plugin

To install the Plugin you need to open the "Preferences" dialog in IntelliJ. In that dialog go to the "Plugin" section as shown in the following picture:

intellij pref
Figure 7. IntelliJ Preferences

In this dialog you must click the "Browse repositories…​" Button in the bottom. By doing so you can search in the IntelliJ repository for plugins that are not added to your IntelliJ installation. The dialog provides a search field. Once you start typing "Dolphin" in the search field the Dolphin Platform plugin will be found:

intellij plug
Figure 8. IntelliJ Plugin

You only need to click the green install button in the plugin description to install the Dolphin Platform plugin to your IDE.

4.2. Use the Plugin

To be true there is nothing special that you need to know :) By adding the plugin IntelliJ will automatically detect Dolphin Platform properties and collections and create all methods for you by using the default functionality to create getter & setter.

The following screen shows a class that defines 2 Dolphin Platform properties.

intellij generate
Figure 9. IntelliJ sample

By opening the "Generate" popup you can now trigger the "Getter and Setter" action as always. The following dialog shows the 2 properties that part of the class. By selecting both IntelliJ will create all getter / setter methods for you:

intellij generated
Figure 10. Generated code

5. JavaDoc

The Dolphin Platform is based on some modules. The following diagram shows the modules of the Dolphin Platform and the dependecies between the modules:

deps
Figure 11. Module dependencies of the Dolphin Platform

The JavaDoc for the modules can be found here:

6. The Dolphin Platform Lifecycle

The Dolphin Platform provides lifecycles that defines the communication between client and server and the internal lifecycle of a MVC group.

6.1. The Lifecycle for one client

Each clients that is connected to the Dolphin Platform server has its own lifecycle. This lifecycle starts with the inital connection and will end when the client disconnects from the server. The Dolphin Platform provides several classes in its public API that wrap the lifecycle and make it quite easy to create an application that is based on the Dolphin Platform.

6.2. Creating a JavaFX clients that supports the Dolphin Platform Lifecycle

The dolphin-platform-client-javafx module provides the abstract DolphinPlatformApplication class that can easily be used to create a JavaFX based client for the Dolphin Platform. Internally the class already defines the Dolphin Platform lifecycle and by extending the class for a custom client a developer only needs to define some general information. The following code shows how a basic implementation might look like:

public class MyCustomClient extends DolphinPlatformApplication { (1)

    @Override
    protected URL getServerEndpoint() {
        try {
            return new URL("http://localhost:8080/dolphin"); (2)
        } catch (MalformedURLException e) {
             throw new RuntimeException("Can not create URL!", e);
        }
    }

     @Override
     protected void start(Stage s, ClientContext c) throws Exception { (3)
         s.setScene(new Scene(new Label("Hello Dolphin")));
         s.show();
     }

}
1 Our custom client extends the abstract DolphinPlatformApplication class
2 We need to implement the abstract method by providing the server endpoint of the Dolphin Platform. This is /dolphin by default but can be configured on the server
3 Like the void start(Stage primaryStage) method of the JavaFX Application class this method will automatically be called once JavaFX is ready. In additon this method provides the Dolphin Platform ClientContext as an parameter. By doing so you already have a connection to the Dolphin Platform server once this method is called. The method is called on the JavaFX Platform thread.

The given example already creates a connection to the Dolphin Platform server and shows an UI once the connection is established and JavaFX is ready. In addition this client already provides a disconnect once the UI will be closed by a user and some raw error handling. All this is defined in the DolphinPlatformApplication class but can simply be overwritten. If you want more control of the error handling you can simply provide a custom implementation for the methods void onInitializationError(Stage primaryStage, ClientInitializationException initializationException) and void onShutdownError(ClientShutdownException shutdownException). A client that defines specific functionallity for all this states of the lifecycle might look like this:

public class MyCustomClient extends DolphinPlatformApplication {

    private Logger LOG = LoggerFactory.getLogger(MyCustomClient.class);

    protected URL getServerEndpoint() {
        try {
            return new URL("http://localhost:8080/dolphin");
        } catch (MalformedURLException e) {
             throw new RuntimeException("Can not create URL!", e);
        }
    }

    @Override
    protected void start(Stage s, ClientContext c) throws Exception {
        LOG.info("Dolphin Platform Lifecycle init successful");
        s.setScene(new Scene(new Label("Hello Dolphin")));
        s.show();
    }

    @Override
    protected void onInitializationError(Stage s, ClientInitializationException e) {
        LOG.error("Dolphin Platform Lifecycle error on init", e);
        super.onInitializationError(s, e);
    }

    @Override
    protected void onShutdownError(ClientShutdownException e) {
        LOG.error("Dolphin Platform Lifecycle error on shutdown", e);
        super.onShutdownError(e);
    }


     @Override
     protected void onShutdown() {
         LOG.info("Dolphin Platform Lifecycle shutdown successful"); (5)
         super.onShutdown();
     }
}
1 In this custom implementation a logger should be used to log information of the lifecycle
2 Once the start method is called the Dolphin Platform initialization has been done and the client is connected to the server
3 If an exception occurs on the initialization this method will be called. You should call the super method in a custom implementation since a clear shutdown is already defined in the DolphinPlatformApplication class.
4 If an exception occurs on the shutdown this method will be called. You should call the super method in a custom implementation since a clear shutdown is already defined in the DolphinPlatformApplication class.
5 Once the start method is called the Dolphin Platform shutdown has been done and the client can be destroyed. You should call the super method in a custom implementation since a clear shutdown is already defined in the DolphinPlatformApplication class. Note: onShutdown() is not supported anymore in 0.8.8.

To be true these are not the only moments in that the Dolphin Platform can throw an exception. Maybe the server shuts down when a client is still connected to the server. In this case the DolphinPlatformApplication class provides an additional method that can be overridden to handle this kind of remoting exception:

public class MyCustomClient extends DolphinPlatformApplication {

    private Log LOG = LoggerFactory.getLogger(MyCustomClient.class);

    protected URL getServerEndpoint() {
        try {
            return new URL("http://localhost:8080/dolphin");
        } catch (MalformedURLException e) {
             throw new RuntimeException("Can not create URL!", e);
        }
    }

     @Override
     protected void start(Stage s, ClientContext c) throws Exception {
         LOG.info("Dolphin Platform Lifecycle init successful");
         s.setScene(new Scene(new Label("Hello Dolphin")));
         s.show();
     }

     @Override
     protected void onInitializationError(Stage primaryStage, ClientInitializationException initializationException) {
         LOG.error("Dolphin Platform initialization error", initializationException); (1)
         super.onInitializationError(primaryStage, initializationException);
     }

     @Override
     protected void onRuntimeError(final Stage primaryStage, final DolphinRuntimeException runtimeException) {
         LOG.error("Dolphin Platform runtime error in thread " + runtimeException.getThread().getName(), runtimeException); (2)
         super.onRuntimeError(primaryStage, runtimeException);
     }

     @Override
     protected void onShutdownError(ClientShutdownException shutdownException) {
         LOG.error("Dolphin Platform shutdown error", shutdownException); (3)
         super.onShutdownError(shutdownException);
     }
}
1 This method is called if the connection to the Dolphin Platform server can’t be created.
2 This method is called if the connection to the Dolphin Platform server throws an exception at runtime.
3 This method is called if the connection to the Dolphin Platform server can’t be closed.

You should call the super methods in a custom implementation of the error handlers since a clear shutdown is already defined in the DolphinPlatformApplication class.

6.3. The Dolphin Platform Lifecycle on the server

A Dolphin Platform based server provides a http endpoint that is used for the communication between client and server. Whenever a new client is created and connects to the server a session is created on the server. The Dolphin Platform provides its own session type that is called DolphinSession. This session type is "lower than the http session". The DolphinSession is important if you want to create web application, for example. Since all the tabs of a browser share the same http session it’s hard to define data that is only related to one tab in the browser. In that case the lifecycle of a DolphinSession is bound to a tab in the browser and ends when the tab will be closed.

Once a client disconnect its connection to the server the DolphinSession for this client will automatically be removed. If a client can not disconnect because of an exception or maybe the Java process on the client is killed by an user the DolphinSession will automatically be removed with the http session on the server.

To react on the lifecycle of a client on the server a developer can provide implementations of the DolphinSessionListener interface that is part of the public server API of the Dolphin Platform. Each implementation of the DolphinSessionListener interface that is annotated with @DolphinListener will be instantiated at runtime and used as a listener for created and destroyed session. A basic implementation of such a class might look like this:

@DolphinListener
public class MyCustomListener implements DolphinSessionListener {

    private Logger LOG = LoggerFactory.getLogger(MyCustomListener.class);

    public void sessionCreated(DolphinSession s) {
        LOG.info("Session with id {0} created!", s.getId());    (1)
    }

    public void sessionDestroyed(DolphinSession s) {
        LOG.info("Session with id {0} destroyed!", s.getId());  (2)
    }

}
1 This method is called for each new DolphinSession that is created on the server
2 This method is called for each destroyed DolphinSession on the server

7. The Model API

One of the core features and maybe the most important concept of Dolphin Platform is the model synchronization between client and server. For each view in the client a controller instance "lives" in the server that defines all the logic of the view. A model is automatically shared and synchronized between the client view and the controller on the server.

model sync
Figure 12. Synchronization of the model

Such a model can be a bean or a hierarchy of several beans. Dolphin Platform provides an API to create full observable beans for the model layer.

The public API of the module layer is defined in the dolphin-platform-core module. The JavaDoc of the module can be found here

7.1. Creating a bean

A model for the Dolphin Platform can simply be defined as a Java bean but instead of using primitive date types for all the attributes of the bean Dolphin Platform provides the Property<V> interface that should be used to define attributes. Based on this a definition for a bean with only one String attribute will look like this:

@DolphinBean
public class MyModel {

    private Property<String> value;

    public Property<String> valueProperty() {
        return value;
    }

    public String getValue() {
        return value.get();
    }

    public void setValue(String value) {
           this.value.set(value);
    }

}
Maybe you ask yourself why the @DolphinBean annotation is needed. Internally the Dolphin Platform checks if a bean class is annotated with @DolphinBean and will trow a BeanDefinitionException if the annotation is not present. By doing so it will be easy to check if you use the right model classes. We plan to add some additional features based on the annotation in the future. One example can be an annotation processor that checks if all classes that are defined as the Dolphin Platform beans (by adding the @DolphinBean annotation) are valid Dolphin Platform beans.
As you can see the class defines not only a basic getter and setter method for the property. Like for JavaFX properties we think that it’s best practice to provide a method to access the property and getter / setter methods for the internal value of the property. Since writing all this methods might take some time we provide a plugin for IntelliJ that generates that methods for you. You can read more about this plugin in the "Dolphin Platform IntelliJ Plugin" chapter.

7.2. Supported types

Currently Dolphin Platform properties and collections support the following types as content:

  • Integer

  • Long

  • Float

  • Double

  • Byte

  • Short

  • Boolean

  • String

  • java.util.Date (since version 0.8.4)

  • java.util.Calendar (since version 0.8.4)

  • Enums (since version 0.8.4)

  • Dolphin Platform Bean (see description)

As some browsers have issues with Timezone, one has to use UTC. If the provided Calendar is not set to UTC, it will be converted.

Since version 0.8.8 the Dolphin Platform contains the optional module dolphin-platform-date-time-converter that provides the support for addition types for properties and collections. All the types are part of the Java 8 date and time API:

  • java.time.Duration

  • java.time.LocalDateTime

  • java.time.Period

  • java.time.ZonedDateTime

Currently the additional data types are only supported in Java. The support for the JavaScript will be added in a future version

7.2.1. Custom data types

Since version 0.8.8 the Dolphin Platform provides a SPI to add custom data types for properties and collections. To add support for a new data type a custom converter must be provided. To do so the com.canoo.dolphin.converter.ConverterFactory interface must be implemented and provided for Java Service Provider Interface (SPI). Examples for custom data types can be found in the sources of the optional dolphin-platform-date-time-converter module.

7.3. Using collections

The first example of a Dolphin Platform bean contained only a String property but most of the time you need more complex beans. That’s why we added collection support to the Dolphin Platform model API. Next to the properties a bean can contain lists that are define by the ObservableList<V> interface. The interface extends the default Java List interface by adding support for observers. A Dolphin Platform bean that contains a list might look like this:

@DolphinBean
public class MyModel {

    private ObservableList<String> values;

    public ObservableList<String> getValues() {
        return values;
    }

}

A ObservableList<V> supports all the same generic types as a property.

7.4. Defining hierarchies

A Property<V> or ObservableList<V> instance can not only hold primitive types. As defined in the supported types a property or list can contain other beans, too. By doing so a model can define a hierarchy of several beans. The following class shows how a model class that contains a list of other beans might look:

@DolphinBean
public class MainModel {

    private ObservableList<MyModel> values;

    public ObservableList<MyModel> getValues() {
        return values;
    }

}

In this example instances of the MyModel that was shown as an example earlier can be stored in the list of this model class.

It’s important to now that a developer should never create new model instances "by hand". If a model will be created by simply calling it’s constructor it won’t become part of the synchronized models. To create new model instances the Dolphin Platform bean manager must be used. The API of the bean manager and how it can be used to create new instances of models and beans will be shown later.

7.5. Add observer

Both the Property<V> and ObservableList<V> interface implement the observer pattern. By doing so changes in the model layer can easily be handled in the view and the controller. The interfaces provide methods to add observer that will be called whenever the internal content will change. The following snippet shows how an observer can be added to a property:

myModel.valueProperty().onChanged(e -> System.out.print("Property changed"));

Once the value of the value property will be changed the given observer will be triggered. For properties the observer is defined by the ValueChangeListener class. In the given example a lambda expression is used as the observer. So whenever the value will be changed a "Property changed" message will be printed to the console.

The Dolphin Platform modules are compiled against Java 7 but provide a very good support for Java 8 lambda expressions. Wherever it makes sense interfaces are defined as functional interface. By doing so instances of the interface can be defined as a lambda expression when using Java 8. In the given example the lambda implements the ValueChangeListener interface of the Dolphin Platform.

Whenever a ValueChangeListener is called an event object is provided. This object can be used to access the old and the new value of a change:

myModel.valueProperty().onChanged(e -> print("Property changed from " + e.getOldValue() + " to " + e.getNewValue()));

TODO: Subscription

Subscription subscription = myModel.valueProperty().onChanged(e -> print("Property changed"));

7.6. Defining the model of a MVC group

As you can see in the following image each MVC group in the Dolphin Platform contains a model. A MVC group is based on the MVC pattern and contains a controller, a view and a model. In the Dolphin Platform approach the controller lives on the server and the view is defined on the client. The model is shared between the client and the server.

model sync
Figure 13. A MVC group

As we have seen until now a model is defined by properties and collections. Since Property<V> and ObservableList<V> are defined as interfaces and no model provides a specific constructor it’s still not clear how a new instance of a model should created. Thanks to the Dolphin Platform architecture a developer don’t need to think about the model instanciation or the lifecycle of a model. Whenever a new MVC group is created by the platform the model instance will be created automatically. To do so the model must be defined in the controller. The Dolphin Platform provides the @DolphinModel annotation that is used to inject the created model instance in the controller instance. The following sample code shows a minimal controller that defines its model type:

@DolphinController
public class MyController {

    @DolphinModel
    private MyModel model;

}

The model instance will be automatically synchronized with the client. Since the model is completelly observable you can simply bind the properties and lists that are defined in the model to your UI components in the client.

7.7. Working with the BeanManager

TODO

7.7.1. Creating new model instances

Since all beans of the Dolphin Platform model layer will be synchronized between client and server a new model instance can not be created "by hand" (MyBean bean = new MyBean). So instead of creating a new bean instance by calling its constructor the BeanManager interface must be used to create a new bean instance. By doing so the bean instance will automatically be added to the bean manager and synchronized between client and server. Here is an example how a bean instance can be created:

MyBean bean = beanManager.create(MyBean.class);

7.7.2. Removing a bean

The BeanManager interface provides several deprecated methods to remove beans:

  • void remove(Object bean)

  • void removeAll(Class<?> beanClass)

  • void removeAll(Object…​ beans)

  • void removeAll(Collection<?> beans)

All these methods do not work transitively. This means that in a hierarchy of beans all beans must be detached from the bean manager seperately.

In addition the BeanManager interface provides a method to check if a bean is still synchronized between client and server or if it’s already removed from the bean manager:

  • boolean isManaged(Object bean)

Currently all mentioned methods are deprecated. Since the Dolphin Platform provides its own Garbage Collector beans can be automatically removed by the bean manager once they are not referenced in the model layer anymore. The deprecated methods will be removed in a future release.

7.8. The Dolphin Platform garbage collection

The Dolphin Platform contains a garbage collection on the server that will automatically remove all bean instances from the remoting layer that aren’t referenced anymore by other beans. Internally the garbage collection is working by reference counting.

7.9. How to work with the Model API

To get a better overview of the API that helps you to define presentation models in Dolphin Platform we added a small example. Let’s say we have the following view that can be part of a photo library app:

model example 1
Figure 14. Example Application

In this view, we have several elements that need a data model. If the data of this app is stored on a server the data model must be shared between the client and the server. When having a look at the screen we can see 3 different elements that need data from the data model to visualize itself or provide user interaction:

  • The title of the screen needs a String as its content. We can display the title of a photo album or an internationalized string.

  • The slider that defines a value. Let’s imagine that the interaction with the slider changes the size of the pictures in the main area. Maybe the last value of the slider should be stored on the server to automatically save user preferences

  • All pictures in the main area. As you can see each card in this area contains an image and maybe a badge in the top right corner. A badge element in the top right corner visualizes if the photo is flagged.

Based on this definition we would create a presentation model that might look like this one:

model example 2
Figure 15. The presentation model

When defining such a model in JavaFX, you can use the cool property API and the observable collections that are part of JavaFX. Modern JavaScript frameworks like AngularJS or Polymer provide a similar behavior and therefore we decided to offer the same benefits when defining models with the Dolphin Platform. In Dolphin Platform you work with properties and observable collections, too. Therefore it’s really easy to define a hierarchical model for your view. A model for the shown view might look like this:

@DolphinBean
public class PhotoOverviewModel {

  private Property<String> title;

  private Property<Double> sliderValue;

  private ObservableList<PhotoModel> photos;

  //getter & setter

}

@DolphinBean
public class PhotoModel {

  private Property<String> imageUrl;

  private Property<Boolean> flagged;

  //getter & setter

}

All properties and collections in the Dolphin Platform are observable and therefore it’s quite easy to observe them on the client and the server:

myModel.getTitleProperty().onChange(e -> System.out.println("New title: " + e.getNewValue()));

For all client APIs we support first class support for the Dolphin Platform properties. When working with JavaFX for example it’s quite easy and intuitive to bind a synchronized Dolphin Platform property to a JavaFX property:

FXBinder.bind(booleanJavaFXProperty).bidirectionalTo(booleanDolphinProperty);

On JavaScript clients the handling is even more elegant as you can bind the Dolphin Platform model directly in HTML.

The main benefit of this concept is that you can use the same model classes on the server and the client. Because the model will automatically be synchronized between the view and the server controller it feels like you work with the same instance. By doing so you can simply bind a string property to a textfield in the view and observe it’s value on the server. The change events will automatically be fired on the server when you start typing in the textfield.

7.10. Property Binding

The Dolphin Platform provides an easy way to create a bidirectional binding between 2 properties of the same generic type. Currently the binding API only supports properties that are defined in the same client session. If you want to sync properties on several clients the event bus is currently the best way to do this. To create a binding between 2 properties in the same client session you need the PropertyBinder class. An instance of this class can simply be injected in any controller:

@DolphinController
public class MyController {

    @Inject
    private PropertyBinder binder;

}

All bindings are defined by qualifiers that are represented by the Qualifier class. For a new binding you need to define a Qualifier instance that defines the generic type of the properties that should be bound. Since you can reuse the Qualifier instance for all bindings of that type it’s best practice to create a static instance:

public interface MyConstants {

    public final static Qualifier<String> userNameQualifier = Qualifier<String>.create();

}

Once you have the PropertyBinder instance and the Qualifier you can start defining bindings. To do so you define the same qualifier for all properties that should be bound:

propertyBinder.bind(model.userNameProperty(), MyConstants.userNameQualifier);

By using the same qualifier in several controller classes you can simply bind properties in a client scope without doing manual updates.

8. Controller API

When using Dolphin Platform the frontend of an application is split into several views. For each view instance in the client a controller instance is managed in the server.

mvc1
Figure 16. Views and Controllers
The public API of the server layer is defined in the dolphin-platform-server module. The JavaDoc of the module can be found here

8.1. Defining a controller

A controller type is defined as a Java class on the server. This is done with the @DolphinController annotation. Each controller class on the server must be annotated by using this annotation to define the class as a Dolphin Platform controller:

@DolphinController
public class MyViewController {

    //...

}

Each controller type / class is marked by an unique identifier. If developers don’t specify an identifier on their own the full name of the controller class is used. In the given example the controller would have the identifier com.company.app.controller.MyViewController (if it’s part of the com.company.app.controller package). In general it’s a better to specify an identifier for the controller class since refactoring (like moving the controller class in a different package) can change its identifier. A custom identifier for a controller can simply be defined by using the @DolphinController annotation:

@DolphinController("ViewController1")
public class MyViewController {

    //...

}

In this example the controller has the unique identifier ViewController1. When defining custom names for the controllers it’s important to always specify a controller name that is unique in the whole application. Since the identifier is used on the client to access a specific controller type it’s best practice to define the controller names as constants in a module that is shared between client and server (both depend on the module).

common
Figure 17. Dependencies of a common module

All the constants can easily be specified in an interface, for example:

public interface ControllerConstants {

    public static final String CONTROLLER1_NAME = "MyViewController";

    public static final String CONTROLLER2_NAME = "ViewController2";

}

By doing so the constants can easily be used to specify the identifier of a controller:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    //...

}

8.2. Defining the model

Since the view and the controller share a model it’s important that the controller knows about the model. To do so the model type must be specified in the controller class. Whenever a controller will be created the model will automatically be injected in the controller. To define the model in the controller Dolphin Platform provides the @DolphinModel annotation. By using this annotation the related model type can be specified in a controller and simply injected at runtime:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

}

How a model can be specified and how it can be used on the client and the server can be found in the documentation of the model API.

8.3. Controller actions

Actions are endpoints of the controller that can be triggered by the view. For example this can happen when a user clicks a button in the client. This button action will then trigger an action in the server controller instance that maybe stores some date in the database or refreshes the model.

action
Figure 18. Calling an action

A controller action is defined as a method in the controller that is annotated by @DolphinAction:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @DolphinAction
    public void onAction() {
        System.out.println("Action was triggered!");
    }

}

Because the action will be triggered by the client it must be specified by an identifier. This identifier must be unique in the given controller type. By default the name of the method will be used but as described for the @DolphinController annotation it’s best practice to specify a custom constant name to avoid refactoring issues:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @DolphinAction(ControllerConstants.ACTION1_NAME)
    public void onAction() {
        System.out.println("Action was triggered!");
    }

}

Sometimes it’s necessary to submit parameters to the server action. Like controllers and actions parameters must be specified by an identifier. To define the identifier for a parameter of the action Dolphin Platform provides the @Param annotation that must be added to each parameter of the action method:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @DolphinAction(ControllerConstants.ACTION1_NAME)
    public void onAction(@Param("id") String id) {
        System.out.println("Action was triggered for id " + id);
    }

}

Even if in this example only one parameter is defined the Dolphin Platform actions support any count of parameters. Here it’s only important that each parameter has a unique identifier in the context of the action and that the parameters are of a supported type. Currently Dolphin Platform supports the following types for parameters: Integer, Long, Double, Float, Boolean, String. It’s best practice to not use primitives here since null values are always supported and can end in an Exception since Java autoboxing can’t convert null to a primitive number or boolean. Some additional common types like Date will be added in future releases.

8.4. Benefits of managed controllers

Since a controller is always created and managed by the underlying platform (like Spring or JavaEE) a developer doesn’t need to care about controller and model creation. The last sample already defines a full useable controller that will automatically be created (and a new model instance will be injected) whenever the related view is created on the client. Next to this the controller offers all the benefit that a managed beans offers based on the underlying platform. Based on this it’s no problem to use CDI if Dolphin Platform is used in a JavaEE environment. In addition default annotations like @PostConstruct and @PreDestroy are supported. The following example shows how such a controller could look like:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @Inject
    private MyService service;

    @PostContruct
    public void init() {
        System.out.println("Hello");
    }

    @PreDestroy
    public void shutdown() {
        System.out.println("Bye, bye");
    }

}

It’s important to know that even if a controller instance is a managed object it can not be injected in any other managed bean. This belongs on some restrictions in the Dolphin Platform Architecture: Even if the lifecycle of a controller is well defined it’s possible to have several instances of the same controller. Let’s say your front-end contains a tabbed pane and you have 2 instances of the same view in this pane. By doing so it’s not possible to specify what controller instance should be injected in bean. For a future release of Dolphin Platform it’s planned to define parent-child-relations between controller instances. Currently the Dolphin Platform event bus should be used for communication between controllers and / or additional services.

8.5. Definition of the client scope

Dolphin Platform provides an additional custom Scope called ClientScope. This scope is currently implemented for JavaEE and Spring and it’s defined as a scope that is "lower than the session scope". This scope is important if you want to create web applications, for example. Since all the tabs of a browser share the same session it’s hard to define data that is only related to one tab in the browser. In that case the lifecycle of a client scope is bound to a tab in the browser and ends when the tab will be closed.

clientscope
Figure 19. Definition of the client scope

For both JavaEE and Spring a @ClientScoped annotation is defined that can be used to give any bean the specific scope:

@ClientScoped
public class MyLocalStoreService {

    private List<String> values = new ArrayList();

    public void add(String val) {
        values.add(val);
    }

}

The given service can now simply be injected in any controller:

@DolphinController(ControllerConstants.CONTROLLER1_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @Inject
    private MyLocalStoreService service;

}

Internally the client scope is defined by an unique identifier that is shared between client and server with each request. Based on this the scope only "lives" inside a Dolphin Platform request roundtrip. At the moment this means that beans that are defined for the client scope can only be injected in Dolphin Platform controller classes or classes that are (transitive) injected in controllers. For a future release we plan to support general HTTP endpoints that can be called from a client and "live" in the client scope. This will be useful to provide specific content like images or files to the client.

8.6. Injection of Dolphin Platform specific services

The Dolphin Platform provide several services that can be injected in any managed bean. Since the Dolphin Platform controllers are managed beans and support dependency injection the Dolphin Platform specific services can be injected in any controller.

8.6.1. The bean manager

The Dolphin Platform bean manager provides methods to manage the model layer. More information about the model layer and how to use the bean manager can be found in the Model API chapter. The bean manager is defined by the com.canoo.dolphin.BeanManager interface. Instances are managed objects in the client session scope.

Example:

@DolphinController
public class MyViewController {

    @Inject
    private BeanManager beanManager;

     //...

}

8.6.2. The event bus

The Dolphin Platform provides its own event bus that can be used to provide interaction between several sessions or to access the Dolphin Platform from a background thread or endpoint. The event bus is defined by the com.canoo.dolphin.server.event.DolphinEventBus interface. Instances are managed objects in the application scope.

Example:

@DolphinController
public class MyViewController {

    @Inject
    private DolphinEventBus eventBus;

     //...

}

8.6.3. The Dolphin Platform session

The Dolphin Platform offers a service to interact with the client session. To do so the com.canoo.dolphin.server.DolphinSession can be injected. The interface provides similar functionallity as the javax.servlet.http.HttpSession interface does for the http session. Instances are managed objects in the client session scope.

Example:

@DolphinController
public class MyViewController {

    @Inject
    private DolphinSession session;

     //...

}

8.6.4. The property binder

The Dolphin Platform provides a way to create binding between properties in the model layer. This bindings can be created by injecting the com.canoo.dolphin.server.binding.PropertyBinder interface. A more detailed description of bindings can be found in the Model API chapter. Instances are managed objects in the client session scope.

Example:

@DolphinController
public class MyViewController {

    @Inject
    private PropertyBinder binder;

     //...

}

8.7. React on events on the server

The Dolphin Platform provides 2 server side APIs that can be used to react on events on the server. When talking about events this can mean several different scenarios: A server side controller instance maybe needs to publish some data to other controller instances or a REST endpoint on the server needs to call the functionallity of controller instance for one or many clients. Another common scenario is the usageage of background threads and tasks on the server that are triggered by a Dolphin Platform controller instance and will notify the controller about a calculated behavior in any future.

One approach to handle this use cases is by using the Dolphin Platform event bus that implements the publish / subscribe pattern and is automatically provided as a managed bean in Spring and JavaEE. More about the functionallity and the API of the event bus can be found in the chapter about the Dolphin Platform event bus.

TODO way 2: session.runLater…​

9. The Event Bus

Dolphin Platform provides an event bus that can be used to send events to client sessions on the server. By doing so it’s quite easy to create dynamic application with the Dolphin Platform that automatically propagate information to several clients. A good example for such an application is a messaging app in that all messages that are typed by a user will be directly visible in the client instances of several other users.

9.1. General overview of the event bus

The event bus of Dolphin Platform can only be used on the server. It’s part of the basic server module (dolphin-platform-server`) and therefore no special dependencies need to be added to a Dolphin Platform application to use the event bus. The event bus implements the publish-subscribe-pattern and can be used to send events to all subscribers of a specific topic.

event bus1
Figure 20. Overview of the event bus

To do so a Dolphin Platform controller (or other beans that live in the Dolphin Platform client scope) can be registered as subscriber for a specific topic. Any managed bean of an application can send custom events to a defined topic. By doing so all subscribers will be notified and receive the event. The event can contain custom data which allows a developer to send informations to several controller instances.

event bus2
Figure 21. Overview of the event bus

9.2. Defining a topic

As already mentioned the Dolphin Platform event bus is topic based. This means that all event must be published to a specified topic and all receivers are always subscribed to a topic. In Dolphin Platform the topic is not simply defined by a String but by the Topic<E> class. By doing so the topic can already define what types of data can be send to this topic based on the generic identifier. Since topics are used at the publisher and the receiver site it’s best practice to define them as constants. Here is an example how topics can be specified:

public interface EventTopics {

    public static final Topic<String> DESCRIPTION_ADDED_TOPIC = new Topic();

    public static final Topic<MyCustomBean> DATA_CHANGED_TOPIC = new Topic();

}

In this example only items of type MyCustomBean can be send to the DATA_CHANGED_TOPIC topic. Internally the topic uses an unique identifier for specification. A developer can define it’s own identifier by simply passing a String to the constructor of the Topic. This is useful for debugging since you can see a readable name of the topic but in that case a developer must check that all topics have unique identifiers:

public interface EventTopics {

    public static final Topic<String> DESCRIPTION_ADDED_TOPIC = new Topic("descriptionAddedTopic");

    public static final Topic<MyCustomBean> DATA_CHANGED_TOPIC = new Topic("dataChangedTopic");

}

9.3. Sending an event

The Dolphin Platform provides an injectable DolphinEventBus bean that must be used to send an event to a topic. By doing so each bean that is managed by the underlying container (like Spring or JavaEE) can publish events. To publish an event you always need to define the topic to that the event should be published. In addition data can be sent as part of the event message. Publishing an event is a non-blocking operation. Here is an example how a event can be sent to a specific topic:

@ApplicationScoped
public class MyLocalStoreService {

    @Inject
    private DolphinEventBus eventBus;

    public void addDescription(String desc) {
        eventBus.publish(EventTopics.DESCRIPTION_ADDED_TOPIC, desc);
    }

    public void updateData(MyCustomBean data) {
        eventBus.publish(EventTopics.DATA_CHANGED_TOPIC, data);
    }

}

In this example the previously created topics are used. As you can see data of the type that is defined in the generic identifier of the Topic<E> instances is sent to the topic. Sending another type of data (an instance of a different class) will end in a compiler exception.

9.4. Receiving events

To receive events of a specific topic the receiver must subscribe itself to the topic. This is done by adding a message listener to the topic. This listener will be called whenever a new event was published for the given topic. Since the event handling is always done in a Dolphin Platform communication roundtrip an event receiver must be a Dolphin Platform controller or a managed bean that is defined by the client scope. The following controller class shows how a subscription can be done:

@DolphinController(ControllerConstants.CONTROLLER_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @Inject
    private DolphinEventBus eventBus;

    @PostContruct
    public void init() {
        eventBus.subscribe(EventTopics.DATA_CHANGED_TOPIC, e -> {
                System.out.println("Data changed: " e.getData().getName();
        });

        eventBus.subscribe(EventTopics.DESCRIPTION_ADDED_TOPIC, e -> {
                model.getDescriptions().add(e.getData());
        });
    }
}

As you can see the listener can simply access the data that was sent to the topic by using event.getData(). Thanks to the typesafety of the event bus the data doesn' t need to be casted to the needed type. In the example you can see one big benefit of the event bus: Since all Dolphin Platform model instances will automatically be shared and synchronized between client and server the changes of the model that are done in the subscription of the DESCRIPTION_ADDED_TOPIC topic will automatically be snychronized with the client and can trigger an update of the UI. By doing so it’s quite easy to update several clients dynamically based on events.

The DolphinEventBus doesn’t provide a method to unregister subscriptions. Instead of this the subscribe(…​) methods return an object of type Subscription. This object can easily be used to unsubscribe from a topic. By doing so the following code defines a controller that can be dynamically subscribed and unsubscribed from a topic:

@DolphinController(ControllerConstants.CONTROLLER_NAME)
public class MyViewController {

    @DolphinModel
    private MyModel model;

    @Inject
    private DolphinEventBus eventBus;

    private Subscription subscription;

    @DolphinAction
    public void activateLiveUpdates() {
        if(subscription != null) {
            subscription = eventBus.subscribe(EventTopics.DESCRIPTION_ADDED_TOPIC, e -> {
                    model.getDescriptions().add(e.getData());
            });
        }
    }

    @DolphinAction
    public void deactivateLiveUpdates() {
        if(subscription != null) {
            subscription.unsubscribe();
            subscription = null;
        }
    }
}

9.5. Session filtering for events

When publishing an event to the event bus a filter can be defined. By doing so the filter will decide to what client sessions the event should be published. By doing so it’s quite easy to send an event only to the current client session or to all client sessions in the current HTTP session (this will infect all tabs in a browser, for example).

To do so an implementation of the EventSessionFilter interface must be defined. This filter will check all client session that are subscriped to the given topic and can avoid that the event will be published tonthe given client session.

A basic implementation of the EventSessionFilter interface that will allow only one client session to receive events might look like this:

public class OneClientSessionFilter implements EventSessionFilter {

    private final String clientSessionId;

    public OneClientSessionFilter(final String clientSessionId) {
        this.clientSessionId = clientSessionId;
    }

    public boolean shouldHandleEvent(String sessionId) {
        return clientSessionId.equals(sessionId);
    }
}

Such an event filter can no be used when an event will be published. To do so the Dolphin Platform event bus provides a method to publish an event to a specific topic by using such a filter:

eventBus.publish(myTopic, myData, myFilter);

9.6. Use-cases for the event bus

There are several use cases and scenarios that can simply be developed by using the Open Dolphin event bus. TODO

9.7. Using a distributed event bus

Since the Dolphin Platform can be installed in a cluster by using sticky sessions the default implementation of the event bus will not work in such a scenario. In that case a distributed event bus is needed. The Dolphin Platform provides the optional module dolphin-platform-distributed-eventbus that adds such an alternative event bus that is based on Hazelcast (TODO: Link). Once this module is added to a server application the Dolphin Platform can be configured to use the distributed event bus instead of the default implementation. A developer do not need to change any source code in that case since the interface of the Dolphin Platform event bus will be still the same. Only the injected implementation is a different one. To activate the distributed event bus the following configuration property must be set in the dolphin.properties file (or by using Spring configuration):

eventbusType=distributed

Once this configuration is set Hazelcast will be used internally to provide a distributed event bus. Since each event bus instance (1 by server runtime) is defined as a Hazelcast client a remote Hazelcast server must be running. The Dolphin Platform event bus will automatically connect the internal Hazelcast client to the Hazelcast server and use it to distribute events to several server runtimes.

9.7.1. Hazelcast configuration

If the distributed event bus is used a Hazelcast server is needed. All application instances that use the distributed event bus will automatically create a connection to that server. Such a connection must be configured. To do so a developer can define the following parameters in the dolphin.properties file:

Table 1. Table title
Name Description Supported values Default value

hazelcast.server.name

Defines the host name of the Hazelcast server.

any string that defines the host name of the hazelcast server

localhost

hazelcast.server.port

Defines the port of the Hazelcast server.

a valid port number

5701

hazelcast.group.name

Defines the name of the Hazelcast group that should be used

a String

micro-landscape

Currently this values can only be specified in the dolphin.properties file. The Dolphin Platform provides an integration in Spring configuration to configure the common properties of the Dolphin Platform. This is currently not possible for the Hazelcast integration (see this issue). In addtion a already defined Hazelcast client that is provided by Spring or JavaEE as a managed bean can not be used (see this issue).

10. JavaEE Integration

Even if mostly all of the public server APIs of the Dolphin Platform don’t depend on the underlying container implementation a special dependency must be added to a project that is created as a JavaEE based application.

javaee dependency
Figure 22. Dependencies for a JavaEE based server

The dolphin-platform-server-javaee dependency adds the Dolphin Platform server API and JavaEE APIs as transitive dependencies. For a maven based project a minimum pom.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.canoo.sample</groupId>
    <artifactId>server</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.canoo.dolphin-platform</groupId>
            <artifactId>dolphin-platform-server-javaee</artifactId>
            <version>{dolphin-platform-version}</version>
        </dependency>
    </dependencies>

</project>

In most cases you will define the model classes in a separate module that is shared between client and server and server must be added as a second dependency to your application. Once you start the server application the Dolphin Platform bootstrap will automatically be initialized.

10.1. Configuration of the application

The Dolphin Platform part of the application can be configured a dolphin.properties file. This file is not JavaEE specific and therefore it’s described in the general server part.

10.2. Next steps

When developing a Dolphin Platform application with JavaEE you can use all the benefits of both worlds. This means that you can simply define CDI beans and inject them in Dolphin Platform controllers. A general overview of the Dolphin Platform server API can be found here.

10.3. Using the jumpstart

Dolphin Platform provides several Maven archetypes to quickly create a full client server application. By doing so you only need an installed Maven on your system and can run the following command on a shell:

mvn archetype:generate -Dfilter=com.canoo.dolphin-platform:

This will start a Maven build that creates a full client-server Maven project for you. A full documentation of the jumpstart can be found here.

11. Spring Integration

Even if mostly all of the public server APIs of the Dolphin Platform don’t depend on the underlying container implementation a special dependency must be added to a project that is created as a Spring (Boot) application.

spring dependency
Figure 23. Dependencies for a Spring based server

The dolphin-platform-server-spring dependency adds the Dolphin Platform server API and Spring as transitive dependencies. For a maven based project a minimum pom.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.canoo.sample</groupId>
    <artifactId>server</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.canoo.dolphin-platform</groupId>
            <artifactId>dolphin-platform-server-spring</artifactId>
            <version>{dolphin-platform-version}</version>
        </dependency>
    </dependencies>

</project>

In most cases you will define the model classes in a separate module that is shared between client and server and server must be added as a second dependency to your application.

11.1. Defining the main class of the application

To start your Spring Boot based server a class with a main method need be be defined. In the main method Spring must be started. In addition the Dolphin Platform bootstrap must be configured and initalialized. To do so Dolphin Platform provides the @DolphinPlatformApplication annotation that must be added to the main class to automatically load all needed configurations and trigger the Dolphin Platform Bootstrap. Based on the annotation a main class will always looks like this:

@DolphinPlatformApplication
public class ServerMain {

    public static void main(String... args) {
        SpringApplication.run(ServerMain.class, args);
    }

}

11.2. Configuration of the application

The server application can be configured by using all the possibilities that Spring offers for configuration. In addition a dolphin.properties file can be used to define a configuration for the Dolphin Platform part of the server. This file is not Spring specific and therefore it’s described in the general server part.

11.3. Provide configuration of the Dolphin Platform by Spring Boot

The default application.properties file for Spring Boot configuration (or any other valid Spring Boot configuration file or provider) can be used to configure the Dolphin Platform. In this case a developer do not need to create a dolphin.properties file. All configurations can easily be defined in the application.properties. In that case the dolphinPlatform. prefix is needed for all configuration properties of the Dolphin Platform.

To following table gives an overview of all Dolphin Platform related properties that can be configured by using Spring configuration (like application.properties file):

Name Description Supported values Default value

dolphinPlatform.useCrossSiteOriginFilter

Boolean property that defines if the HTTP filter for the cross site origin policy should be used. By default the filter will be used.

true, false

true

dolphinPlatform.useSessionInvalidationServlet

Boolean property that defines if the servlet for session validation should be used. By default the servlet will not be used. We plan to remove the servlet completelly in a future release and this property is just for a fallback if not using the servelt will end in issues.

true, false

false

dolphinPlatform.dolphinPlatformServletMapping

A string based property that defines the server URL endpoint for the Dolphin Platform.

any String that defines an URL mapping

/dolphin

dolphinPlatform.sessionTimeout

A int that defines the http session timeout in seconds

any integer value

900

dolphinPlatform.idFilterUrlMappings

A list that contains all endpoint that will be filtered by the id filter. This means that all requests for this endpoints must provide a client id in its header (Header name = dolphin_platform_intern_dolphinClientId).

a comma seperated list

/dolphin

dolphinPlatform.maxClientsPerSession

Defines how many clients can exist in a http session. By using the client scope you can have several clients that share a session but don’t use the same data (by using the client scope / dolphin session). By doing so several tabs in a browser can handle its own clients

any positive integer value

10

dolphinPlatform.rootPackageForClasspathScan

Defines the root package that should be used to scan the classpath for Dolphin Platform controllers. By default all classes in the classpath will be scanned but this might take some time in big projects. By defining a root package all classes that are located in this package or any subpackage of the root package will be scanned.

a String that matches the java package structure like "com.canoo" or a empty string if the complete classpath should be scanned.

NULL (scan complete classpath)

dolphinPlatform.mBeanRegistration

Defines if the Dolphin Platform will register custom MBeans to provide information about the platform and the sessions

true, false

true

dolphinPlatform.useGc

By activating the garbage collection the Dolphin Platform checks internally if Dolphin beans are referenced (for example in a Dolphin Bean hierarchy). If not this beans will automatically be removed on the server and the client. This is an unstable feature at the moment and should only be activated for tests.

true, false

true

dolphinPlatform.maxPollTime

Defines the timeout of the Dolphin Platform polling mechanism that is used for the event bus and for the session.runLater call.

any long

5000

active

Defines if the Dolphin Platform should be bootstraped at the start of the server

true, false

true

11.4. Next steps

When developing a Dolphin Platform application with Spring you can use all the benefits of both worlds. This means that you can simply define Spring components and service and inject them in Dolphin Platform controllers. A general overview of the Dolphin Platform server API can be found here.

11.5. Using the jumpstart

Dolphin Platform provides several Maven archetypes to quickly create a full client server application. By doing so you only need an installed Maven on your system and can run the following command on a shell:

mvn archetype:generate -Dfilter=com.canoo.dolphin-platform:

This will start a Maven build that creates a full client-server Maven project for you. A full documentation of the jumpstart can be found here.

12. Server configuration

The Dolphin Platform can simply be configured by a property file. To configure the server part of the Dolphin Platform a dolphin.properties-file must be created in the META-INF folder. In a Maven based project the file would be defined as project/src/main/resources/META-INF/dolphin.properties. This file must be defined as a regular Java property file. Here is an example how such a file might look:

openDolphinLogLevel=OFF
useCrossSiteOriginFilter=true

12.1. Supported properties

Currently the following properties are supported by the Dolphin Platform:

Table 2. Table title
Name Description Supported values Default value

openDolphinLogLevel

Defines the logging level for the remoting layer.

off, severe, warning, info, config, fine, finer, finest, all

severe

useCrossSiteOriginFilter

Boolean property that defines if the HTTP filter for the cross site origin policy should be used. By default the filter will be used.

true, false

true

accessControlAllowHeaders

List of String headers which needs to be allowed with Access-Control-Allow-Headers for the cross site origin policy. See here - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Content-Type, x-requested-with, origin, authorization, accept, client-security-token

Content-Type, x-requested-with, origin, authorization, accept, client-security-token

accessControlAllowMethods

List of Methods which needs to be allowed with Access-Control-Allow-Methods for the cross site origin policy. See here - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

*

*

accessControlAllowCredentials

Boolean property that allow Access-Control-Allow-Credentials for the cross site origin policy. are allowed. See here - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

true, false

true

accessControlMaxAge

Long property that defines Access-Control-Max-Age for the cross site origin policy. are allowed. See here - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

timestamp Long value

86400

useSessionInvalidationServlet

Boolean property that defines if the servlet for session validation should be used. By default the servlet will not be used. We plan to remove the servlet completelly in a future release and this property is just for a fallback if not using the servelt will end in issues.

true, false

false

servletMapping

A string based property that defines the server URL endpoint for the Dolphin Platform.

any String that defines an URL mapping

/dolphin

sessionTimeout

A int that defines the http session timeout in seconds

any integer value

900

idFilterUrlMappings

A list that contains all endpoint that will be filtered by the id filter. This means that all requests for this endpoints must provide a client id in its header (Header name = dolphin_platform_intern_dolphinClientId).

a comma seperated list

/dolphin

maxClientsPerSession

Defines how many clients can exist in a http session. By using the client scope you can have several clients that share a session but don’t use the same data (by using the client scope / dolphin session). By doing so several tabs in a browser can handle its own clients

any positive integer value

10

rootPackageForClasspathScan

Defines the root package that should be used to scan the classpath for Dolphin Platform controllers. By default all classes in the classpath will be scanned but this might take some time in big projects. By defining a root package all classes that are located in this package or any subpackage of the root package will be scanned.

a String that matches the java package structure like "com.canoo" or a empty string if the complete classpath should be scanned.

NULL (scan complete classpath)

mBeanRegistration

Defines if the Dolphin Platform will register custom MBeans to provide information about the platform and the sessions

true, false

true

garbageCollectionActive

By activating the garbage collection the Dolphin Platform checks internally if Dolphin beans are referenced (for example in a Dolphin Bean hierarchy). If not this beans will automatically be removed on the server and the client. This is an unstable feature at the moment and should only be activated for tests.

true, false

true

maxPollTime

Defines the timeout of the Dolphin Platform polling mechanism that is used for the event bus and for the session.runLater call.

any long

5000

active

Defines if the Dolphin Platform should be bootstraped at the start of the server

true, false

true

13. JavaFX client API

When creating a JavaFX client you will normally use FXML to create your view. Next to the FXML file you will have a view controller to bind all the properties of the view and attach action handlers.

javafx 1
Figure 24. Usage of FXML

When talking about the Dolphin Platform, this view controller is the perfect point to bind the view to the synchronized model and the server side controller. Therefore we call this class the "Binder". There are several ways how you can define such a binding but the most easy one is to extend the AbstractViewBinder class that is part of the Dolphin Platform JavaFX library. This class already implements the complete lifecycle of the view and you can simply bind the synchronized presentation model to your view properties. Here is a small example for a view that contains only one textfield and a button:

public class MyBinder extends AbstractViewBinder<MyModel> {

  @FXML
  private Button button;

  @FXML
  private TextField textfield;

  public MyBinder(ClientContext clientContext) {
    super(clientContext, ControllerConstants.NAME);
  }

  @Override
  public void init() {
    FXBinder.bind(textfield.textProperty()).bidirectionalTo(getModel().nameProperty());
    button.setOnAction(e -> invoke(ControllerConstants.SAVE));
  }

  @Override
  public Node getRootNode() {
    return null;
  }

}

Once the view binder is instantiated, the server controller and the model will automatically be created on the server. Since the model will be synchronized all the time between client and server you don’t need to create it on the client. After this initialization is done the init() method of the binder will be called. With this we bind the name property that is part of the synchronized model to the text property of the textfield. In addition, we define an action handler for the button. When the button is pressed an action in the server side controller should be called. To do so the abstract binder provides the invoke(String name) method that triggers actions on the server controller. In this specific case the server controller might look like this:

@DolphinController(ControllerConstants.NAME)
public class Controller {

  @Inject
  private PersistenceService persistence;

  @DolphinModel
  private MyModel model;

  @DolphinAction(ControllerConstants.SAVE)
  public void save() {
    persistence.insert(model.getName());
  }

}

As you can see we never send any data to the server. Since the model will be automatically synchronized, we can directly store the name string in the model to the database. The Dolphin Platform guarantees that the model will be the same as it’s on the client when pressing the button.

javafx 2
Figure 25. MVC and Sync

Another nice benefit that you might notice is that even if we have communication between the server and the client we don’t need to handle several threads. The Dolphin Platform handles all the concurrency and handles all actions in the right thread. Therefore, the binding between JavaFX properties and Dolphin properties will automatically be handled on the JavaFX application thread.

14. The AngularJS client integration

Based on the generic JavaScript client API the Dolphin Platform provides a client API for AngularJS 1.x (TODO: Link to Angular). By using this API it’s quite easy to create Dolphin Platform based MVC groups based on AngularJS controllers.

14.1. Adding the Dolphin Platform client API to an AngularJS project

As all JavaScript based modules of the Dolphin Platform the AngularJS integration module is published by bower (TODO: LINK BOWER). Based on this the easiest way to add the module to a JavaScript project can be reached by using bower for the dependency management of the project. For such a project the module can easily be added as a dependency by executing the following command:

$ bower install --save dolphin-platform-angularjs

This command will automatically add the dolphin-platform-angularjs module and all its dependecies to the project.

The Dolphin Platform Jumpstart creates next to several other client implementations an AngularJS based client for the project that is created by the jumpstart. This can easily be used as a template for a custom Dolphin Platform client that is based on AngularJS

Since the JavaScript modules of the Dolphin Platform always provide a compiled JavaScript file that contains the module and all needed dependencies you only need to add one JavaScript file (dolphin-platform-angular.js) to your project. Based on this the sourcecode of a minimal html page that will use AngularJS and the DolphinPlatform might look like this:

<!DOCTYPE html>
<html lang="en">
<head>

    <!-- Angular  -->
    <script src="bower_components/angular/angular.min.js"></script>

    <!-- Dolphin-Platform  -->
    <script src="bower_components/dolphin-platform-angularjs/dist/dolphin-platform-angular.min.js"></script>

</head>

<body>
    READY!
</body>

14.2. Configuration of the Dolphin Platform with Angular

To use the Dolphin Platform API in AngularJS controllers the DolphinPlatform module must be added to the AngularJS application. In addition a Dolphin Platform client configuration must be provided. The Dolphin Platform integration for AngularJS provides a $dolphinConfigProvider that can be injected in a config(…​) method for an AngularJS application. This method is used to configure the connection to the server that will be used and automatically injected by AngularJS.

The following code snippet shows an example how such a configuration can be provided:

<script>
    var app = angular.module("MyApp", ["DolphinPlatform"]).config(function ($dolphinConfigProvider) {
        $dolphinConfigProvider.configure({
            DOLPHIN_URL: "http://localhost:8080/dolphin"
        });
    });
</script>

Based on this the minimal html source code for an page that creates an AngularJS application with Dolphin Platform support and configuration might look like this:

<!DOCTYPE html>
<html lang="en">
<head>

    <!-- Angular  -->
    <script src="bower_components/angular/angular.min.js"></script>

    <!-- Dolphin-Platform  -->
    <script src="bower_components/dolphin-platform-angularjs/dist/dolphin-platform-angular.min.js"></script>

</head>

<body>

    <!-- Definition of AngularJS application and Dolphin Platform configuration -->
    <script>
        var app = angular.module("MyApp", ["DolphinPlatform"]).config(function ($dolphinConfigProvider) {
            $dolphinConfigProvider.configure({
                DOLPHIN_URL: "http://localhost:8080/dolphin"
            });
        });
    </script>

    READY!
</body>

14.3. Using the Dolphin Platform with Angular

The best practice to use the Dolphin Platform in an AngularJS based application is the useage of Dolphin Platform controller proxies directly in AngularJS controllers. By doing so each AngularJS controller instance will be connected to a Dolphin Platform controller instance on the server. To create a Dolphin Platform controller proxy in an AngularJS controller the client context of Dolphin Platform can be injected in any AngularJS controller.

The following code snippet shows an AngularJS controller that creates a Dolphin Platform controller proxy:

app.controller("myAngularComponentName", function ($scope, clientContext) {
        clientContext.createController($scope, 'myDolphinPlatformControllerName');
});

The call of the createController(…​) method returns a Promise that should be used to define the internal logic of the AngularJS controller and the interoperation with the Dolphin Platform controller.

A developer do not need to take care of the destruction of the Dolphin Platform controller on the server. The AngularJS controller will automatically start the destruction once the view component is removed from the HTML DOM.

14.3.1. Calling actions on the Dolphin Platform controller

The controller proxy instance that is created can easily be used to invoke Dolphin Platform actions (that are defined by @DolphinAction as descripted in the chapter about the controller API). To do so the controller proxy provides an invoke(…​) method.

Let’s say we have an AngularJS view that is defined by the following html code:

<div ng-controller="MyController">
    <button type="button" ng-click="save()">save</button>
    <button type="button" ng-click="delete()">delete</button>
</div>

This view snippet will be bound to an AngularJS controller with the defined name MyController. In addition 2 methods (save() and delete()) can be called by the view. This 2 methods must be provided by the AngularJS scope of the controller. The following implementation of the AngularJS controller assumes that the Dolphin Platform controller has 2 Dolphin Platform actions defined. This actions will be invoked by the save() and delete() methods.

app.controller("MyController", function ($scope, clientContext) {
        clientContext.createController($scope, 'MyServerController').then(function (controllerProxy) {

            $scope.save = function () {
                controllerProxy.invoke('save');
            };

            $scope.delete = function () {
                controllerProxy.invoke('delete');
            };
        }
});

As all the other Dolphin Platform client APIs the AngularJS related one provides the functionality to pass parameters to the server side action. In all JavaScript based client APIs the parameters can be specified as a JSON object. The following snippet shows how a Dolphin Platform action can be called with parameters:

app.controller("MyController", function ($scope, clientContext) {
        clientContext.createController($scope, 'MyServerController').then(function (controllerProxy) {

            $scope.sayHello = function () {
                controllerProxy.invoke('sayHello', {'message': 'Hello from the AngularJS client'});
            };
        }
});

The description about the support of custom parameters in a server side Dolphin Platform action can be found in the chapter about the controller API.

14.3.2. Make use of the Dolphin Platform model

TODO

15. Security

The Dolphin Platform do not provide its own security concept but can easily be combined with any security framework. To do so the client API of the Dolphin Platform provides access to the http connection. Based on this it’s quite easy to enrich the http requests of the Dolphin Platform with additional information like a JWT token.

15.1. Provide Security in a Java based client

When using a Java client API of the Dolphin Platform (like JavaFX or Android) the configuration provides the functionality to define a custom http connection factory and a http response handler. Based on this a developer can implement any custom security mechanism for the Dolphin Platform.

All http connections that will be used by the Dolphin Platform are created by the HttpURLConnectionFactory instance that is provided by the client configuration. A developer can easily provide a custom implementation of the HttpURLConnectionFactory interface and add this to the client configuration:

HttpURLConnectionFactory myFactory = url -> {
    HttpURLConnection connection = (HttpURLConnection) url.connect();
    connection.setRequestProperty("Security-Header", "My secret token");
    return connection;
}

ClientConfiguration configuration = new JavaFXConfiguration(serverEndpoint);
configuration.setConnectionFactory(myFactory);

Next to this a response handler can be implemented to extract needed information of the server response. A custom response handler will be called after the Dolphin Platform client API has handled the response. To provide such a handler a developer need to provide a custom implementation of the HttpURLConnectionResponseHandler interface:

HttpURLConnectionResponseHandler myhandler = response -> {
    String mySecurityToken = response.getHeaderField("Security-Header");
    MySecurityManager.setToken(mySecurityToken);
}

ClientConfiguration configuration = new JavaFXConfiguration(serverEndpoint);
configuration.setResponseHandler(myhandler);

Next to this the Dolphin Platform internally uses a cookie store to handle the session cookie of the current client session. If a developer needs a specific cookie store or wants to add some custom cookies to the http requests of the Dolphin Platform the cookie store that will be used by the Dolphin Platform client can be defined by the client configuration:

CookieStore mySpecialCookieStore = MySecurityManager.getCookieStore();

ClientConfiguration configuration = new JavaFXConfiguration(serverEndpoint);
configuration.setCookieStore(mySpecialCookieStore);
The Dolphin Platform offers its own implementations of the interfaces and configurations that are described in this chapter. The ClientConfiguration class already provides them as a default configuration. For more information a developer can have a look at DefaultHttpURLConnectionFactory and DefaultHttpURLConnectionResponseHandler.

15.2. Provide Security in a JavaScript based client

The JavaScript client API of the Dolphin Platform provides a simply way to add custom headers to all HTTP(s) requests that will be called the client. As in the Java API this behavior will be defined in the configuration. A cient configuration can contain a list of custom headers as string based JSON members in the JSON configuration. This list must be defined in the headersInfo JSON array as part of the JSON object for configuration.

The following JavaScript snippet shows how 2 custom headers (‘X-Custom-Header’ and ‘Cookie’) and connection parameter ('maxRetry' and 'timeout') are defined. By creating the Dolphin Platform connection that way all requests of the client will contain that headers and connection parameters.

config:{
    headersInfo:{
      ‘X-Custom-Header’ : ‘security-token’,
      ‘Cookie’ : ‘session=xxxxxxxxxxxxxx;’
    },
    connection:{
      'maxRetry': 3,
      'timeout' : 5000
    }
}
 dolphin.connect(url, config);

16. Validation Support

The Dolphin Platform provides the optional dolphin-platform-bean-validation module that adds Java Bean Validation (JSR 303) support to the model layer of the Dolphin Platform. To use the validation support the module must be added as a dependency to your project:

<dependency>
    <groupId>com.canoo.dolphin-platform</groupId>
    <artifactId>dolphin-platform-bean-validation</artifactId>
    <version>{dolphin-platform-version}</version>
</dependency>

If you have defined a common module that contains the model descriptions and is shared between client and server you can simply replace the dolphin-platform-core dependency with dolphin-platform-bean-validation since the validation module adds a transitive dependency to the core module.

validation dependency
Figure 26. Dependencies for the validation module

The dolphin-platform-bean-validation module don’t depend on a JSR-303 implementation. If your application server don’t provide an implementation you need to add for example Hibernate Validation as a dependency. Here is an example for Maven:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.1.3.Final</version>
</dependency>
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.0</version>
</dependency>

If you depend on JavaEE your application container will automatically provide an implementation for you. In that case you don’t need to add a specific implementation as a dependency.

16.1. Using validation constraints

Once the module is added as a dependency the contrains annotations of JSR-303 can be used in Dolphin Platform models. To do so the Property fields of a model bean must be annotated with the contrains annotations. Here is an example that defines a "not null" constrain for a String property:

@DolphinBean
public class MyModel {

    @NotNull
    private Property<String> value;

    public Property<String> valueProperty() {
        return value1;
    }
}

A general description of the Dolphin Platform model API can be found here.

16.2. Validate a model

By using a validator you can now easily validate instances of the model as described in the bean validation documentation or several tutorials. Here is a basic code snippet that creates a validator by hand and validates a Dolphin Platform model:

Configuration<?> validationConf = Validation.byDefaultProvider().configure();

Validator validator = validationConf.buildValidatorFactory().getValidator();

Set<ConstraintViolation<TestBean>> violations = validator.validate(dolphinModel);
if(!violations.isEmpty()) {
    //Handle violations
}

Some platforms provide injection of a Validator instance. In that case you don’t need to create a configuration and validator by hand.

17. Spring controller tests

The Dolphin Platform provides a test framework to write unit tests for Dolphin Platform controllers. For Spring the Dolphin Platform offer some abstract test classes that can be extended to write custom tests for controllers. Tests can be written by using TestNG or JUnit.

17.1. Testing a controller

When writing a unit tests for a controller Spring automatically manages the controller and provides a facade to interact with the controller and test its behavior. To do so the Dolphin Platform contains the interface ControllerUnderTest that provides access to the controller. By using this interface you can call actions on the controller, access the model and destroy the controller. This interface is independent of the container and test framework and therefore it’s used in all controller tests. The concrete test implementation defines hoe instances of the ControllerUnderTest interface will be created but once you have created your instance writing a test is quite easy:

@Test
public void test1 {
    ControllerUnderTest<MyModel> controllerProxy = ... // We will see later how to create it
    MyModel model = controllerProxy.getModel();
    model.setTitle("Any title");
    controllerProxy.invoke(RESET_VALUES);
    Assert.assertEquals(model.getTitle(), "");
    controllerProxy.destroy();
}

As you can see in this small example you can write controller tests like you write the view of a controller. The biggest difference is, that you use a test framework like JUnit or TestNG to define some assertions for the controller or its model.

17.2. Destroying a controller

At the end of each test you should destroy the controller that was tested. The ControllerUnderTest interface provides a destroy() method that must be called to destroy the controller. Like in the real application the controller and its lifecycle is managed by Spring. This means that methods that are annotated with @PostConstruct or @PreDestroy will be called automatically. When calling the destroy() method Spring will destroy the controller and automatically call methods that are annotated with @PreDestroy. In the given example the destroy() method is already called at the end of the test. Test frameworks like JUnit or TestNG provide features to automatically call such a method after each test.

17.3. Proving a testable controller

The Dolphin Platform provides the ControllerTest interface that should be implemented by each test class. This interface provides a method to create new ControllerUnderTest instances. For Spring the Dolphin Platform already contains an implementation of the interface that can easily be used to write tests for your controllers. By using the ControllerTest interface a test method will look like this:

@Test
public void test1 {
    ControllerUnderTest<MyModel> controllerProxy = createController(MY_CONTROLLER);
    MyModel model = controllerProxy.getModel();
    model.setTitle("Any title");
    controllerProxy.invoke(RESET_VALUES);
    Assert.assertEquals(model.getTitle(), "");
    controllerProxy.destroy();
}

17.4. Testing a controller in Spring

If you write your server application based on Spring you want to use Spring as a container to run your tests, too. Normally you have defined some services or components that are injected in your controllers since the Dolphin Platform provides full support of the DI features of Spring. When writing a test you want all the services to be automatically injected. Spring provides some specific test classes for this purpose and based on the Spring classes the Dolphin Platform offers some more specific test classes that contains everything you need to write tests for controllers:

  • If you want to use JUnit to write your controller tests your test classes should extend SpringJUnitControllerTest

  • If you want to use TestNG to write your controller tests your test classes should extend SpringTestNGControllerTest

17.4.1. JUnit controller test in Spring

As already said a test class should extend the SpringJUnitControllerTest class. By doing so a first simple test class will look like this:

public class MyControllerTest extends SpringJUnitControllerTest {

    @Test
    public void test1 {
        ControllerUnderTest<MyModel> controllerProxy = createController(MY_CONTROLLER);
        MyModel model = controllerProxy.getModel();
        model.setTitle("Any title");
        controllerProxy.invoke(RESET_VALUES);
        Assert.assertEquals(model.getTitle(), "");
        controllerProxy.destroy();
    }

}

Normally you will write one test class for each controller type. In addition this class will contain more than 1 method to test the controller. By doing so you can use some of the annotations that are provided by JUnit to write a readable and maintainable test class:

public class MyControllerTest extends SpringJUnitControllerTest {

    private ControllerUnderTest<MyModel> controllerProxy;

    private MyModel model;

    @Before
    public void init() {
        controllerProxy = createController(MY_CONTROLLER);
        model = controllerProxy.getModel();
    }

    @After
    public void destroy() {
        controllerProxy.destroy();
    }


    @Test
    public void test1 {
        model.setTitle("Any title");
        controllerProxy.invoke(RESET_VALUES);
        Assert.assertEquals(model.getTitle(), "");
    }

    @Test
    public void test2 {
        model.setTitle("Any title");
        controllerProxy.invoke(SHOW_DEFAULTS);
        Assert.assertEquals(model.getTitle(), "Default");
    }

}

In this case a new ControllerUnderTest instance is created for each test and will be destroyed directly after the test. By doing so a developer don’t need to handle this in each test.

17.4.2. TestNG controller test in Spring

As already said a test class should extend the SpringTestNGControllerTest class. By doing so a first simple test class will look like this:

public class MyControllerTest extends SpringTestNGControllerTest {

    @Test
    public void test1 {
        ControllerUnderTest<MyModel> controllerProxy = createController(MY_CONTROLLER);
        MyModel model = controllerProxy.getModel();
        model.setTitle("Any title");
        controllerProxy.invoke(RESET_VALUES);
        Assert.assertEquals(model.getTitle(), "");
        controllerProxy.destroy();
    }

}

Normally you will write one test class for each controller type. In addition this class will contain more than 1 method to test the controller. By doing so you can use some of the annotations that are provided by TestNG to write a readable and maintainable test class:

public class MyControllerTest extends SpringJUnitControllerTest {

    private ControllerUnderTest<MyModel> controllerProxy;

    private MyModel model;

    @BeforeMethod
    public void init() {
        controllerProxy = createController(MY_CONTROLLER);
        model = controllerProxy.getModel();
    }

    @AfterMethod
    public void destroy() {
        controllerProxy.destroy();
    }


    @Test
    public void test1 {
        model.setTitle("Any title");
        controllerProxy.invoke(RESET_VALUES);
        Assert.assertEquals(model.getTitle(), "");
    }

    @Test
    public void test2 {
        model.setTitle("Any title");
        controllerProxy.invoke(SHOW_DEFAULTS);
        Assert.assertEquals(model.getTitle(), "Default");
    }

}

In this case a new ControllerUnderTest instance is created for each test and will be destroyed directly after the test. By doing so a developer don’t need to handle this in each test. Unresolved directive in index.adoc - include::build.adoc[]

18. Changelog

The following overview contains all changes of the Dolphin Platform since version 0.8.0.

Until version 1.0.0 we will not mark breaking changes in the public API seperately since the API might still change. To be true most of the public API of the Dolphin Platform is stable since 0.8.5 and the most changes are related to changing package structure or adding additional methods to public interfaces. In addition it will be best practice to not use any method of the API is annotated by @Deprecated since we plan to remove all this methods and classes for the final 1.0.0 release.

18.1. Version 0.10.0

Release date: Mar 21, 2017

  • Server configuration can be injected as a bean (issue)

  • Add config to deactivate DolphinPlatformBootstrap while testing(issue)

  • No need to add @DolphinBean etc. to the complete class hierarchy of a bean (issue)

  • Android API is compiled against latest DolphinPlatform (issue)

  • Bugfix for NPE in DolphinCommandRegistration (issue)

  • Bugfix for CME in BeanRepository (issue)

  • Config can be provided as managed bean in Spring (issue)

  • WAR for Integration Tests is now created by the build (issue)

  • Bugfix in Calender converter (issue)

  • Added New module that provides a server app to test the DP with Docker (issue)

  • Bugfix for ProcessMonitoring sample is not working (issue)

18.2. Version 0.9.0

Release date: Mar 6, 2017

  • Release workflow supports major & minor releases (issue)

  • Documentation of the distributed event bus (issue)

  • Add dolphin.properties to jump start project (issue)

  • Bugfix for DolphinListener setup in Spring (issue)

  • Bugfix for Refactoring of BackgroundRunner (issue)

18.3. Version 0.8.14

Release date: Feb 24, 2017

  • Polymer client provide an event which is fired when controller is ready (issue)

  • Bugfix for missing config should be logged as info (issue)

  • Added controller test to the sample project (issue)

  • Added cross origin header configuration to server (issue)

  • Added documentation how to run tests locally on Safari (issue)

  • Added logging about the configurations on server (issue)

  • Added AngularJS client to jumpstart (issue)

  • Bugfix to generate lcov report for Polymer client (issue)

18.4. Version 0.8.13

Release date: Feb 22, 2017

  • Bugfix to resolves an issue with invalid headers which appears when the Dolphin server runs behind an Apache(issue)

  • Bugfix to resolve a racing condition that appears when many controllers are initialized together

18.5. Version 0.8.12

Release date: Feb 08, 2017

  • Bugfix for NPE while running generated project from spring-boot-archetype (issue)

  • Bugfix for exception in promise returned by disconnect from ClientContext (issue)

  • Bugfix for remove beans in list is not reflected to Angular client API (issue)

18.6. Version 0.8.11

Release date: Feb 03, 2017

  • Support for distributed event bus (issue)

  • Support for custom headers in HTTP layer in the JS library (issue)

  • All JS repositories are build on Travis, run tests on SauceLabs and provide reports to Sonar (issue, issue, issue, issue, issue, issue)

  • Disconnect functionality in JS provides promise (issue)

  • Some better log messages (issue, issue)

  • Client ID filtering is limited to Dolphin Platform endpoint by default(issue)

  • Angular client build include all dependencies (issue)

  • Support for new datatype BigDecimal, BigInteger and UUID (issue)

  • Dolphin Platform can be configured by Spring Boot configuration (issue)

  • Internal map implementation in JS is replaced by core.js implementation(issue)

18.7. Version 0.8.10

Release date: Jan 12, 2017

  • Provide a first implementation of an Android client API

  • The http connection of the Java client can be accessed and customized to provide custom security or authentification mechanisms.

  • Dependency to Apache HttpClient library is removed. Java basic HttpUrlConnection is used now.

  • Removed some unneeded dependencies

  • All classes of the Dolphin Platform are refactored to Java. The Dolphin Platform do not provide any Groovy based implementations any more. (issue, issue)

  • Groovy was removed as dependency (for runtime). Currently only the unit tests of the remoting modules still depend on Groovy. (issue, issue)

  • Several sources of the remoting layer are refactored and unused functionality was removed. (issue, issue)

  • GSON is used as JSON parser for the remoting layer (issue)

  • The Dolphin Platform provides Docker based integration tests. Currently the integration tests will be executed against server applications that run in Payara, TomEE and Wildfly. (issue)

  • The Dolphin Platform publish reports to sonar. An overview of the current state of the Dolphin Platform can be found here (issue)

  • JavaDocs hosted at GitHub pages. (issue)

  • Some issues and code smells that were reported by sonar are refactored. (issue)

  • Garbage Collection is activated by default (issue)

  • Garbage Collection will be automatically called by every long poll (issue)

  • Sonar reports for all projects (issue)

18.8. Version 0.8.9

Release date: Dec 16, 2016

  • Provide functionality to step into the DP lifecycle on the server (issue)

  • Dependency to GParse is completely removed (issue)

  • Refactoring of the event bus implementation (issue)

  • Event bus can be used in controller tests for Spring (issue)

  • Unneeded javax.inject dependency has been removed (issue)

  • Provide additional information for wrong usage of scopes in Spring (issue)

  • Several Groovy classes are refactored to Java (issue)

18.9. Version 0.8.8

Release date: Nov 14, 2016

  • Optional module that adds support for new Java 8 Data & Time API in the model layer (issue)

  • Better lifecylce definition in the client API (issue)

  • Bugfix for missing exception handling on the client (issue)

  • Groovy sources compiles with right Java version (issue)

  • Dependency to GParse removed in client and common API (issue)

  • Most parts of the Groovy based client API are refactored to Java (issue)

  • Client API use URL to define connection string (issue)

  • SPI to support custom data types in the model (issue)

18.10. Version 0.8.7

Release date: Aug 24, 2016

  • Root package for controller scan can be configured (issue)

  • Dolphin beans must be annotated with @DolphinBean (issue)

  • Better exception handling for the garbage collection (issue)

  • Refactoring of Groovy code to Java (issue)

  • Update of all external dependecies to latest version (issue)

  • Usage of MBeans can be configured (issue)

  • Documenation is published to GitHub pages (issue)

  • Binding support for properties (issue)

  • Session timeout can be specified in JBoss application server (issue)

18.11. Version 0.8.6

Release date: Jul 7, 2016

  • ClientScope to support multiple browser tabs (issue)

  • Http client can be configured in the client API (issue)

  • Initialization in client API is async (issue)

  • Support for controller tests in Spring (issue)

  • Support for session timeout (issue)

  • Introduction of an optional module that provides reactive approaches for the model layer (https://github.com/canoo/dolphin-platform/pull/176issue])

  • Bugfix for model garbage collection (issue)

18.12. Version 0.8.5

Release date: May 20, 2016

  • Build switched to Gradle (issue)

  • Integration of the remoting layer (open dolphin) as direct part of the Dolphin Platform project / repo (issue)

  • Build documentation added (issue)

  • Introduction of the model garbage collection as experimental feature (issue)

18.13. Version 0.8.4

Release date: May 9, 2016

  • Support for several new data types (like enum & date) in the model layer (issue)

  • Bugfix for the client-server connection (issue)

  • Listener support for the lifecylce of the dolphin session on the server (issue)

  • Memory leak on server removed (issue)

  • Public Interfaces for all functionality of the API that is needed by application developers (issue)

  • Configuration support for the server (issue)

  • Asiidoc based documentation started (issue)

18.14. Version 0.8.3

Release date: Mar 18, 2016

  • Introduction of the DolphinSession (issue)

  • Licence header for all sources (issue)

  • Definition of JavaFX basic view class to easily bind the model to the view (issue)

18.15. Version 0.8.2

Release date: Mar 7, 2016

  • Refactoring of the internal server API (issue)

  • Refactoring of the internal client API (issue)

  • Factory interfaces for the client API (issue)

18.16. Version 0.8.1

Release date: Feb 19, 2016

  • Support for JBoss application server (issue)

  • Command names in the protocol are shorted (issue)

  • Fix for a bug in the list change event (issue)

  • Example isn’t deployed to Maven central anymore (issue)

18.17. Version 0.8.0

Release date: Feb 4, 2016

  • This was the first public release of the Dolphin Platform

  • Optional module for JSR 303 (Bean Validation) support added (issue)

  • The event bus provides type safe topics (issue)

  • Memory leak for session data after session was destroyed has been fixed (issue)

  • Support for list binding in JavaFX (issue)

19. Dolphin Platform release documentation

The sources of the Dolphin Platform are seperated in several repositories:

In addition we provide several repositories that contain jumpstarts for the Dolphin Platform. This repositories should be released with each release of the Dolphin Platform:

Based on this seperation a release for the Dolphin Platform is more complex than a release for a framework that is based on one single repository.

There are 2 different release types for the Dolphin Platform: * The regular (major) release introduces new features and / or bugfixes. We currently try to create such a release in a periode of 2 weeks. This release will be created from the master branches of all repositories. * A bugfix (minor) release provides a critical bugfix and will be created based on a regular /major Dolphin Platform release. This release will normally be based on the release branch of the major release.

19.1. Install needed tools for the release

TODO: some tools must be installed on the local machine. Add overview for all tools

19.2. Major Release of the Dolphin Platform

This chapter describes how a new major release for the Dolphin Platform can be created.

When doing a major release of the Dolphin Platform the first or second number in the version number should be updates. Based on this the next major release after 0.9.0 will be 0.10.0 and after 1.2.0 the next release will be 1.3.0.

For a release the repositories shoud be released in a defined order to create a accurate release. The following documentation shows a step by step instruction to release all repositories. In general a release starts by releasing the sources of the JavaScript API repositores followed by the Java repository. Once this is done the jumpstart related repositories can be released in any order.

Releasing the JavaScript repositories

A developer should always start to release the JavaScript basic API. To do so several steps are needed:

  • The JavaScript basic API repository must be check out on the local machine

  • Create a new branch release/VERSION where VERSION defines the version that should be released (like release/0.10.0)

  • Check out the created release branch

  • If a developer has checked out the repository for the first time he needs to call npm install in the root folder of the repository to install all the npm dependencies

  • If a developer has checked out the repository for the first time he needs to call bower install in the root folder of the repository to install all the bower dependencies

  • In the root folder of the project the npm update command must be called to update all npm dependencies

  • In the root folder of the project the bower update command must be called to update all bower dependencies

  • As a next step the project must be build by using gulp. To do so call gulp clean verify build in the root folder of the project

  • If the build executes without any error the new version number for the release must be specified in the following files: bower.json, package.json, sonar-project.properties

  • Once this is done the current state must be tagged in git. The name of the tag must match the version of the new release like 0.10.0

  • Once the tag is uploaded to the global repository at GitHub the JavaScript basic API has been released

As a next step the Poylmer API can be released. To do so several steps are needed:

  • The Polymer API repository must be check out on the local machine

  • Create a new branch release/VERSION where VERSION defines the version that should be released (like release/0.10.0)

  • Check out the created release branch

  • The dependency of the Dolphin Platform JS client must be set to the new version in the bower.json file

  • If a developer has checked out the repository for the first time he needs to call npm install in the root folder of the repository to install all the npm dependencies

  • If a developer has checked out the repository for the first time he needs to call bower install in the root folder of the repository to install all the bower dependencies

  • In the root folder of the project the npm update command must be called to update all npm dependencies

  • In the root folder of the project the bower update command must be called to update all bower dependencies

  • The new version number for the release must be specified in the following files: bower.json, package.json and sonar-project.properties

  • As a next step the project must be build by using gulp. To do so call gulp clean verify build in the root folder of the project.

  • Once this is done the current state must be tagged in git. The name of the tag must match the version of the new release like 0.10.0

  • Once the tag is uploaded to the global repository at GitHub the Polymer basic API has been released

As a next step the AngularJS API can be released. To do so several steps are needed:

  • The AngularJS API repository must be check out on the local machine

  • Create a new branch release/VERSION where VERSION defines the version that should be released (like release/0.10.0)

  • Check out the created release branch

  • The dependency of the Dolphin Platform JS client must be set to the new version in the bower.json file

  • If a developer has checked out the repository for the first time he needs to call npm install in the root folder of the repository to install all the npm dependencies

  • If a developer has checked out the repository for the first time he needs to call bower install in the root folder of the repository to install all the bower dependencies

  • In the root folder of the project the npm update command must be called to update all npm dependencies

  • In the root folder of the project the bower update command must be called to update all bower dependencies

  • The new version number for the release must be specified in the following files: bower.json, package.json and sonar-project.properties

  • As a next step the project must be build by using gulp. To do so call gulp clean verify build in the root folder of the project.

  • Once this is done the current state must be tagged in git. The name of the tag must match the version of the new release like 0.10.0

  • Once the tag is uploaded to the global repository at GitHub the AngularJS basic API has been released

Releasing the Java repository
  • update the dependencies of all JavaScript modules in the platform-examples folder to the new version

  • Try all examples

  • check the changelog for current release in the documentation if all changes are mentioned

  • Commit the changes to master

  • Create a new branch release/VERSION where VERSION defines the version that should be released (like release/0.10.0)

  • Check out the created release branch

  • Call ./gradlew clean build from the project folder to check that the build is working

  • update the version number in the gradle.properties file

  • Call ./gradlew clean bintrayUpload from the project folder. For step the bintray user name and api token must be configured in the gradle.properties file (/userHome/.gradle/gradle.properties). Add the properties bintrayUsername and bintrayApiKey to the file.

  • Create a tag from the release branch. The name of the tag must match the version of the new release like 0.10.0

  • Upload the tag is to the global repository at GitHub

  • If this release changed the first number in the version number (like 1.X.X → 2.X.X) we need to update the SNAPSHOT version in the master

  • Login to Bintray and publish all artifacts to JCenter and Maven Central

Releasing the Android repository
  • Create a new branch release/VERSION where VERSION defines the version that should be released (like release/0.10.0)

  • update the version number of Dolphin Platform in the gradle.properties file

  • Call ./gradlew clean build from the project folder to check that the build is working

  • Call ./gradlew clean bintrayUpload from the project folder. For step the bintray user name and api token must be configured in the gradle.properties file (/userHome/.gradle/gradle.properties). Add the properties bintrayUsername and bintrayApiKey to the file.

  • Create a tag from the release branch. The name of the tag must match the version of the new release like 0.10.0

  • Upload the tag is to the global repository at GitHub

  • Login to Bintray and publish all artifacts to JCenter and Maven Central

Releasing the jumpstart repository
  • Change the version number of Dolphin Platform in src/main/resources/archetype-resources/pom.xml

  • Change the version number of Dolphin Platform in src/main/resources/archetype-resources/polymer-client/bower.json

  • Do mvn clean install

  • Create sample project by calling mvn archetype:generate -Dfilter=com.canoo.dolphin-platform: in separate folder

  • Check everything in the sample project

  • Create a new branch release/VERSION where VERSION defines the version that should be released (like release/0.10.0)

  • Update version number in main pom.xml

  • Call mvn clean deploy from the project folder. For step the bintray user name and api token must be configured in the .m2/setting.xml Maven setting file.

  • Create a tag from the release branch. The name of the tag must match the version of the new release like 0.10.0

  • Upload the tag is to the global repository at GitHub

  • Login to Bintray and publish all artifacts to JCenter and Maven Central

General release steps
  • Next to the release of the software the version numbers of the latest stable should be updated in tutorials and readme.md files of the repos.

  • Create a release in GitHub (see releases) with the name of the released version and copy the changelog for the new version in it

  • The milestone in github must be closed (see milestones)

19.3. Minor Release of the Dolphin Platform

This chapter describes how a new minor release for the Dolphin Platform can be created.

When doing a major release of the Dolphin Platform the third number in the version number should be updates. Based on this the next minor release after 0.9.0 will be 0.9.1 and after 1.2.1 the next release will be 1.2.2.

For a release the repositories shoud be released in a defined order to create a accurate release. The following documentation shows a step by step instruction to release all repositories. In general a release starts by releasing the sources of the JavaScript API repositores followed by the Java repository. Once this is done the jumpstart related repositories can be released in any order.

Releasing the JavaScript repositories

A developer should always start to release the JavaScript basic API. To do so several steps are needed:

  • The JavaScript basic API repository must be check out on the local machine

  • Check out the last release branch (like release/0.10.0)

  • If a developer has checked out the repository for the first time he needs to call npm install in the root folder of the repository to install all the npm dependencies

  • If a developer has checked out the repository for the first time he needs to call bower install in the root folder of the repository to install all the bower dependencies

  • In the root folder of the project the npm update command must be called to update all npm dependencies

  • In the root folder of the project the bower update command must be called to update all bower dependencies

  • As a next step the project must be build by using gulp. To do so call gulp clean verify build in the root folder of the project

  • If the build executes without any error the new version number for the release must be specified in the following files: bower.json, package.json, sonar-project.properties

  • Once this is done the current state must be tagged in git. The name of the tag must match the version of the new release like 0.10.1

  • Once the tag is uploaded to the global repository at GitHub the JavaScript basic API has been released

As a next step the Poylmer API can be released. To do so several steps are needed:

  • The Polymer API repository must be check out on the local machine

  • Check out the last release branch (like release/0.10.0)

  • The dependency of the Dolphin Platform JS client must be set to the new version in the bower.json file

  • If a developer has checked out the repository for the first time he needs to call npm install in the root folder of the repository to install all the npm dependencies

  • If a developer has checked out the repository for the first time he needs to call bower install in the root folder of the repository to install all the bower dependencies

  • In the root folder of the project the npm update command must be called to update all npm dependencies

  • In the root folder of the project the bower update command must be called to update all bower dependencies

  • The new version number for the release must be specified in the following files: bower.json, package.json and sonar-project.properties

  • As a next step the project must be build by using gulp. To do so call gulp clean verify build in the root folder of the project.

  • Once this is done the current state must be tagged in git. The name of the tag must match the version of the new release like 0.10.1

  • Once the tag is uploaded to the global repository at GitHub the Polymer basic API has been released

As a next step the AngularJS API can be released. To do so several steps are needed:

  • The AngularJS API repository must be check out on the local machine

  • Check out the last release branch (like release/0.10.0)

  • The dependency of the Dolphin Platform JS client must be set to the new version in the bower.json file

  • If a developer has checked out the repository for the first time he needs to call npm install in the root folder of the repository to install all the npm dependencies

  • If a developer has checked out the repository for the first time he needs to call bower install in the root folder of the repository to install all the bower dependencies

  • In the root folder of the project the npm update command must be called to update all npm dependencies

  • In the root folder of the project the bower update command must be called to update all bower dependencies

  • The new version number for the release must be specified in the following files: bower.json, package.json and sonar-project.properties

  • As a next step the project must be build by using gulp. To do so call gulp clean verify build in the root folder of the project.

  • Once this is done the current state must be tagged in git. The name of the tag must match the version of the new release like 0.10.1

  • Once the tag is uploaded to the global repository at GitHub the AngularJS basic API has been released

Releasing the Java repository
  • Check out the last release branch (like release/0.10.0)

  • Change the version of the project in the pom.xml to a SNAPSHOT version (like 0.10.1-SNAPSHOT)

  • update the dependecies of all JavaScript modules in the platform-examples folder to the new version

  • Try all examples

  • check the changelog for current release in the documentation if all changes are mentioned

  • Commit the changes to master

  • update the version number in the gradle.properties file

  • Call ./gradlew clean build from the project folder to check that the build is working

  • Remove the SNAPSHOT suffix from the version in the pom.xml (like 0.10.1)

  • Call ./gradlew clean bintrayUpload from the project folder. For step the bintray user name and api token must be configured in the gradle.properties file (/userHome/.gradle/gradle.properties). Add the properties bintrayUsername and bintrayApiKey to the file.

  • Create a tag from the release branch. The name of the tag must match the version of the new release like 0.10.1

  • Upload the tag is to the global repository at GitHub

  • Login to Bintray and publish all artifacts to JCenter and Maven Central

Releasing the Android repository
  • Check out the last release branch (like release/0.10.0)

  • Change the version of the project in the pom.xml to a SNAPSHOT version (like 0.10.1-SNAPSHOT)

  • update the version number of Dolphin Platform in the gradle.properties file

  • Call ./gradlew clean build from the project folder to check that the build is working

  • Call ./gradlew clean bintrayUpload from the project folder. For step the bintray user name and api token must be configured in the gradle.properties file (/userHome/.gradle/gradle.properties). Add the properties bintrayUsername and bintrayApiKey to the file.

  • Remove the SNAPSHOT suffix from the version in the pom.xml (like 0.10.1)

  • Create a tag from the release branch. The name of the tag must match the version of the new release like 0.10.1

  • Upload the tag is to the global repository at GitHub

  • Login to Bintray and publish all artifacts to JCenter and Maven Central

Releasing the jumpstart repository
  • Check out the last release branch (like release/0.10.0)

  • Change the version of the project in the pom.xml to a SNAPSHOT version (like 0.10.1-SNAPSHOT)

  • Change the version number of Dolphin Platform in src/main/resources/archetype-resources/pom.xml

  • Change the version number of Dolphin Platform in src/main/resources/archetype-resources/polymer-client/bower.json

  • Do mvn clean install

  • Create sample project by calling mvn archetype:generate -Dfilter=com.canoo.dolphin-platform: in separate folder

  • Check everything in the sample project

  • Update version number in main pom.xml

  • Call mvn clean deploy from the project folder. For step the bintray user name and api token must be configured in the .m2/setting.xml Maven setting file.

  • Remove the SNAPSHOT suffix from the version in the pom.xml (like 0.10.1)

  • Create a tag from the release branch. The name of the tag must match the version of the new release like 0.10.1

  • Upload the tag is to the global repository at GitHub

  • Login to Bintray and publish all artifacts to JCenter and Maven Central

General release steps
  • If the minor release is the newest version of the Dolphin Platform next to the release of the software the version numbers of the latest stable should be updated in tutorials and readme.md files of the repos.

  • Create a release in GitHub (see releases) with the name of the released version and copy the changelog for the new version in it

  • The milestone in github must be closed (see milestones)