This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information

DOAG 2017

Auch in diesem Jahr sind wir wieder auf der DOAG anzutreffen. Details zur Veranstaltung.

Neu ist, das wir mit 3 Vorträgen vertreten sind:

  • Transformation: Fachbereich & IT digitalisieren gemeinsam
  • Low Code Plattform – A Web & Mobile UI Live Coding Session
  • Pimp your Forms - Forms Modernisierung mit Java

Nähere Details zu den Vorträgen, sind demnächst im Vortragsprogramm zu finden.

GUI Testing with VisionX

A new AddOn for VisionX is coming :)

Our GUI testing tool

A first impression

Application tester

Application tester

The tool is a recorder for VisionX/JVx applications. It has an option to export created tests as JUnit tests which makes it easy for Software developers to integrate the GUI tests in their CI systems. It's a super easy tool with so much value for you!

The App Tester will be available in our solution store and will be an AddOn for VisionX.

If you are interested in details, leave a comment.

JVx Reference, Resource and UIResource

Let's talk about Resources and UIResources, and why they sound similar but are not the same.

The Basics

We've previously covered how the JVx GUI layer works, now we are going to have a better look at the Resources and UIResources. With "Resource" we do not mean images or similar, we mean the implementation at the technology layer which is encapsulated by a wrapper class (Bridge Pattern). An "UIResource" on the other hand is an encapsulated concrete implementation of one of the interfaces on the UI layer.

Let's do a short brush-up on how the JVx architecture looks like in regards to the GUI stack:

JVx Resource

The UI Wrappers are the main UI classes which are used to create the GUI (f.e. UIButton). These are wrapping the Implementations (f.e. a SwingButton) which themselves are wrapping the Extension/Technology (f.e. a JVxButton/JButton). Only the UI and Implementation classes are implementing the interface required for the component (f.e. IButton). That also means that the Implementation is dependent on the Extension/Technology component, but the UI can use any object which implements the interface.

Now, with that knowledge we can start defining what is what:

JVx Resource 2

The resource itself, accessed by calling <uiwrapper>.getResource(), is the Extension/Technology component. The uiresource can be accessed by calling <uiwrapper>.getUIResource(). The uicomponent can be accessed by calling <uiwrapper>.getUIComponent() and is usually the UI Wrapper itself. If we use our previous Swing example, the resource would be a JVxButton/JButton, the uiresource would be the SwingPanel and the uicomponent would be the UIButton.

As one can see, access to all objects which are comprising the GUI is at all times possible. We, of course, have the UI component, we can access the Implementation component and we can access the Extension/Technology component. Theoretically we could also swap them at runtime, but in JVx this is limited to the construction of the object to greatly reduce the error potential.

Creating custom components

Equipped with that knowledge, we can revisit the previous guide on how to create custom components, the part about the BeepComponent to be exact:

  1. public class BeepComponent extends UIComponent<IPanel>
  2. {
  3.    public BeepComponent()
  4.    {
  5.       super(new UIPanel());
  6.        
  7.       UIButton highBeepButton = new UIButton("High Beep");
  8.       highBeepButton.eventAction().addListener(Beeper::playHighBeep);
  9.      
  10.       UIButton lowBeepButton = new UIButton("Low Beep");
  11.       highBeepButton.eventAction().addListener(Beeper::playLowBeep);
  12.      
  13.       UIFormLayout layout = new UIFormLayout();        
  14.      
  15.       uiResource.setLayout(layout);
  16.       uiResource.add(new UILabel("Beep"), layout.getConstraints(0, 0, -1, 0));
  17.       uiResource.add(highBeepButton, layout.getConstraints(0, 1));
  18.       uiResource.add(lowBeepButton, layout.getConstraints(1, 1));
  19.    }
  20. }

