Webcentric

Webcentric is a layout manager for positioning your components on screen in the main application.

Objectives

In this tutorial you will create various layouts and position the trade tile component inside a Webcentric application.

Setup

The application which you imported before starting this set of tutorials contains a set of classes, themes, and configuration required to be able to start a Webcentric application. This includes the WebcentricManager class which is used to launch and stop the Webcentric application.

The WebcentricManager class needs to know the username of the currently logged in user in order to display their layouts, as such it has a dependency on the UserService. We already saw in the last tutorial how to register the StreamLinkUserService as the implementation of UserService for our application.

As a first step, we are going to update the application so that rather than display three tiles it launches with an empty Webcentric layout.

Open CaplinTrader/apps/TutorialApp/default-aspect/src/mycompany/App.js.

You will recall from the previous tutorial that we have a callback method onServicesInitialisedSuccess(), which is invoked when the services have initialised. We are currently using this callback to create three tiles. Replace this tile creation with a call to the existing method startWebcentric().

mycompany.App.prototype._onServicesInitialisedSuccess = function()
{
    this._startWebcentric();
};

Refresh the application. You will now see a blank Webcentric layout.

tutorial webcentric blank layout

Create a simple layout

Now we will edit our empty layout so that it contains one tile.

The XML configuration for the blank layout resides in CaplinTrader/apps/TutorialApp/default-aspect/webcentric/layout/layouts/blank_layout.xml.

Modify this layout to include a single Presenter component, which is our tile, by changing the content to the below. Note that we have to change the top level element from a vanilla Webcentric Panel to a caplin:Panel, which extends the basic Panel with extra functionality.

<caplin:Panel caption="My Tile" xmlns:caplin="http://schema.caplin.com/CaplinTrader/webcentric">
    <state>
        <br.presenter-component templateId="mycompany.tutorial.tile.tile-template" presentationModel="mycompany.tutorial.tile.TilePresentationModel" />
    </state>
</caplin:Panel>

Don’t try refreshing the application yet, you won’t see any changes.

Reset the Webcentric Database

The Webcentric layouts are defined in XML files but must be loaded into the Webcentric database in order for the application to user them. So in order to see an altered layout on screen, you’ll first need to load your updated XML into the database by resetting it. When you reset the Webcentric database, it is wiped and repopulated using the various layout XML files.

You can reset the Webcentric database for your application in two ways. You can open a command prompt, navigate to the CaplinTrader/sdk directory and run this command:

bladerunner reset-db TutorialApp

Or you can switch back to the dashboard in your browser and click the Reset DB button in the top right corner.

tutorial webcentric reset db button

Note that when you reset your database you will lose any layouts which you have created and saved via the Layout > Save As in the context menu. The only layouts available after a database reset will be those that are loaded from your XML configuration.

When you are developing your application using Caplin Trader 3, the Webcentric database is a local in-memory H2 database. This is basically a throw-away database only for local development, so you can freely wipe it and recreate it whenever you make a change to some layout XML.

For a real production environment there will be a persistent Webcentric database somewhere in the customer’s network, probably using something like Microsoft SQL Server or Oracle. The connection between your Caplin Trader application and the Webcentric database is fully configurable.

Of course, in a real environment you would always run SQL updates on the Webcentric database rather than wipe and recreate it, because it will contain all of the customer’s saved layouts.

Fix the application

After you reset the Webcentric database you can refresh your application. You will see an error message, and if you open the console you will see that the root cause of the error is something like this:

Cannot read property 'substring' of undefined

If you look at CaplinTrader/apps/TutorialApp/tutorial-bladeset/blades/tile/src/mycompany/tutorial/tile/TilePresentationModel.js you will see the problem. The constructor of the class expects to be given the currency pair as a string, and then it determines the dealt currency by taking the first three characters:

mycompany.tutorial.tile.TilePresentationModel = function(currencyPair)
{
    this.currencyPair = new caplin.presenter.property.EditableProperty(currencyPair);
    this.dealtCurrency = new caplin.presenter.property.EditableProperty(currencyPair.substring(0,3));
    ...
}