We are setting a new UIResource (an UIPanel) in the constructor (at line #5) which is to be used by the UIComponent. In this case it is not an Implementation, but another UI component, but that doesn't matter because the UIResource only must implement the expected interface. At line #15 we start using that custom UIResource.

Because UIComponent is an abstract component designed for exactly this usage, the example might not be the most exciting one, but it clearly illustrates the mechanic.

Bolting on functionality

Also from the previous guide on how to create custom components we can reuse the PostfixedLabel as example.

  1. private UILabel testLabel = new UILabel()
  2. {
  3.     public UILabel()
  4.     {
  5.         super(new PostfixedLabel("", "-trial"));
  6.     }
  7. };

Now testLabel will be using the PostfixedLabel internally, but with no indication to the user of the object that this is the case. This allows to extend the functionality of a component completely transparently, especially in combination with functions which do return an UIComponent and similar.

An important note about the component hierarchy

If we create a simple component extensions, like the BeepComponent above, it is important to note that there is one other layer of indirection in regards to the hierarchy on the technology layer. If we create a simple frame with the BeepComponent in it, one might expect the following hierarchy:

         UI                   Technology
---------------------    ----------------------
 UIFrame                  Frame
   \-UIPanel                \-Panel
       \-BeepComponent          \-BeepComponent
                                    \-Panel
                                        |-Label
                                        |-Button
                                        \-Button

With the BeepComponent added and its sub-components as its children. However, the actual hierarchy looks like this:

         UI                   Technology
---------------------    ----------------------
 UIFrame                  Frame
   \-UIPanel                \-Panel
       \-BeepComponent          \-Panel
                                    |-Label
                                    |-Button
                                    \-Button

That is because such extended components are not "passed" to the Technology, they do only exist on the UI layer because they do not have a Technology component which could be used. That is done by adding the UIComponent to the UI parent, but for adding the actual Technology component the set UIResource is used.

The special case of containers

Another special case are containers. For example we could create a panel which does display an overlay in certain situations and we will need to use that throughout the whole application.

UIResourceContainer Example

That means we do not want to build it every time anew, so one option would be to use a factory method to "wrap" the content, something like this:

  1. UIFormLayout panelLayout = new UIFormLayout();
  2. panelLayout.setHorizontalAlignment(UIFormLayout.ALIGN_CENTER);
  3. panelLayout.setVerticalAlignment(UIFormLayout.ALIGN_CENTER);
  4.    
  5. UIPanel panel = new UIPanel();
  6. panel.setLayout(panelLayout);
  7. panel.add(new UILabel("Firstname"), panelLayout.getConstraints(0, 0));
  8. panel.add(new UITextField("John"), panelLayout.getConstraints(1, 0));
  9. panel.add(new UILabel("Lastname"), panelLayout.getConstraints(2, 0));
  10. panel.add(new UITextField("Doe"), panelLayout.getConstraints(3, 0));
  11. panel.add(new UILabel("Street"), panelLayout.getConstraints(0, 1));
  12. panel.add(new UITextField("Old R. Road"), panelLayout.getConstraints(1, 1, 3, 1));
  13. panel.add(new UILabel("ZIP"), panelLayout.getConstraints(0, 2));
  14. panel.add(new UITextField("11946"), panelLayout.getConstraints(1, 2));
  15. panel.add(new UILabel("Place"), panelLayout.getConstraints(2, 2));
  16. panel.add(new UITextField("Hampton Bays"), panelLayout.getConstraints(3, 2));
  17.    
  18. parentContainer.add(OverlayPanelFactory.wrap(panel), UIBorderLayout.CENTER);

And the wrap method itself:

  1. public static final UIPanel wrap(IComponent pContent)
  2. {
  3.     UILabel overlayLabel = new UILabel("FOR YOUR<br>EYES ONLY");
  4.     overlayLabel.setBackground(null);
  5.     overlayLabel.setFont(UIFont.getDefaultFont().deriveFont(UIFont.BOLD, 48));
  6.     overlayLabel.setForeground(UIColor.createColor("#3465a4"));
  7.     overlayLabel.setHorizontalAlignment(UILabel.ALIGN_CENTER);
  8.    
  9.     UIFormLayout layout = new UIFormLayout();
  10.    
  11.     UIPanel panel = new UIPanel();
  12.    
  13.     panel.setLayout(layout);
  14.     panel.setBackground(UIColor.createColor("#3465a4"));
  15.     panel.add(overlayLabel, layout.getConstraints(0, 0, -1, -1));
  16.     panel.add(pContent, layout.getConstraints(0, 0, -1, -1));
  17.    
  18.     return panel;
  19. }

Which is easy enough, but let's say we'd like to add logic to that wrapper, at that point it becomes more complicated. We can't use the same technique as for custom component from above, because in that case the "overlaying panel" would simply not be displayed. However, there is a similar mechanism for containers, setting the UIResourceContainer.

The UIResourceContainer is another special mechanism which works similar to setting the UIResource, but it works exactly the other way round. While setting the UIResource does "hide" components from the Technology which are there in UI layer, setting the UIResourceContainer does hide components from the UI layer while there are added in the Technology. A little bit complicated, here is our example again using this technique:

  1. public static class OverlayedPanel extends UIPanel
  2. {
  3.     public OverlayedPanel()
  4.     {
  5.         super();
  6.        
  7.         UILabel overlayLabel = new UILabel("FOR YOUR<br>EYES ONLY");
  8.         overlayLabel.setBackground(null);
  9.         overlayLabel.setFont(UIFont.getDefaultFont().deriveFont(UIFont.BOLD, 48));
  10.         overlayLabel.setForeground(UIColor.createColor("#3465a4"));
  11.         overlayLabel.setHorizontalAlignment(UILabel.ALIGN_CENTER);
  12.        
  13.         UIPanel innerPanel = new UIPanel();
  14.        
  15.         UIFormLayout layout = new UIFormLayout();
  16.        
  17.         setLayout(layout);
  18.         setBackground(UIColor.createColor("#3465a4"));
  19.         add(overlayLabel, layout.getConstraints(0, 0, -1, -1));
  20.         add(innerPanel, layout.getConstraints(0, 0, -1, -1));
  21.        
  22.         setUIResourceContainer(innerPanel);
  23.     }
  24. }

What we've done is extend an UIPanel (line #1), setting it up and adding children and then we've declared one of its children as the UIResourceContainer (line #22). So all methods which are specific to IContainer (adding children, setting a layout, etc.) are now forwarding to the innerPanel and manipulating the contents of the OverlayedPanel directly is not directly available.

And here is how it is used:

  1. UIFormLayout panelLayout = new UIFormLayout();
  2. panelLayout.setHorizontalAlignment(UIFormLayout.ALIGN_CENTER);
  3. panelLayout.setVerticalAlignment(UIFormLayout.ALIGN_CENTER);
  4.    
  5. UIPanel panel = new OverlayedPanel();
  6. panel.setLayout(panelLayout);
  7. panel.add(new UILabel("Firstname"), panelLayout.getConstraints(0, 0));
  8. panel.add(new UITextField("John"), panelLayout.getConstraints(1, 0));
  9. panel.add(new UILabel("Lastname"), panelLayout.getConstraints(2, 0));
  10. panel.add(new UITextField("Doe"), panelLayout.getConstraints(3, 0));
  11. panel.add(new UILabel("Street"), panelLayout.getConstraints(0, 1));
  12. panel.add(new UITextField("Old R. Road"), panelLayout.getConstraints(1, 1, 3, 1));
  13. panel.add(new UILabel("ZIP"), panelLayout.getConstraints(0, 2));
  14. panel.add(new UITextField("11946"), panelLayout.getConstraints(1, 2));
  15. panel.add(new UILabel("Place"), panelLayout.getConstraints(2, 2));
  16. panel.add(new UITextField("Hampton Bays"), panelLayout.getConstraints(3, 2));
  17.    
  18. parentContainer.add(panel, UIBorderLayout.CENTER);

Notice that we can use it is any other panel (line #5) and simply add it to the parent (line #18). For a user of the API it is transparent as to whether there are more components or not, this is also visible in the created component hierarchy:

         UI                   Technology
---------------------    ----------------------
 UIPanel                  Panel
   \-OverlayedPanel         \-Panel
       |-UILabel                |-Label
       |-UITextField                \-Panel
       |-UILabel                    |-Label
       |-UITextField                |-TextField
       |-UILabel                    |-Label
       |-UITextField                |-TextField
       |-UILabel                    |-Label
       |-UITextField                |-TextField
       |-UILabel                    |-Label
       \-UITextField                |-TextField
                                    |-Label
                                    \-TextField

This makes it very easy to have containers which add additional components without the actual GUI noticing or caring.

Conclusion

Because of the way the JVx framework is designed, it is easy to access all layers of the GUI framework and also facilitate the usage of these layers to create custom components and allow easy access to the wrapped components, no matter on what layer or of what kind they are.

VisionX Demo Application

With the preparations for the VisionX 2.4 Update 1 we've also created several simple but telling demo applications. Let's have a look at these.

Demo-AMLD

We start our short tour with the first demo, the "Aircraft Manufacturing Line Dashboard" which is emulating a dashboard as it can be found in various factories and manufacturing companies.

DemoAMLDDemoAMLD

Its main frontend is a Swing based GUI which displays (randomly generated) data, additionally there is a Vaadin powered frontend which displays the raw data. The main highlights of this demo are:

  • Capability to customize technology-specific components.
  • Utilizing custom components in the VisionX designer.
  • Usage of cell formatters.
  • Making WorkScreens only available in certain environments.
  • Data is generated with either the Random object or OpenSimplexNoise.

Demo-BIELS

This is a demo of a typical dashboard and master data forms of an ERP application, in this case a small company focused on import and export of chemical/biochemical materials and similar.

DemoBIELSDemoBIELS

All WorkScreens are fully functional in Swing and Vaadin environments. The main highlights of this demo are:

  • Demonstration of feature parity and interchangeability between Swing and Vaadin.
  • Creation of custom controls for displaying data.
  • Fully and easily editable with the VisionX designer.

Demo-CNASP

Next up is a demonstration of the ability to create end-user facing web portals with JVx and VisionX.

DemoCNASPDemoCNASP

The whole application is designed to function on large and small displays alike, like those of smartphones. The main highlights are:

  • Demonstration of extensive customization via CSS.
  • Differing layouts depending on the environment (Desktop, Tablet, Smartphone).

Demo-DMARS

Accessing data and documents on the go is always kind of a hassle, but we can make it easier and more comfortable by providing easy to use GUIs for such occasions.

DemoDMARSDemoDMARS

This simple document management system shows off a simple and easy to use GUI for accessing and manipulating documents in the storage. The highlights are:

  • GUI designed to be used on small, touch-enabled displays.
  • Extending the Table to allow checkbox based multi-row selection.
  • Inserting and retrieving files from a database.
  • The new Upload component available in ProjX.

Demo-Reports

Last but, not least, is the "big guide" of how to use reporting in VisionX.

DemoReportsDemoReports

This application will take you step by step through the capabilities and features of the VisionX reporting solution. Every WorkScreen demonstrates another feature complete with a description, example, access to the template and final report and of course the related documentation.

  • A step-by-step introduction to the VisionX reporting solution.
  • 12 examples with easy to read and documented source code.
  • All parts easily accessible: Source code, Template, Data and Report.

Availability

All these applications are available in the VisionsX Solution Store, on demand. The source code is licensed under the permissive Apache License.

VisionX 2.4 update I is out

VisionX 2.4 update release I is out. The exact version number is 2.4.544.310.
It's a powerful update release becasue it contains latest versions of relevant opensource libraries and frameworks.

What's new?

  • Thumbnails

    If application contains a thumbnail, VisionX won't add a random thumbnail anymore.

  • HTML5 Theme

    It's now possible to change the HTML5 theme via web settings: Standard, Valo, Mobile

    Web settings

    Web settings

  • Live preview

    Live preview does now work if you apply external changes or if you change the source code in Eclipse (with installed EPlug).

  • GridLayout is now supported

    The GridLayout of JVx is now officially supported in VisionX' Designer.

  • Vaadin 7.7.9

    VisionX updates contains Vaadin 7.7.9.

  • Solution store update

    The solution store now shows available solutions based on your VisionX version.

  • New demo solutions

    We have awesome new demo applications for licensed users.

    Demo applications

    Demo applications

  • New Reporting demo

    The reporting demo is a complete documentation about VisionX reporting features and the reporting API.

    Reporting

    Reporting

    Report overview

    Report overview

  • Smaller bugfixes

All customers will find the new version in their download area!
The trial version was updated as well!

JVx Reference, DataBooks

Let's talk about DataBooks, which allow access to data without any effort.

What is it?

DataBooks are an active model, which allow you to directly query and manipulate the data. Contrary to many other systems JVx does not map the data into objects, but instead allows you to directly access it in a table like fashion, exposing columns, rows and values.

One could say that it is like a three dimensional array, with these dimensions:

  1. DataPages
  2. DataRows
  3. Columns/Values

With DataPages containing DataRows, which itself contain the values and everything is referencing the RowDefinition, which outlines how a row looks like.

DataBook Architecture

RowDefinition

The RowDefinition defines what columns are available in the row and stores some additional information about them, like the names of the primary key columns. You can think of the RowDefinition as the headers of a table.

Its creation and usage is rather simple, and if you're working with RemoteDataBooks there is no need to create one at all, as it is automatically created when the DataBook is opened. A RowDefinition holds and manages ColumnDefinitions, which define the columns.

RowDefinition rowDefinition = new RowDefinition();
rowDefinition.addColumnDefinition(columnDefinitionA);
rowDefinition.addColumnDefinition(columnDefinitionB);
rowDefinition.addColumnDefinition(columnDefinitionC);

dataBook.setRowDefinition(rowDefinition);

ColumnDefinition

The ColumnDefinition defines and provides all necessary information about the column, like its DataType, its size and if it is nullable or not. You can think of it as one column in a table.

ColumnDefinition columnDefinition = new ColumnDefinition("NAME", new StringDataType());
columnDefinition.setNullable(false);

MetaData

Most of the ColumnDefinition is additional information about the column, like if it is nullable, the label of the column, default values, allowed values and similar information.

DataType

Of course we must define what type the value in the column has, this is done by setting a DataType on the ColumnDefinition. The DataType defines what kind of values the column holds, like if it is a String, or a Number or something else. We do provide the widest used DataTypes out of the box:

  • BigDecimal
  • BinaryData
  • Boolean
  • Long
  • Object
  • String
  • Timestamp

It is possible to add new DataTypes by simply implementing IDataType.

DataRow

The DataRow repesents a single row of data, it holds/references its own RowDefinition and of course provides access to the values of the row. Accessing the DataRow can be done either by column index or column name, and the methods do either return or accept Objects. Let's look at a simple usage example:

DataRow dataRow = new MemDataRow(rowDefinition);

String value = (String)dataRow.getValue("COLUMN_A");

dataRow.setValue("COLUMN_A", "New Value");

DataPage

The DataPage is basically a list of DataRows, it also holds its own RowDefinition which is shared with all the contained DataRows.

The main usage of DataPages is to allow paging in a master/detail relationship. If the master selects a different row, the detail databook does select the related DataPage.

DataBook

The DataBook is the main model of JVx, it provides direct access to its current DataPage and DataRow by extending from IDataRow and IDataPage.

By default, the DataBook holds one DataPage and only has multiple DataPages if it is the detail in a master/detail relationship.

Usage example

Here is a simple usage example of a MemDataBook, an IDataBook implementation which does only operate in memory:

IDataBook dataBook = new MemDataBook();
dataBook.setName("test");
dataBook.getRowDefinition().addColumnDefinition(new ColumnDefinition("ID", new LongDataType()));
dataBook.getRowDefinition().addColumnDefinition(new ColumnDefinition("COLUMN_STRING", new StringDataType()));
dataBook.open();

dataBook.insert(false);
dataBook.setValue("ID", Long.valueof(0));
dataBook.setValue("COLUMN_STRING", "VALUE");

dataBook.insert(false);
dataBook.setValue("ID", Long.valueof(1));
dataBook.setValue("COLUMN_STRING", "VALUE_A");

dataBook.saveSelectedRow();

dataBook.setSelectedRow(0);

dataBook.setValue("COLUMN_STRING", "VALUE_NEW");

dataBook.saveSelectedRow();

dataBook.setSelectedRow(1);

dataBook.delete();

Accessing the data with Strings

One of the major advantages of the DataBook concept is that there is no need to create new classes to represent each table, view or query result. One can always use the DataBook, directly and easily and model changes don't necessitate changes on the client side. The downside to this approach is that we lose compile time checks because we access the data dynamically. However, This can be mitigated by using EPlug, an Eclipse plugin which provides compile time checks and many more features.

No primitives, Objects only

We do not provide overloads to fetch primitives, that is because there are mainly three types of data inside a database:

  1. Numbers
  2. Text
  3. Binary Data

Text and Binary Data are both objects (arrays of primitives are Objects after all) and Numbers are either primitives or Objects. Most of the time if we deal with numbers inside a database we want them to be of arbitrary precision, which means we must represent them as BigDecimal. Supporting double or float in these cases would be dangerously, because one might write a float into the database which might or might not end up with the correct value in the database. To completely eliminate such problems, we do only support Objects, which means that one is "limited" to the usage of Number extensions like BigLong and BigDecimal, which do not suffer from such problems.

Where are the DataPages?

What is not clear from this example is how and when DataPages are used. As a matter of fact, most of the time there is no need to think about DataPages because they are managed directly by the DataBook, and if used this like there is only one DataPage. Multiple DataPages will be used if there is a Master/Detail relationship defined in which case the DataBook does select the correct DataPage automatically.

Master/Detail

Master/Detail is something that occurs in nearly every data model. It means simply that there is one master dataset which is referenced by one or multiple detail datasets. Or to express it in SQL:

SELECT
  *
FROM
  MASTER m
  LEFT JOIN DETAIL d ON m.ID=d.MASTER_ID;

We can of course express a Master/Detail relationship when using DataBooks. For that we just create a ReferenceDefinition and assign it to the Detail DataBook:

IDataBook masterDataBook = new MemDataBook();
masterDataBook.setName("master");
masterDataBook.getRowDefinition().addColumnDefinition(new ColumnDefinition("ID", new LongDataType()));
masterDataBook.open();

IDataBook detailDataBook = new MemDataBook();
detailDataBook.setName("detail");
detailDataBook.getRowDefinition().addColumnDefinition(new ColumnDefinition("ID", new LongDataType()));
detailDataBook.getRowDefinition().addColumnDefinition(new ColumnDefinition("MASTER_ID", new LongDataType()));
detailDataBook.setReferenceDefinition(new ReferenceDefinition(new Streing[] {"MASTER_ID"}, masterDataBook, new String[] {"ID"});
detailDataBook.open();

Let's assume the following data for illustration purposes:

MASTER              DETAIL
======        =================
  ID            ID  | MASTER_ID
------        ------|----------
     1             1|         1
     2             2|         1
     3             3|         2
                   4|         2
                   5|         2
                   6|         3
                   7|         3
                   8|         3

Now if we select the second row in the masterDataBook, the detailDataBook will just contain the rows with the corresponding MASTER_ID, so 3, 4 and 5.

MASTER              DETAIL
======        =================
  ID            ID  | MASTER_ID
------        ------|----------
     1             3|         2
S    2             4|         2
     3             5|         2

The detailDataBook is automatically adjusted according to the selection in the masterDatabook. Of course this can have an arbitrary depth, too.

Conclusion

The DataBook is the backbone of JVx, it provides a clean and easy way to access and manipulate data. At the same time, it is flexible and can be customized to specific needs with ease.

JVx Reference, Application Basics

Let's talk about the basics, how a JVx application starts, works and how the connection strings together the client and server side.

Multitier Architecture

JVx is designed to be Multitier by default. It allows a clean and easy separation of processes and makes it easy to build, maintain and extend applications by separating the client, server and data storage.

Launchers

The following method is a simplified way to launch a JVx application. Normally, you'd use the technology specific launcher to launch the application. These launchers do know exactly what is required to set it up and start the technology and the application. However, covering the launchers is out of scope for this post, so we will review them and their mechanics in a follow-up.

The simplest JVx application: Just the GUI

But first, we will start without anything. The most simple application you can create with JVx is an application which does open a single window and only works with in memory data (if at all). This can be easily achieved by "just starting" the application.

The JVx GUI is a simple layer on top of the Technology which implements the actual functionality. So if we want to have a GUI we'll need to initialize the factory before doing anything else:

UIFactoryManager.getFactoryInstance(SwingFactory.class);

With this little code we have initialized everything we need to create a simple Swing application. Now we can start to create and populate a window with something:

UIFrame frame = new UIFrame();
frame.setLayout(new UIBorderLayout());
frame.addComponent(new UILabel("Hello World!"));

frame.pack();
frame.setVisible(true);

frame.eventWindowClosed().addListener(() -> System.exit(0));

We can start to create and manipulate the GUI, in this case we are building a simple window with a label inside. Last but not least, we make sure that the JVM will exit when the window is closed.

A very good example and showcase for that is the JVx Kitchensink.

That's it. That is the most simple way to start a JVx application. We can use all controls and we can use MemDataBooks without any problem or limitation. And best of all, we can simply switch to another Technology by using another factory.

Anatomy of a remote JVx application

Of course JVx wouldn't be that useful if it would just provide static GUI components. Now, to explain what else is required for a remote JVx application I have to go far afield, so let's head down the rabbit hole.

JVx Layers

What you are seeing here is a rough sketch of how the architecture of JVx looks like. Let's walk through the image step by step. We will look at each successive layer and work our way from the database on the server to the databook on the client.

DBAccess, accessing a database

Accessing a database is easy when using DBAccess. All we must do is to set the JDBC URL of the server and connect to it:

DBAccess dbAccess = DBAccess.getDBAccess(
           "jdbc:postgresql://server:5432/schema",
           "username",
           "password");
dbAccess.open();

As a note, the instance returned by getDBAccess is the database specific DBAccess extension, which does know how to handle its database.

We can of course use DBAccess to directly access the database:

dbAccess.executeStatement("insert into SOME_TABLE values (?, ?);",
        BigDecimal.valueOf(1),
        "Some Value");

List<Bean> data = dbAccess.executeQuery("select * from SOME_TABLE");

...or manipulate the database, or query information about the database or execute procedures or do anything else.

DBStorage, preparing the database access for databooks

The downside of using DBAccess is that everything must be database specific. To become database agnostic we must use DBStorage. DBStorage does not care which database it is connected to and can operate on any of them:

DBStorage storage = new DBStorage();
storage.setDBAccess(dbAccess);
storage.setWritebackTable("SOME_TABLE");
storage.open();

We can use this to insert, update, delete and fetch data. Additionally the DBStorage does retrieve and manage the metadata of the table we've set, which means that we can query all column names, what type they are, we can even access the indexes and the default values. Short, the DBStorage leaves little to be desired when it comes to operating on a database.

If we query data from the DBStorage we receive a List of rows. The rows are are either represented as Object array, IBean or a POJO and we can easily manipulate the data, like this:

for (IBean row : storage.fetchBean(null, null, 0, -1))
{
    row.put("SOME_COLUMN", "newvalue");
    storage.update(row);
}

As one can see, it looks quite familiar to the DataBook, which isn't a coincidence. The DBStorage "powers" the DataBooks on the server side, a DataBook will get its data from and will send its modified data to the DBStorage.

I've been using the DBStorage here as an example, but actually the Storage is not dependent on a database. IStorage can be implemented to provide any sort of data provider, like reading from an XML or JSON file, scraping data from a website, fetching data from a different process or reading it directly from a hardware sensor.

Life Cycle Objects, the business objects with all the logic

Life Cycle Objects, or LCOs, are the server side business objects which contain and provide the business logic. They are created and destroyed as is requested by the client-side and are used to provide specific functionality to the client, like providing functionality specific to one screen or workflow. This is done by RPC, Remote Procedure Calls, which means that the client is directly calling the methods defined in the LCOs, which includes getting the Storages for the DataBooks.

There is also a security aspect to these, as you can permit one client access to a certain LCO but lock out everyone else, which means that only that client can use the functionality provided by the LCO.

But let's not get ahead of our selves, there are three important "layers" of LCOs which we will look at.

Application

The LCO for the application represents the application itself and provides functionality on the application layer. It is created once for the lifetime of the application and this instance is shared by all sessions.

public class Application extends GenericBean
{
}
Session

The LCO for the session represents one session, which most of the time also equals one client connection. It provides functionality which should be session-local, like providing the database connection which can be used.

public class Session extends Application
{
    protected DBAccess getDBAccess() throws Exception
    {
        // Code for initialization and caching of DBAccess goes here.
    }
}
Sub-Session aka Screen

The sub-session, also known as screen, LCO is the last in the chain. It provides functionality specific to a certain part of the application, like a single screen, and provides the storages required to power the databooks and other functionality.

public class MySubSession extends Session
{
    public DBStorage getTablename() throws Exception
    {
        // Code for initialization and caching of DBStorage goes here.
    }
}

Server, serving it up

There really isn't much to say about the server, it accepts connections and hands out sessions. Of course it is not that easy, but for this guide we will not go into any further detail.

Connection, connecting to a server

The connection which strings together the client and the server is used for the communication between them, obviously. It can be anything, from a simple direct connection which strings two objects together to a HTTP connection which talks with a server on the other side of the planet.

By default we provide different IConnection implementations, the DirectServerConnection, DirectObjectConnection, the HttpConnection and the VMConnection. The DirectServerConnection is a simple IConnection implementation which does simply forward method calls to known Objects - without serialization - and is used when the client and server reside inside the same JVM. The HttpConnection communicates with the server over a HTTP connection and is used whenever the client and server are not inside the same JVM. The DirectObjectConnection and VMConnection are used for unit tests.

As example we will use the DirectServerConnection, which serves as Server and Connection. It is used if the server and client reside in the same JVM.

IConnection connection = new DirectServerConnection();
// The connection will be automatically opened by the MasterConnection.

Master- and SubConnections, client-side lifecycle management

The MasterConnection is the main connection which is used to access the server and its functionality. When a MasterConnection is established, a Session LCO on the server is created.

MasterConnection masterConnection = new MasterConnection(connection);
masterConnection.open();

A SubConnection is a sub connection of the MasterConnection and allows to access specific functionality encapsulated in an LCO. When a SubConnection is established, the requested/specified LCO on the server is created and can be accessed through the SubConnection.

SubConnection subConnection = masterConnection.createSubConnection("MySubSession");
subConnection.open();

The SubConnection can now access the functionality provided by the Application, the Session and the LCO which was specified.

subConnection.callAction("doSomethingOnTheServer");

DataSource, preparing the connection for the databook

To provide data to the databooks we can use the connection which we've described earlier. However, the DataBook does not directly know about the connection, it expects an IDataSource, which is used as an intermediate:

IDataSource dataSource = new RemoteDataSource(subConnection);
dataSource.open();

Of course the RemoteDataSource is just one possible implementation of IDataSource which can be used to provide data to the DataBook.

DataBook, accessing data

And now we are at the other end of the chain, at the databook on the client side. We just need to tell our databook what datasource to use, and we are done.

RemoteDataBook dataBook = new RemoteDataBook();
dataBook.setDataSource(dataSource);
dataBook.setName("storagename");
dataBook.open();

The name of the DataBook is used to access the DBStorage object in the LCO provided by the datasource. The mechanism for that is a simple search for a getter with the set name.

Interactive Demo

Here is an interactive demo which allows you to explore the connections between the client and server side. The complement classes are always highlighted and you can click on the names of the objects to receive additional information about them.

The JVx application: Manual example

Now that we have seen all layers that make up the architecture of JVx, let us put all of that into code:

public class JVxLocalMain
{
    public static void main(String[] pArgs) throws Throwable
    {
        // ############################## Server ##############################
       
        // ----------------------------- DBAccess -----------------------------
       
        // The DBAccess gives us access to the database.
        DBAccess dbAccess = DBAccess.getDBAccess(
                "jdbc:h2:mem:database",
                "",
                "");
        dbAccess.open();
       
        // We'll insert some data for this example.
        dbAccess.executeStatement("create table if not exists TEST("
                + "ID int primary key auto_increment,"
                + "NAME varchar(128) default '' not null);");
        dbAccess.executeStatement("insert into TEST values (1, 'Name A');");
        dbAccess.executeStatement("insert into TEST values (2, 'Name B');");
        dbAccess.executeStatement("insert into TEST values (3, 'Name C');");
       
        // ---------------------------- DBStorage -----------------------------
       
        // Our sole storage.
        DBStorage testStorage= new DBStorage();
        testStorage.setDBAccess(dbAccess);
        testStorage.setWritebackTable("TEST");
        testStorage.open();
       
        // -------------------- LCO / Session / Application -------------------
       
        // We are skipping the LCO, Session and Application in this example.
       
        // ####################### Network / Connection #######################
       
        // For this example we are initializing a DirectObjectConnection, which
        // does not require a server.
        // It is designed to be used mainly for unit testing.
        DirectObjectConnection connection = new DirectObjectConnection();
        connection.put("test", testStorage);
       
        // ############################## Client ##############################
       
        // ------------------------- MasterConnection -------------------------
       
        MasterConnection masterConnection = new MasterConnection(connection);
        masterConnection.open();
       
        // -------------------------- SubConnection ---------------------------
       
        // We are skipping the SubConnection in this example.
       
        // ---------------------------- DataSource ----------------------------
       
        IDataSource dataSource = new RemoteDataSource(masterConnection);
        dataSource.open();
       
        // ----------------------------- DataBook -----------------------------
       
        RemoteDataBook dataBook = new RemoteDataBook();
        dataBook.setDataSource(dataSource);
        dataBook.setName("test");
        dataBook.open();
       
        // You can use the DataBook here.
       
        // Perform cleanup of all opened objects here.
    }
}

With this little example we have a completely working JVx application. We provide ways to create most of this out of the box and read most of it from configuration files, so there really is just little code to be written, see the JVx FirstApp as a perfect example for that. So there is rarely any need to write code like this, all you have to do is create a new application and start it.

Additionally, we could combine this long example with the simple one from before to initialize and create a GUI which could use our RemoteDataBook, like this:

// Insert after the RemoteDataBook has been created.

// Set the UI factory which should be used, in this case it is
// the SwingFactory.
UIFactoryManager.getFactoryInstance(SwingFactory.class);

UIFrame frame = new UIFrame();
frame.setLayout(new UIBorderLayout());
frame.add(new UITable(dataBook));

frame.pack();
frame.setVisible(true);

frame.eventWindowClosed().addListener(() -> System.exit(0));

Abstractions on every step

As you can see, you always have full control over the framework and can always tailor it to your needs. There is always the possibility to provide a custom implementation to fulfill your needs:

  1. Accessing a not supported database can be achieved by extending DBAccess
  2. Having a different service/way of providing data can be implemented on top of IStorage
  3. Supporting a different connection can be implemented on top of IConnection
  4. And a completely different way of providing data can be implemented on top of IDataSource

You can swap out every layer and provide custom and customized implementations which exactly work as you require it.

Just like that

Just like that we've walked through the whole stack of a JVx application, from the database which holds the data all the way to the client GUI. Of course there is much more going on in a full-blown JVx application, for example I've spared you here the details of the configuration, server, network and providing actual LCOs and similar. But all in all, this should get you going.

Alexa, shutter control

It's time for some words about my "shutter control" side project.

My house has electrical roller shutters with remote control units for every window. It was/is possible to close and open the shutters with these remote control units without any problems. But it wasn't possible to open/close them automatically because I don't own a central control unit. I'm not sure if such a central control unit is available for my "old" receiver modules?
Anyway, I'm a researcher and builder. So I built a central control unit based on a Raspberry Pi. I wrote about it in the past:

In the meantime, the project got a lux sensor to close the shutter if it's dark outside and it recognizes public holidays for later opening. It's not a good idea to open shutters on public holidays at 6 am :) The whole project is working very stable and it's so useful!

A few months ago, I heard about Google Home and Amazon Echo because both systems offer Voice control. I thought that voice control would be an awesome feature for my shutter control because sometimes it happens that my smartphone or remote control units are not at hand. And voice control is faster than using a smartphone or remote control. The only problem was that Google Home and Amazon Echo weren't available in Austria, not for German language and I didn't find any information about their APIs and whether if it's possible to develop addons. ... Sometimes you have to wait...

Things have changed in the meantime and both devices have APIs but only Amazon Echo is available for German language. I read some test reviews about both devices and Amazon is the pioneer. It supports more 3rd party devices than Home and looks proven. I had no preferences for Echo or Home, but Echo was available. The decision was simple :)

I made some simple tests with Echo because I tried to find out if it's really useful to have such a thing at home. And for sure, it is. It works really great and saves time :) It's great as replacement for standard radios or to stream your favourite music. It's also nice to listen to daily news or the weather forecast. A nice feature is the alarm clock!

But enough, it's a nice and useful device which simply works. The idea was voice control of my roller shutters.

Amazon Echo allows developers to create AddOns. Such AddOns are called Skills. It's not trivial but also not complex to create a custom Skill. The getting started is well documented and more documentation is available online. A developer forum exists and is active enough. It's really no problem to start but some pieces of the puzzle are unclear. I didn't find a detailed technical overview or didn't search long enough. It also was unclear where the Skills will be executed. I thought a skill is like an app and runs directly on the device... This was not the case.

Amazon Echo is a simple client and sends all requests to the amazon cloud. It handles responses but the brain is in the cloud. This means that all your services must be in the cloud. It's not a problem to host your own services in your own infrastrcuture but all your services have to be available via Internet. It's not possible to access a server in your Intranet directly. It's not possible to tweak with custom router configurations or custom dns records. Your services have to be available as cloud services. I found many solutions with service proxies or request forwarding but I didn't like this solutions because my home network is private.

But this was the only limitation and not really a problem because we have technologies like MQTT. I didn't use MQTT in the past but read a lot about it. The problem was that I didn't have a use-case for it. This was changed with Echo.

I played around with Mosquitto some hours and after TLS with and without user certificates were working, I was ready for connecting my shutter client. I don't write about my Mosquitto configuration here because there are so many good blog entries available in the wild.
It might be interesting that I use Eclipse paho as Java client library.

Ooh... and this utility class was really helpful. It made it possible to communicate secure to my Mosquitto broker. The class didn't support authentication without user certificate and it didn't read from InputStreams, but it was a good starting point. It also didn't work in with my Jetty application server. Here's a snippet of my code:

public static SSLSocketFactory getSocketFactory(Object pCACrt, Object pCrt,
                                Object pKey, String pPassword)
{
    try
    {
        // Load Certificate Authority (CA) certificate
        PEMParser reader = new PEMParser(createReader(pCACrt));
        X509CertificateHolder caCertHolder = (X509CertificateHolder)reader.readObject();
        reader.close();

        JcaX509CertificateConverter conv = new JcaX509CertificateConverter();
       
        X509Certificate caCert = conv.getCertificate((X509CertificateHolder)caCertHolder);
       
        // CA certificate is used to authenticate server
        KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        caKeyStore.load(null, null);
        caKeyStore.setCertificateEntry("ca-certificate", caCert);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                                TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(caKeyStore);
       
        // Create SSL socket factory
        SSLContext context = SSLContext.getInstance("TLSv1.2");

        if (exists(pCrt))
        {
            // Load client certificate
            reader = new PEMParser(createReader(pCrt));
            X509CertificateHolder certHolder = (X509CertificateHolder)reader.readObject();
            reader.close();

            X509Certificate cert = conv.getCertificate(certHolder);

            // Load client private key
            reader = new PEMParser(createReader(pKey));
            Object keyObject = reader.readObject();
            reader.close();

            PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder().
                                build(pPassword.toCharArray());
            JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter().setProvider("BC");

            KeyPair key;

            if (keyObject instanceof PEMEncryptedKeyPair)
            {
                key = keyConverter.getKeyPair(((PEMEncryptedKeyPair)keyObject).
                                decryptKeyPair(provider));
            }
            else
            {
                key = keyConverter.getKeyPair((PEMKeyPair)keyObject);
            }

            // Client key and certificates are sent to server so it can authenticate
            // the client
            KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            clientKeyStore.load(null, null);
            clientKeyStore.setCertificateEntry("certificate", cert);
            clientKeyStore.setKeyEntry("private-key", key.getPrivate(),
                                pPassword.toCharArray(), new Certificate[] { cert });

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
                                KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientKeyStore, pPassword.toCharArray());
           
            context.init(keyManagerFactory.getKeyManagers(),
                                trustManagerFactory.getTrustManagers(), null);
        }
        else
        {
            context.init(null, trustManagerFactory.getTrustManagers(), null);
        }

        return context.getSocketFactory();
    }
    catch (Exception e)
    {
        throw new RuntimeException(e);
    }
}

So, the communication via MQTT was configured and ready to use. The next step was the creation of a Skill. Echo supports using external services via https. The Skills-API comes with a special Servlet, the SpeechletServlet. You have to extend this servlet for every service. This creates boilerplate code because the servlet does nothing special. It usually configures the Speechlet. A Speechlet is more or less the main class for your service. So I decided to create a generic Servlet:

public class GenericServlet extends SpeechletServlet
{
    public void init(ServletConfig pConfig) throws ServletException
    {
        String sSpeechlet = pConfig.getInitParameter("speechlet");
       
        try
        {
            Class<?> clazz = Class.forName(sSpeechlet);
           
            Object obj = clazz.newInstance();
           
            if (obj instanceof Speechlet)
            {
                setSpeechlet((Speechlet)obj);
            }
            else
            {
                setSpeechlet((SpeechletV2)obj);
            }
        }
        catch (Exception e)
        {
            throw new ServletException("Can't init speechlet, e");
        }
       
        super.init(pConfig);
    }

}   // GenericServlet

Simple configuration in web.xml and no additional servlets needed:

<servlet>
    <servlet-name>ShutterService</servlet-name>
    <servlet-class>com.sibvisions.alexa.services.GenericServlet</servlet-class>

  <init-param>
    <param-name>speechlet</param-name>
    <param-value>com.sibvisions.alexa.services.shutter.ShutterSpeechlet</param-value>
  </init-param>
</servlet>

The Skill itself doesn't need source code. Amazon offers a web UI for the configuration and a simple test tool. The creation was straight forward and I simply followed an example from the Skills-API package. One tricky step was the certificate configuration, but Amazon supports self-signed certificates and wildcard certificates without problems. So, the Skill creation was done very fast because everything was done online without coding.

The custom service creation wasn't very difficult because the API is really simple and doesn't need more than implementing 4 interface methods:

@Override
public void onSessionStarted(SessionStartedRequest pRequest, Session pSession) throws SpeechletException
{
}

@Override
public void onSessionEnded(SessionEndedRequest pRequest, Session pSession) throws SpeechletException
{
}

@Override
public SpeechletResponse onIntent(IntentRequest pRequest, Session pSession) throws SpeechletException
{
    Intent intent = pRequest.getIntent();
   
    String intentName = (intent != null) ? intent.getName() : null;

    TranslationMap tmap = getTranslation(pRequest);
   
    if ("DownIntent".equals(intentName)
        || "UpIntent".equals(intentName)
        || "HaltIntent".equals(intentName))
    {
        String sText;

        try
        {
            if ("DownIntent".equals(intentName))
            {
                client.down();
               
                sText = "The shutters are moving down!";
            }
            else if ("UpIntent".equals(intentName))
            {
                client.up();
               
                sText = "The shutters are moving up!";
            }
            else
            {
                client.halt();
               
                sText = "The shutters are stopping!";
            }
        }
        catch (Exception e)
        {
            sText = "Shutter control not possible!";
        }
       
        sText = tmap.translate(sText);
       
        // Create the Simple card content.
        SimpleCard card = new SimpleCard();
        card.setTitle(tmap.translate("Shutter control"));
        card.setContent(sText);

        // Create the plain text output.
        PlainTextOutputSpeech speech = new PlainTextOutputSpeech();
        speech.setText(sText);

        return SpeechletResponse.newTellResponse(speech, card);
    }
    else
    {
        throw new SpeechletException(tmap.translate("Invalid intent!"));
    }
}

@Override
public SpeechletResponse onLaunch(LaunchRequest pRequest, Session pSession) throws SpeechletException
{
    TranslationMap tmap = getTranslation(pRequest);
   
    String sText = tmap.translate("Here we go!");
   
    // Create the Simple card content.
    SimpleCard card = new SimpleCard();
    card.setTitle(tmap.translate("Shutter control"));
    card.setContent(sText);

    // Create the plain text output.
    PlainTextOutputSpeech speech = new PlainTextOutputSpeech();
    speech.setText(sText);

    PlainTextOutputSpeech repromptSpeech = new PlainTextOutputSpeech();
    repromptSpeech.setText(tmap.translate("Tell me the direction!"));
   
    Reprompt reprompt = new Reprompt();
    reprompt.setOutputSpeech(repromptSpeech);

    return SpeechletResponse.newAskResponse(speech, reprompt, card);
}

It's also possible to use Amazons infrastructure for your services, but I have my own application server.

After some days, my roller shutters were controlled by Amazon Echo aka Alexa with voice commands. It was really cool and simple!

Here's a demonstration of the result (it's in German):

Alexa in action

I can only recommend this device!

Three years SIB Visions, a look back and ahead

My third year at SIB Visions is now coming to an end, once more it is time to take a deep breath, sit down and have a look at everything that has happened so far.

My year has been filled with a lot of non-public work, so please forgive me if I cannot go into details on various fronts.

Prototypes

Quite a few customers approached us and asked if we could implement prototypes for their projects, for one reason or another they all ended up on my table, the prototypes, that is. I like working on prototypes, because they allow me to quickly and easily explore new concepts and they have a definitive, narrow scope. Also we can finally use and show off all these cool new features we have built, like the new client-side layouts which we've created for our Vaadin implementation.

In the course of the year I've worked on over half a dozen prototypes for different customers. They all came with their own set of requirements and goals, obviously, and so they were very different to work on. The most interesting part about these prototypes is the vast spectrum of different designs and ideas. I unfortunately can't go into much detail here, but we've created application frames ranging from simple Swing input forms to highly customized Vaadin dashboards and information screens. Further down I'll talk a little more about these concepts and ideas.

Improved MySQL Fetching

Our MySQL implementation did suffer from a major problem, it fetched all records, always. That is not just an inconvenience as you can imagine, it might also lead to OutOfMemoryExceptions and similar problems. So one fine day we sat down and did take a deep look at our possibilities to either mitigate or completely fix this problem. As it turned out, the fix was far simpler and easier to implement than we imagined.