This worked fine when we were explicitly instantiating this class in App.js and passing in a currency pair string, but now the tiles are being instantiated by Webcentric. Webcentric cannot pass your class any constructor arguments, so the currency pair is coming through as undefined. When the code tries to execute the substring() method on undefined, it blows up with the error you are seeing in your browser console.

It is possible to make Webcentric provide arguments to your components, and we will cover this later. For now, we will hack in a quick fix to hard-code the currency pair if it is not provided. Add the line in bold to your constructor:

mycompany.tutorial.tile.TilePresentationModel = function(currencyPair)
{
    currencyPair = currencyPair || "GBPUSD";
    this.currencyPair = new caplin.presenter.property.EditableProperty(currencyPair);
    this.dealtCurrency = new caplin.presenter.property.EditableProperty(currencyPair.substring(0,3));
    ...
}

If you’re not familiar with this sort of JavaScript syntax, it effectively just means "if the value of currencyPair is undefined, set it to GBPUSD".

Now you can refresh the application and you should see a working GBPUSD tile. Note that you didn’t have to reset the Webcentric database before refreshing the application because you didn’t change the layout XML, you just changed some code.

More complex layouts

Now that we know how to create a layout containing a tile, we can utilise the key feature of Webcentric which is creating complex layouts.

Here is a list of layouts we could implement:

  1. Two horizontally aligned panels i.e. a Terrace

  2. Two vertically aligned panels i.e. a Tower

  3. Two tabbed panels i.e. a Stack

  4. A complex layout including a combination of Terraces, Towers and Stacks.

For the first one, we will provide the code. Update your blank_layout.xml so that it looks like this:

<Terrace splitters="true" xmlns:caplin="http://schema.caplin.com/CaplinTrader/webcentric">
    <FrameItems>
        <caplin:Panel caption="Tile One">
            <state>
                <br.presenter-component templateId="mycompany.tutorial.tile.tile-template" presentationModel="mycompany.tutorial.tile.TilePresentationModel"/>
            </state>
        </caplin:Panel>
        <caplin:Panel caption="Tile Two">
            <state>
                <br.presenter-component templateId="mycompany.tutorial.tile.tile-template" presentationModel="mycompany.tutorial.tile.TilePresentationModel"/>
            </state>
        </caplin:Panel>
    </FrameItems>
</Terrace>

Remember, you must reset the Webcentric database before you refresh your application to see the results. You should see that the application is now divided into two vertical panels with a draggable splitter between them, each containing an instance of our tile.

Note that Webcentric automatically calculates how much of the screen to allocate each component. In this case we didn’t specify any sizes on the two components in our Terrace, so Webcentric just gave them half of the space each. You can fine-tune the size by, for example, specifying that one component takes 20% (or a pixel amount) of the space and the other takes the rest, but that is not in scope for this tutorial.

Have a go at the other three layout tasks. You can experiment with creating your own layout for the fourth task. One thing to watch out for when you attempt task three is that you must give your Stack element the attribute decorators="stackDecorator" or you won’t be able to see it.

You will probably find this page useful to refer to.

Again, don’t forget that you must reset the Webcentric database after each change to the blank_layout.xml in order to see your changes.

Deserializing components

Recall that earlier in the tutorial we hard-coded a currency pair into our tile. It would be better if the currency pair for each tile could be specified in the layout XML.

Webcentric uses the concept of serializing and deserializing for all of the components it manages. This means that each component can be responsible for:

  • Serialization: creating an XML representation of itself. Webcentric will ask the component to do this when the user saves their layout, and will store the returned XML into the database.

  • Deserialization: instantiating itself to the correct state based on the XML. Webcentric will ask the component to do this when the application launches and the saved state must be recreated.

We will upgrade our tile so that it can deserialize itself into a tile with the correct currency pair, based on some XML.

Follow the steps below:

  1. First, let’s update our blank_layout.xml so that it contains a Terrace with two tiles, one for GBPUSD and one for USDJPY. Note the new XML elements in bold.

    <Terrace splitters="true" xmlns:caplin="http://schema.caplin.com/CaplinTrader/webcentric">
        <FrameItems>
            <caplin:Panel caption="Tile One">
                <state>
                    <br.presenter-component templateId="mycompany.tutorial.tile.tile-template" presentationModel="mycompany.tutorial.tile.TilePresentationModel">
                        <currencyPair>GBPUSD</currencyPair>
                    </br.presenter-component>
                </state>
            </caplin:Panel>
            <caplin:Panel caption="Tile Two">
                <state>
                    <br.presenter-component templateId="mycompany.tutorial.tile.tile-template" presentationModel="mycompany.tutorial.tile.TilePresentationModel">
                        <currencyPair>USDJPY</currencyPair>
                    </br.presenter-component>
                </state>
            </caplin:Panel>
        </FrameItems>
    </Terrace>
  2. Reset the Webcentric database. You can refresh the application but there is no change in behaviour yet, our tile is not dealing with the new information in the layout XML.

  3. Open CaplinTrader/apps/TutorialApp/tutorial-bladeset/blades/tile/src/mycompany/tutorial/tile/TilePresentationModel.js. Since we won’t know which currency pair we are supposed to subscribe to until Webcentric calls our deserialize() method (which we haven’t implemented yet), we need to make some changes to our constructor. We will no longer expect a currency pair argument, and we will initialise our currency pair and dealt currency to empty strings.

    The other thing we must do is remove the two lines at the bottom of the constructor that subscribe to VIEW permissions for the currency pair, because we don’t know what it is yet! When you’ve finished editing the constructor it should look like this:

    mycompany.tutorial.tile.TilePresentationModel = function()
    {
        this.currencyPair = new caplin.presenter.property.EditableProperty('');
        this.dealtCurrency = new caplin.presenter.property.EditableProperty('');
    
        this.amount = new caplin.presenter.node.Field(50000);
        this.amount.value.addValidator(new caplin.core.validator.NumericValidator("Invalid Entry. Must be a number"));
        this.amount.value.addValidationErrorListener(this, "_onAmountError");
        this.amount.value.addValidationSuccessListener(this, "_onAmountCorrect");
        this.invalidAmountInput = new caplin.presenter.property.EditableProperty(false);
        this.buyRate = new caplin.presenter.node.Field('--');
    
        this.sellRate = new caplin.presenter.node.Field('--');
        this.message = new caplin.presenter.property.WritableProperty('no trade');
    
        var oAllProperties = this.properties();
        this.m_oSnapshot = oAllProperties.snapshot();
    
        this.m_oConnectionService = caplin.core.ServiceRegistry.getService("caplin.connection-service");
        this.m_oConnectionService.addStatusChangedListener(this, true);
    };
  4. If you like, you can refresh the application at this point. You should see two empty tiles with no rates, waiting to be told which currency pair to subscribe to.

  5. In order to handle deserialization we must implement yet another interface, called SerializablePresentationModel. Add this line to the block of implement statements under the constructor:

    caplin.extend(mycompany.tutorial.tile.TilePresentationModel, caplin.presenter.PresentationModel);
    caplin.implement(mycompany.tutorial.tile.TilePresentationModel, caplin.services.messaging.SubscriptionListener);
    caplin.implement(mycompany.tutorial.tile.TilePresentationModel, caplin.trading.statemachine.StateChangedListener);
    caplin.implement(mycompany.tutorial.tile.TilePresentationModel, caplin.services.ConnectionServiceListener);
    caplin.implement(mycompany.tutorial.tile.TilePresentationModel, caplin.services.security.PermissionServiceListener);
    caplin.implement(mycompany.tutorial.tile.TilePresentationModel, caplin.presenter.SerializablePresentationModel);
  6. The SerializablePresentationModel interface defines two methods, serialize() and deserialize(). The second one is what we need to implement in order for Webcentric to give us the XML containing the currency pair. Implement the method like this:

    mycompany.tutorial.tile.TilePresentationModel.prototype.deserialize = function(sXml) {
        var xmlObject = caplin.core.XmlParser.parse(sXml);
        var currencyPair = xmlObject.textContent;
    
        this.currencyPair.setValue(currencyPair);
        this.dealtCurrency.setValue(currencyPair.substring(0,3));
    
        var sProduct = "/FX/" + this.currencyPair.getValue();
        caplin.core.ServiceRegistry.getService("caplin.permission-service").addPermissionListener(sProduct, null, 'VIEW', this);
    };
  7. Have a good look at this method and make sure you understand the three things it is doing. First, we use a helper class provided in the SDK called XmlParser to convert the raw string <currencyPair>GBPUSD</currencyPair> into an object. We can pull the "GBPUSD" out of this object and set the values of our currencyPair and dealtCurrency properties, like we were doing in the constructor up until now. Finally, we can request the VIEW permission for this currency pair. Remember, it’s only when we receive the permission callback saying whether we are allowed to subscribe to rates that the tile will actually subscribe.