To cut an already told long story short, we are now utilizing the MySQL support to limit the fetched datasets (the LIMIT keyword). Which means that we are not only able to display initial data faster, but also fetching of more rows is now working as one would expect.

Impressions

As you can see, we can now display initial data a lot quicker than before.

Vaadin Client-Side Layouts

More work has been poured into the aforementioned new client-side layouts and they are now usable for testing and (limited) productive usage.

Since the beginning we had trouble to sculpt the Vaadin layouts into the necessary shape to support our ideas about layouts. Over a year ago we started to work an new layout implementations which would operate completely client-side, most of the work was based off earlier work to improve the FormLayout. These new layouts have no logic on the server-side, but instead operate completely client-side only using "hints" from the server. That means that one can implement quite complicated layout logic (like our FormLayout) with ease.

We've implemented a new component, the so called LayoutedPanel, which can have different layouts. It does provide the base component for the new layouts but does not provide any logic itself. Instead it delegates all the layouting logic to layout implementations, that means it basically works the same as the Panel from AWT/Swing. Performing the layout logic on the client has multiple upsides, for example that the actual size of the components is known and that you can do it with a lot less elements.

Let's have a short look at how the usage of the LayoutedPanel looks like in pure Vaadin:

// Assume the following classes:
//    com.sibvisions.rad.ui.vaadin.ext.ui.panel.LayoutedPanel
//    com.sibvisions.rad.ui.vaadin.ext.ui.panel.layout.FormLayout
//    com.vaadin.ui.Label

FormLayout layout = new FormLayout();

LayoutedPanel panel = new LayoutedPanel();
panel.setLayout(layout);

panel.addComponent(new Label("Some label"), layout.getConstraints(0, 0));
panel.addComponent(new Label("On the right side"), layout.getConstraints(-1, 0));

As you can see, it is quite easy to use and works nearly identical to our panel/layouts. Let's take a further look into what is happening on the server and what is happening on the client.

Server

On the server the LayoutedPanel manages the list of components which have been added, what layout is currently in use and the constraints of the added components. There are layout classes/implementations on the server, but they are mere placeholders (so that the LayoutedPanel on the client side knows which layout to use) and provide only limited logic (for example how to write the constraints into the state which is send to the client).

Client

On the client side there is the ClientSideLayoutedPanel, with some basic logic for adding the components to the DOM and layouting, but does otherwise delegate all the work to the current layout. The DOM structure created consists of two divs, the main element and the "canvas" element:

  • panel
    • canvas
      • child
      • child
      • child
      • child

As mentioned in my last years post, the panel must fulfill two seemingly contradicting properties:

  1. It must resize itself to the size of/fill the parent container.
  2. It must dictate its own size.

This is necessary because the panel always must fill the parent, but at the same time it must dictate its own size so that the parent does not collapse if it does not have a fixed size set. The next stumbling block on the road was that the children of that panel would be absolute positioned. That means that the panel itself would not have a size because absolute positioned children are skipped when it comes to calculating the size of an element. Quite complex, but there is a rather simple solution:

  • panel (width: 100%, height: 100%)
    • canvas (min-width: 100%, min-height: 100%)
      • child (position: absolute)