Refresh the application. You should see a GBPUSD and a USDJPY tile, both with streaming rates in the buy and sell buttons. Try changing the currency pairs in your layout XML, reset the database, and refresh the application to see the tiles change to different currency pairs.

Serializing components

While you’re logged into the application, click the menu item "Layout → Save As…​", enter a name, and click Save. You will see several error pop-ups and errors in the console. The errors are fairly explicit about the problem:

Failed to save component due to exception: UnimplementedInterface: caplin.presenter.SerializablePresentationModel.serialize() has not been implemented.

This is because our tile is only fulfilling half of the SerializablePresentationModel interface contract. When you save the layout, Webcentric will iterate through every serializable component and ask it for an XML representation of itself. Webcentric will then store this XML in the database, so that next time you login you can load up your saved layout exactly as it was.

Our tile is currently implementing the deserialize() method but not the serialize() method, so it can’t be saved. Let’s fix that.

  1. The implementation of this method is pretty simple. We have to return a <currencyPair> XML element exactly like you see in the blank_layout.xml. The content of the element should be the currency pair that the tile relates to.

    mycompany.tutorial.tile.TilePresentationModel.prototype.serialize = function() {
        return "<currencyPair>" + this.currencyPair.getValue() + "</currencyPair>";
    };

Refresh the application and try this:

  • Drag the splitter over to one side so that one of the tiles has much more space than the other.

  • Click "Layout → Save As…​" on the menu and give your layout a name. Save it. There should be no errors.

  • Since we don’t have a logout button, close the browser tab.

  • Open the application again in a new tab. It defaults to the standard blank layout.

  • On the menu click "Layout → Open.." and select your saved layout. The panels will resize to exactly how you saved them.

You should also see that the XML is extensible enough that you could save as many features of the tile state as you like. For example, you could also serialize the amount that the user currently has entered in the amount box, and when you deserialize you can populate the box accordingly. This would allow the user to save layouts with his most commonly traded amounts already filled in.

Stretch Task - inspect the Webcentric database

If you are interested in seeing the contents of the Webcentric database, it can be done using the browser-based H2 viewer tool:

  1. Find the command window currently running Caplin Trader and kill it with Ctrl+C. Otherwise the Caplin Trader process will have a lock on the database and you won’t be able to access it with the viewer.

  2. Open a new command window. Navigate to the CaplinTrader/sdk directory and run this command:

    java -cp sdk\libs\java\system\h2-1.3.167.jar org.h2.tools.Server
  3. If it doesn’t open automatically, open a new browser tab and navigate to http://localhost:8082 to access the H2 viewer tool.

  4. You can leave most of the fields in the Login dialog untouched. The important one is the JDBC URL. This will vary depending on where you have installed Caplin Trader on your machine, but it will be something like the line below. Adjust it for your installation directory, and app name if necessary. Note that the app name appears twice at the end of the string (this is deliberate).

    jdbc:h2:D:\Training\CaplinTrader\webcentric-db\TutorialApp\TutorialApp
  5. You can leave the username and password unchanged. Click "Connect".

  6. Now you can see the various tables in the Webcentric database. Try clicking on the LAYOUTS table and then run the SELECT * FROM LAYOUTS query. You will be able to see the serialized form of all your saved layouts as Webcentric has stored them.

  7. When you’re done, switch back to the command window running the H2 viewer and kill it with Ctrl+C. Now you can restart Caplin Trader.

Review

At the end of this section you should understand:

  • How to create a Webcentric layout of arbitrary complexity using Terraces, Towers and Stacks;

  • How layouts are stored behind the scenes, and the relationship between the XML files and the database;

  • How to serialize and deserialize your components so they can be loaded and saved;

  • How to view the contents of the Webcentric database.

More information about Webcentric and tutorials can be found on the website.