Additionally, the size of the canvas is always set to the same size as the panel, to prevent the element from collapsing. There is of course more logic in place to make it all feasible and usable, but I won't go into any further detail here.

The layouts itself are simple to implement, they have a list of components and the size of the container, and they set the location and size of each component.

Summary

For everyone adventurous and daring enough, the source for the new layouts is available in the com.sibvisions.rad.ui.vaadin.ext.ui.panel and com.sibvisions.rad.ui.vaadin.ext.ui.client.panel packages in the JVx.Vaadin project.

For those who want to give these new layouts a spin in their application, you must simply set a property on the VaadinFactory to enable them:

UIFactoryManager.getFactory().setProperty(
    VaadinFactory.PROPERTY_COMPONENT_CLIENT_LAYOUTS,
    Boolean.TRUE);

Or if you don't want to import VaadinFactory:

UIFactoryManager.getFactory().setProperty(
    "vaadin.component.client_layouts",
    Boolean.TRUE);

From that moment on, every layout created will use the client-side layouts. Of course you can always set it to false or true inside your code, to get either new or old layouts.

Swing GridLayout improvements

We've also improved the GridLayout in Swing, previously the Swing GridLayout would show gaps every time it is resized. After some back and forth, we've managed to implement a new way to prevent/fill these gaps. That means that the Swing GridLayout is now distributing "left over space" through out its entirety, instead of just to the last column. This creates a very "organic" feel when it is resized and it is barely noticeable that some elements are a pixel wider than others.

More databases

Hurray! Over the course of the year we've added two new databases to JVx!

SQLite

The first one is SQLite. In case you don't know what SQLite is, it is a simple, lightweight and easy to use database which is embedded into the application which uses it. That means there is no server or connection, not even a different thread for the database engine, there is just the driver which does everything. This is interesting for many reasons, but the main one is to allow embedded applications to use a lightweight database. It also supports an in-memory mode, which means that it never must write the data to any kind of storage.

We've added support for it to allow JVx applications to use it as fast and easy database backend whenever there are either system constraints (little memory or CPU power) or if there is no network connection. It can be easily used, like any other database which is supported by JVx:

DBAccess dbAccess = DBAccess.getDBAccess("jdbc:sqlite:");
dba.setUrl("jdbc:sqlite:/var/data/your-database.sqlite");
dba.open();

// Or in-memory:
DBAccess dbAccess = DBAccess.getDBAccess("jdbc:sqlite:");
dba.setUrl("jdbc:sqlite::memory:");
dba.open();

Because SQLite is designed to be used in embedded applications (or at least embedded in applications) it has quite limited functionality, like no triggers, functions or sequences. But nothing beats the minimal footprint and ease of use it has.

H2

Another database which we now support is H2. H2 is a database written completely in Java, which can be started as a server or embedded into an application directly. Last but not least, it does also support an in-memory mode and nearly most features you got used to when using databases. It can be easily used, like any other database which is supported by JVx:

DBAccess dbAccess = DBAccess.getDBAccess("jdbc:h2:");
dba.setUrl("jdbc:h2:/var/data/your-database.h2");
dba.open();

// Or in-memory:
DBAccess dbAccess = DBAccess.getDBAccess("jdbc:h2:");
dba.setUrl("jdbc:h2:mem:your-database");
dba.open();

Personally, I'm quite excited to support the H2, because it is a very easy to use and yet powerful and flexible database engine. I'm looking forward to use it in projects.

Intern

For the summer we actually had an intern which wanted to take a sneak peek into the world of software development. As it is with interns (at least around these parts), they are young, foolish and shy. What took me by surprise about this one was not only the eagerness with which he set to work, but also the will to learn new things as fast as possible. No matter how much information I threw at him (not literally, though, I was disallowed that, too) in the form of books and short lectures, he seemed to be able to digest and at least follow it, that impressed me very much. With very, very few exceptions ("Did you just fetch a Timestamp from a DataBook as String, parse that String to a Date and then convert it back to a Timestamp?") he was up to speed with Java and JVx in no time and wrote very good code. As I said, I am very impressed with how he held up.

Daniel, was a honor having you!

EPlug 1.2.5

Of course the year also brought more work on our Eclipse plugin, called EPlug, and the update to 1.2.5 was released in late summer.

Some of the highlights are:

  • Better hover information/tooltips
  • More and better ways to retrieve metadata
  • More options to control the errors/warnings issued by EPlug
  • QuickFixes for misspelled column names
  • And various more improvements and bug fixes

The most important new feature in this release might be the extended and improved tooltips, which look like this:

New hover information

For this to work we had to rewrite how tooltips were presented to the user. For those familiar with the Eclipse tooltip system, there is the IInformationControl interface which allows to display tooltips and similar. In previous versions we used an implementation which embedded a HTML renderer into the tooltip. That allowed the clients to have absolute control over the content, but it also meant that there were sever restrictions, for example autosizing the tooltip was tough.

With this version we used a new implementation which uses plain old SWT components and displays well prepared information, like this list of properties. This also has the upside that we now have absolute control over how images are treated, so they are now always fully displayed in the preview:

New hover information

All that is accomplished by a rather simple system. It's nothing more than a control with an icon (top-left), a title (top) and the content, which can either be an object or an image. If it is an object there are just a few ifs which are determining how to display that information.

What I took away from that was a quite important lesson. The initial implementation tried to be as flexible as possible by using a HTML rendering engine, the new implementation is not as flexible, but has a clear and well defined behavior and does have definitive advantages. So my advice is one that has been repeated way too often: YAGNI, You Ain't Gonna Need It. Start out small, and start building up as the need arises.

Oracle Forms Importer Improvements

As some of you might remember, one of the first tasks I was assigned to was to create an Oracle Forms Importer, which we do offer to VisionX customers. It is a rather simple piece of software which allows to import Forms directly into VisionX.

In autumn we took another shot at it and improved the import system, so that it now recognizes and correctly imports an even more varied range of forms.

Some of the major changes are:

  • Better recognition of bound controls
  • Improved setup of cell editors
  • Various other layout fixes (for example checkboxes are now correctly size)
  • And other improvements (for example the table column order is now correct)

Importing a form is still quite because of the outlined differences between Forms and JVx, but I believe that we are on a very good track and the current solution works quite well for a wide variety of forms.

Kitchensink improvements

Our Kitchensink, a simple demo application with all components and controls, has seen some improvements throughout the last year:

  • JVx has been updated
  • There are now all libraries and starters in place to get it to run
  • A new sample showing the FontAwesome icons
  • Improved the StyledTableSample and StyledTreeSample
  • Various minor improvements and more options

The Kitchensink is an Eclipse project, that means all you have to do is download or clone the source and import the project into Eclipse. With the included launchers you can now start it either with Swing or JavaFX frontend.

We are currently considering to embed a lightweight application server, so that testing the Vaadin frontend also becomes as easy as starting the Swing variant. That would mean that all three major frontends could be easily tested from the Kitchensink, which would be awesome.

Custom components

Some of our customers requested that we build custom components for them, these component should work in Swing and Vaadin in the same way. As I've outlined in a previous blog post, this is actually easily done depending on what your exact use-case is.

One of the most notable examples is a calendar which we've built on the technology layer. For Swing we've used the java-swing-calendar project and for Vaadin the Vaadin Calendar. Unfortunately, Vaadin has discontinued their Calendar for the time being. Anyway, we've implemented the calendar component and also added bindings for the databook to provide events, which was quite straight forward.

I do have to rant about the Vaadin Calendar, though. It does support two sources for the data, the Container interface and a calendar specific CalendarEventProvider interface. As we already had an implementation for a Container, I used that one and adjusted it so that it could be easily used with the Calendar. All fine and dandy, until I started to work with events, which are only providing a CalendarEvent as information on what event is affected. This CalendarEvent has zero information about the model, and I mean zero. There is no way to track the model from the CalendarEvent, you just don't know what event just was clicked or dragged. The only way is to not use a Container and instead use a CalendarEventProvider which returns custom CalendarEvent extensions which know about the model.

Otherwise implementing these components was very smooth.

EPlug 1.2.6

At the beginning of this year, we did some more work on EPlug and we could release EPlug 1.2.6 after a short time:

  • Cleaned up context menu
  • Resources (Images, etc.) are now also available from outside the source folders
  • Even better handling of DataBooks
  • Some bug fixes and other new features

The updates are available in the marketplace, so Eclipse can automatically update to it.

JVx References

For the last few months I have started to write a series of blog posts about various topics around JVx, the JVx Reference series. The goal is to provide a concise and easy to digest insight into JVx and various of its mechanics and techniques. There are currently three posts published:

And there are more in the pipeline:

  • About DataBooks
  • An Overview of how JVx works
  • What Resources and UIResources are in JVx

So stay tuned!

Demos

Out of the work on prototypes for our customers arose the idea to provide quite a few these new ideas to VisionX users in the form of demo projects. We've prepared these demo projects over the last few weeks and they will be released to the VisionX solution store within the next time.

Here is a sneak peek at those:

Demo BIELS

Demo AMLD

Demo CNASP

Demo BIELS

As said, these demo applications will be available in the VisionX Solution Store.

The look ahead

Wow, that has been an interesting year. A lot of things happened and we made a lot more happen, with even more stuff in the pipeline and yet even more stuff on the horizon. From where I'm sitting, the next year looks very interesting with a lot of new challenges, which we will for sure master.

Thanks to everyone at SIB Visions, it's been an awesome year and I'm looking forward to another one with all of you!

Simple Drop File Support for JVx applications

Our next update release of VisionX will support Dropping files. It's a very useful feature and was easy to implement. Sure, it's a Swing specific feature, but our VisionX is more or less JVx Swing UI based.

Get a first impression

VisionX Drop file support

We drop an exported application archive into VisionX and the import wizard is starting. It's also possible to Drop a file directly into the import wizard.

VisionX is a JVx application and it's super easy to implement such feature for your own JVx application. Here's a code snippet how it'll work:

public SimpleFileDropHandler addTarget(IComponent pTarget, IFileHandleReceiver pListener,
                                       String... pExtension)
{
    Object oResource = pTarget.getResource();
   
    if (!(oResource instanceof JComponent) && !(oResource instanceof JFrame))
    {
        throw new IllegalArgumentException("Given object can't be a drop target!");
    }
   
    SimpleFileDropHandler handler = new SimpleFileDropHandler(pListener, pExtension);
   
    if (oResource instanceof JFrame)
    {
        ((JFrame)oResource).setTransferHandler(handler);
    }
    else
    {
        JComponent jcomp = getComponent((JComponent)oResource);
       
        if (jcomp != null)
        {
            jcomp.setTransferHandler(handler);
        }
    }
}

private JComponent getComponent(JComponent pComponent)
{
    if (pComponent instanceof JVxEditor)
    {
        JComponent comp = ((JVxEditor)pComponent).
                          getCellEditorHandler().getCellEditorComponent();
       
        if (comp instanceof JScrollPane)
        {
            Component cView = ((JScrollPane)comp).getViewport().getView();
           
            if (cView instanceof JComponent)
            {
                return ((JComponent)cView);                    
            }
            else
            {
                return null;
            }
        }
        else
        {
            return comp;
        }
    }
    else
    {
        return pComponent;
    }        
}

In principle, we set the TransferHandler for a JComponent. Above code detects the right JComponent because there's a difference if you use an IEditor.

The TransferHandler could be implemented like our SimpleFileDropHandler

public class SimpleFileDropHandler extends TransferHandler
{
    private IFileHandleReceiver listener;
   
    private String[] extensions;
       
    public SimpleFileDropHandler(IFileHandleReceiver pListener, String... pExtension)
    {
        listener = pListener;
        extensions = pExtension;
    }
   
    @Override
    public boolean canImport(TransferHandler.TransferSupport pSupport)
    {
        if (!pSupport.isDrop())
        {
            return false;
        }

        if (!pSupport.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
        {
            return false;
        }

        boolean copySupported = (COPY & pSupport.getSourceDropActions()) == COPY;
       
        if (copySupported)
        {
            pSupport.setDropAction(TransferHandler.COPY);
            return true;
        }            
       
        return false;
    }
   
    @Override
    public boolean importData(TransferHandler.TransferSupport support)
    {
        if (!support.isDrop())
        {
            return false;
        }

        List<File> files;
        try
        {
            files = (List<File>)support.getTransferable().
                    getTransferData(DataFlavor.javaFileListFlavor);
        }
        catch (UnsupportedFlavorException ex)
        {
            // should never happen (or JDK is buggy)
            return false;
        }
        catch (IOException ex)
        {
            // should never happen (or JDK is buggy)
            return false;
        }
       
        if (listener != null)
        {
            for (File file : files)
            {
                try
                {
                    listener.receiveFileHandle(new FileHandle(file));
                }
                catch (Exception e)
                {
                    ExceptionHandler.raise(e);
                }
            }
        }
       
        return true;
    }
}

Have fun ;-)