21 March 2016

1. Technia Value Components Overview

The TVC contains following components:

  • Structure Browser

  • Collaboration

  • Grid Browser

  • File Manager

  • Report Generator

  • Graphic Reporting

  • Personal Browser

  • MCAD Optimizer

  • XBOM Manager

All these components are built upon a common platform, called Core. The Core contains the common functionality required by the components, including a layer around both the Matrix ADK API as well as the AEF.

TVC can be used without the AEF. However, some functionality like localization of schema names etc. is unavailable.

1.1. Where to start?

If you want to learn how to implement Data Handlers and/or Cell Renderer for the Structure Browser, please look into these chapters:

If you want to build your application on top of TVC and utilize the functionality provided by TVC then you should read following chapters:

  • If you are unfamiliar to the MVC design pattern, you should start read the chapter about the Architecture of TVC.

  • Read the chapter how to create a Plugins to TVC

  • For information how to handle the context object in a TVC please look into Context Handling

1.2. TVC License

The TVC license file is called technia.license, and is required when you attempt to install and/or run TVC.

The name of the license file were changed as of TVC 2011.1.0 due to that we have introduced license management within other softwares provided by TECHNIA.

When TVC is starting up, TVC will try to locate the license file within a couple of different places; the places being searched and the order is shown below:

The recommended location is to place the license file within WEB-INF/classes of your running application. This is the place where the TVC installer will put the file.

If you have JPOs that run TVC code you also need to make sure that the license file is available on the MX_CLASSPATH. In most cases, your MX_CLASSPATH variable is referring to the WEB-INF/lib and WEB-INF/classes folder, but as this might not always is the case, you will then need to ensure that this file gets copied to a folder that is in the MX_CLASSPATH.

When you upgrade to a newer version of TVC, you need to ensure that the license file is updated wherever you are using TVC.

1.3. Architecture

TVC is built on top of the Apache Struts Framework. This framework is based upon the MVC (Model-View-Controller) / Model 2 design pattern. The main purpose of this architecture is to separate database access code, page design code, and control flow code from each other.

The different pieces of a MVC based application could simply be describes as:

1.3.1. Model

The TVC components provides different set of classes that represents its model. These classes are all used for supporting the functionality in each component, however, many of the frequently used and common classes across the different components are available in the TVC core component. The Core component contains a lot of utility classes used for accessing the Matrix database.

1.3.2. View

The view is composed of mainly JSP pages, which are using JSP tags to produce the user interface. This layer is and should never access the database directly, it should only render the user interface based upon information available as beans within the page-, request- or session scope, provided by the controller.

1.3.3. Controller

The central part of TVC is the Action servlet. This servlet is responsible for both initializing TVC properly when the application starts up, as well as delegating requests to the different action handlers.

The TVC servlet is mapped to handle all requests matching this URL pattern: /tvc-action/*

Depending on the trailing part of an URL matching this pattern, the action servlet delegates the control to an action class. The mapping between URL and action classes, are made through configuration files, which are provided by each TVC component and possibly the custom plugin(s). All actions defined in this configuration file, are all part of the controller layer.

1.4. Plugins

If you want to build custom code based upon TVC, you should preferable implement this as a TVC plugin. By doing so, you will be able to take advantage of a rich set of features from TVC, which will solve and simplify a lot in your custom application.

A TVC plugin is a very tiny class that provides TVC with some meta-data about your plugin. The plugin is loaded when TVC is initialized, which happens either when the application server starts if you have set the load-on-startup attribute or when TVC is used the first time after startup.

For information how to implement a TVC plugin, see chapter Creating a Plugin

1.4.1. Plugin Features

The features you can use from TVC, when implementing your own plugin, is shown below:

Localization

Allows you to store localized application string resources in separate files and access these properties in a convenient way, using the same mechanism as TVC does internally.

See this chapter for more information

Actions

Allows you to register custom actions.

See this chapter for more information

Domain Object Model

Allows you to use the Domain Object Model in TVC, where you can map a certain business object type into a Java Class.

See this chapter for more information

Ajax Services

Allows you to define custom Ajax services using the Ajax framework in TVC

See this chapter for more information

Logging

Allows you to define custom loggers available for your code.

See this chapter for more information.

You don’t have to use all the capabilities mentioned above, you can simply use those you wish to use.

1.4.2. Plugin Registration

To register a plugin with TVC you simply have to register your plugin class through the deployment descriptor of your application (web.xml). The name of this system parameter is tvc.core.plugins. See this chapter for information how to apply system parameters.

An example of how to do so is shown below:

<servlet>
    <servlet-name>tvcaction</servlet-name>
    <servlet-class>com.technia.tvc.core.TVCServlet</servlet-class>
    <!-- possible other init parameters -->
    <init-param>
        <param-name>tvc.core.plugins</param-name>
        <param-value>com.company.MyTVCPlugin</param-value>
    </init-param>
</servlet>

You can of course register more than one plugin, if so, just add all the plugin classnames as a comma (,) separated list as shown in the example below

<init-param>
    <param-name>tvc.core.plugins</param-name>
    <param-value>com.company.MyTVCPlugin,com.company.AnotherPlugin,com.company.AndAThirdPlugin</param-value>
</init-param>

1.4.3. Creating a Plugin

Creating a TVC plugin is done by either creating a class that implements the com.technia.tvc.core.TVCPlugin interface, or create a class that extends the com.technia.tvc.core.TVCPluginAdapter class.

The class you create must have a public no-arg constructor.

Consult the API for further details:

  • com.technia.tvc.core.TVCPlugin

  • com.technia.tvc.core.TVCPluginAdapter

1.4.4. Plugin Examples

Below, you will find a couple of different examples, which all shows how to create a plugin.

Plugin Example - 1

A simple plugin, which only provides string resources

    package com.acme.anapplication;

    import com.technia.tvc.core.TVCPlugin;
    import java.net.URL;

    public class MyPlugin implements TVCPlugin {
        public String getDescription() {
            return "Sample plugin for my application";
        }
        public String getDisplayName() {
            return "Application XYZ";
        }
        public String getName() {
            return "application";
        }
        public URL getActionConfigURL() {
            return null;
        }
        public URL getAjaxConfigURL() {
            return null;
        }
        public URL getTypeMappingsURL() {
            return null;
        }
        public String getStringResourceName() {
            return "com.acme.anapplication.resources.StringResources";
        }
    }
Plugin Example - 2

This plugin provides action configuration, type mapping, ajax configuration and string resources.

    package com.acme.anapplication;

    import com.technia.tvc.core.TVCPlugin;
    import java.net.URL;

    public class MyPlugin implements TVCPlugin {
        public String getDescription() {
            return "Sample plugin for my application";
        }
        public String getDisplayName() {
            return "Application XYZ";
        }
        public String getName() {
            return "application";
        }
        public URL getActionConfigURL() {
            return getClass().getClassLoader().getResource("com/acme/anapplication/resources/Actions.xml");
        }
        public URL getAjaxConfigURL() {
            return getClass().getClassLoader().getResource("com/acme/anapplication/resources/AjaxConfig.xml");
        }
        public URL getTypeMappingsURL() {
            return getClass().getClassLoader().getResource("com/acme/anapplication/resources/TypeMappings.properties");
        }
        public String getStringResourceName() {
            return "com.acme.anapplication.resources.StringResources";
        }
    }
Plugin Example - 3

This plugin does not implement the TVCPlugin interface directly, it extends a class called TVCPluginAdapter.

This class allows you to skip implementing functions that you are not interested in, and it also provides you with a simple way for settings up the logging of your application.

    package com.acme.anapplication;

    import com.technia.tvc.core.TVCPluginAdapter;
    import java.net.URL;

    public class MyPlugin extends TVCPluginAdapter {
        public MyPlugin() {
            /*
             * Setup logging
             * This will setup the logging for the domain "com.acme.anapplication"
             * using the DEBUG level, if TVC is running in development mode,
             * or ERROR level, if TVC is running in production mode.
             * The pattern (Log4J pattern) will be:
             * [plugin-name] [%t]%x %5p %d{HH:mm:ss,SSS} (%F:%L) - %m%n
             */
            setupLogger();
        }
        public String getName() {
            return "application";
        }
        public String getStringResourceName() {
            return "com.acme.anapplication.resources.StringResources";
        }
    }

1.4.5. Sample Plugin

Together with TVC, we provide a module called TVX. This module is available for anyone, and contains many examples for many of the TVC components.

TVX is a TVC plugin and contains a number of classes that can be used for reference.

The source code for TVX is available within the tvx.jar file.

TVX is a library of examples. The code is not a product that should be used out-of-the box in a production environment. You can however reuse the code or get some ideas from it and apply that to your application.

Also note that TVX is a product that we don’t provide support for, however, feel free to register issues if you find such, so that we can enhance it in coming releases.

1.5. Localization (i18n)

If you have implemented a TVC plugin, and your plugin provides string resources, you are able to take advantage of the localization support in TVC This chapter illustrates how to use localized messages in different situations.

First of all, you will need to create a plugin, which returns the name of the string resources file to be used. See this chapter for more information how to create a plugin. The string resource file itself, is a plain text file containing key/value pairs, in the same syntax as standard Java property files are written. Example:

com.acme.anapplication.atitle=This is the title for a page
com.acme.anapplication.anotherLabel=A label with one argument: {0}
com.acme.anapplication.yetAnotherLabel=A label with two arguments: {0} {1}

Translating the messages into other languages, follows the traditional Java style. E.g. create a new file per language, and append the locale suffix to the filename. For example, if your localization messages are stored in the file com/company/resources/StringResources.properties, then if you want to provide a german translation of this file then copy the file into com/company/resources/StringResources_de.properties etc.

Please note that you should have a prefix of your localized message keys, which reduces the risk of having two different plugins overriding each other string resource message(s).

Don’t use tvc as a prefix for your keys, as these are used internally for TVC

On the other hand, in some cases you might want to override an existing localized message.

In such case, you can override such a key in your custom string resource property file without the need of touching the internal files in TVC (or another plugin).

New string resources properties can be added through global parameter like below:

Example:

<init-param>
    <param-name>tvc.core.stringResources</param-name>
    <param-value>com.example.navigationmenu.TVCStringResources, com.example.bindingstructure.TVCStringResources
    com.example.lstructuremenu.TVCStringResources
    </param-value>
</init-param>

If there is a file placed directly in classes folder following the pattern “tvc-*.properties” it also included as string resource.

1.5.1. Localization in JSP Pages

From a JSP page, you can use the existing JSP tags for retrieval of localized messages. See the code example below:

<%@ taglib uri="/WEB-INF/tvc-struts-bean.tld" prefix="bean" %>
...
<bean:message key="com.acme.anapplication.atitle"/>
<bean:message key="com.acme.anapplication.anotherLabel" arg0="with an argument"/>
<bean:message key="com.acme.anapplication.yetAnotherLabel" arg0="with more arguments" arg1="second arg"/>

1.5.2. Localization in Java Code

In the Java code, you can also retrieve localized messages easy. See the code example below:

    import com.technia.tvc.core.util.MessageUtils;

    public void someMethod() {
        ...
        /*
         * There are several methods available in the MessageUtils class
         */
        String message = MessageUtils.getMessage(locale, "com.acme.anapplication.atitle");
        ...
    }

You can also create TVCExceptions that contains localized messages, see the code example below.

    import com.technia.tvc.core.TVException;

    public void someMethod() throws TVCException {
        /*
         * Throws an exception, using a localized message
         */
        throw new TVCException("The default message", "com.acme.anapplication.error");

        /*
         * This example is throwing an exception, using a localized message with additional arguments.
         */
        throw new TVCException("The default message", "com.acme.anapplication.error", new String[] {
            "optional", "arguments", "for", "the error message"
        });

        /*
         * Another example, which includes the "rootCause" (the exception that caused the TVCException to be thrown)
         */
        new TVCException("The default message", rootCause, "com.acme.anapplication.error", new String[] {
            "optional", "arguments", "for", "the error message"
        });
    }

The API documentation contains additional information regarding this topic.

  • com.technia.tvc.core.util.MessageUtils

  • com.technia.tvc.core.TVCException

1.6. Actions

As mentioned in the Architecture section, TVC is using the MVC pattern. This chapter describes how to create controller extensions, so called actions.

All TVC components provides actions, most of them are used internally for TVC but some of them can be re-used either as they are or as a base for your own custom actions. When creating new actions, these must be defined inside a so called action configuration descriptor file. The action configuration file defines the mapping between the URL and the action class. The URL’s to each action, will start with /tvc-action/, which is the end-point for the TVCServlet. The TVCServlet will depending on the trailing part of this URL forward the control to the actual action class that will handle the request.

Before you can add new actions into TVC you must create and configure a Plugins. A plugin should provide it’s own action configuration file, containing the mapping between the URL’s, according to the format specified later on.

Please remember that all your actions should have a prefix, which ensures that your actions are not overriding any of the default action mappings inside TVC.

See also the more comprehensive documentation provided by struts. See this resource (external link)

1.6.1. Action Configuration File

Assuming that you have created a plugin, where you have implemented the method getActionConfigURL(), and this URL points to a location where the action configuration file can be read, we are now able to look into the action configuration file structure.

The example below shows a simple action configuration file, which contains two defined actions.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
    <action-mappings>
        <action path="/pathMapping1" type="com.mypackage.mysubpackage.actions.AnAction">
            <forward name="done" path="/app/function1/Done.jsp" redirect="false"/>
            <forward name="error" path="/app/function1/Error.jsp" redirect="false"/>
        </action>
        <action path="/pathMapping2" type="com.mypackage.mysubpackage.actions.AnotherAction">
            <forward name="step1" path="/app/function2/Step1.jsp" redirect="false"/>
            <forward name="step2" path="/app/function2/Step2.jsp" redirect="false"/>
            <forward name="done" path="/app/function2/Done.jsp" redirect="false"/>
            <forward name="error" path="/app/function2/Error.jsp" redirect="false"/>
        </action>
    </action-mappings>
</struts-config>

An action contains the path, which it maps to, as well as the name of the class that handles the request.

Further on, the action can have an arbitrary amount of forwards. Your action can, depending on some condition, forward to different pages. The redirect flag on the forward tag indicates if the browser should be redirected to the page, or if the forward is done using the normal internal forwarding.

These actions in this example maps to the following URL’s.

  • /tvc-action/pathMapping1

  • /tvc-action/pathMapping2

There will be only one instance created per action. E.g. you are not allowed to store instance variables inside an action class.
Also, you should never add the synchronized keyword to any of the methods inside an action.

1.6.2. Obtaining the URL to an Action

To obtain the URL for a particular action, this can be done by:

From a JSP page:

<%@ taglib uri="/WEB-INF/tvc-core.tld" prefix="core" %>
<html>
<body>
<form action="<core:action path="/pathMapping1"/>" method="post">
...
</form>
</body>
</html>

From Java code:

import com.technia.tvc.core.utils.ActionUtils;
...
/*
* Get the absolute URL
*/
String url = ActionUtils.getActionURL(request, "/pathMapping1");

/*
* Get the relative URL (from the context-path)
*/
String url = ActionUtils.getActionURL(request, "/pathMapping1", false);
Creating an Action

An action must be derived, directly or indirectly, from the class called com.technia.tvc.struts.action.Action. However, normally you do not derive your action class directly from this base class, instead you will most often derive from any of these classes:

  • com.technia.tvc.core.actions.TVCAction

  • com.technia.tvc.core.actions.NewTxAction

The difference between these two actions is that the latter one will establish a so called transactional context meaning that during the execution of your action, you will have the possibility to access the Matrix database with a thread safe context.

You will normally extend the first action (TVCAction) in those cases when you do not need to access the database, for example in the case where you only want to validate parameters sent in the request for the purpose of letting the user continue to another page.

Also note that each TVC component might provide some convenient action classes, which you can use as a base, when you are extending functionality on top of that particular TVC component.

Recommendations

When you implement a custom action class, you should

  • Never store any instance variables inside an action

    As there is only one instance of each action class, race conditions will appear if you store state inside instance variables. Store data using the request attributes collection, or possibly in the session.

  • Never mark any methods as synchronized

    As there is only one instance of each action class, a synchronized method will only allow one user to perform the same action concurrently.

  • Strive to reduce the action class code to a minimum, as the action should be the layer between the request (Servlet API) and your model

    Making the action classes too heavy, discourages reuse and makes the application too coupled with the servlet API.

  • Never pass the request or any of the other objects belonging to the Servlet API to the model layer.

    Always collect the things you need in your action class, and possibly wrap them inside a data structure if there is a large number data, and/or complex data, and pass the data structure to your model.

  • Common code, used by several different action classes, should either be placed into an Utility class, or be shared inside a common action class, which your action classes in turn derives from.

Action Examples

Below, you will find a couple of different examples of action classes.

Example 1

A simple action class, which validates some request parameters and forwards to some page depending on if the parameters were ok or not.

    package com.company.anapplication.actions;

    import com.technia.tvc.core.actions.TVCAction;
    import com.technia.tvc.core.util.StringUtils;

    import com.technia.tvc.struts.action.ActionError;
    import com.technia.tvc.struts.action.ActionErrors;
    import com.technia.tvc.struts.action.ActionForm;
    import com.technia.tvc.struts.action.ActionForward;
    import com.technia.tvc.struts.action.ActionMapping;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    public class AnAction extends TVCAction {
        public ActionForward executeImpl(ActionMapping mapping,
                                         ActionForm form,
                                         HttpServletRequest request,
                                         HttpServletResponse response) throws Exception {

            String param1 = request.getParameter("param1");
            String param2 = request.getParameter("param2");

            /*
             * Validate params !
             */

            ActionErrors ae = new ActionErrors();
            if (!StringUtils.isInteger(param1)) {
                ae.add("param1", new ActionError("prefix.error.notAnInteger", param1));
            }

            if (StringUtils.isOnlyWhiteSpaceOrEmpty(param2)) {
                ae.add("param2", new ActionError("prefix.error.missingValue"));
            }

            /*
             * If no errors were found, forward to the next page.
             * Otherwise forward to the original page, which are able to show
             * these error messages.
             */
            if (ae.isEmpty()) {
                return findForwardOrTargetPage(request, mapping, "next-step");
            } else {
                addErrors(request, ae);
                return findForwardOrTargetPage(request, mapping, "dialog");
            }
        }
    }
Example 2

An example action class for revising/updating the revision of a domain object, unless the parameters are invalid (white space or empty).

    package com.company.anapplication.actions;

    import com.technia.tvc.core.TVCException;
    import com.technia.tvc.core.actions.NewTxAction;
    import com.technia.tvc.core.db.BusinessObjectUtils;
    import com.technia.tvc.core.db.transaction.TxContext;
    import com.technia.tvc.core.db.transaction.TxType;
    import com.technia.tvc.core.db.domain.DomainObject;
    import com.technia.tvc.core.util.RequestUtils;
    import com.technia.tvc.core.util.StringUtils;

    import com.technia.tvc.customerdb.model.access.Access;
    import com.technia.tvc.struts.action.ActionForm;
    import com.technia.tvc.struts.action.ActionForward;
    import com.technia.tvc.struts.action.ActionMapping;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    public class AnotherAction extends NewTxAction {
        public AnotherAction() {
            super(TxType.UPDATE);
        }

        public ActionForward execute(TxContext txc,
                                     ActionMapping mapping,
                                     ActionForm form,
                                     HttpServletRequest request,
                                     HttpServletResponse response) throws Exception {

            String objectId = RequestUtils.getObjectId(request);
            if (StringUtils.isOnlyWhitespaceOrEmpty(objectId)) {
                throw new TVCException("No objectId parameter in request");
            }
            DomainObject d = DomainObject.newInstance(objectId);
            DomainObject revised = d.revise();
            ...
            return findForwardOrTargetPage(request, mapping, "result");
        }
    }

1.7. Ajax

TVC contains an Ajax (Asynchronous JavaScript and XML) framework, which can be used by custom applications.

Ajax can be used to make web pages feel more responsive by exchanging small amounts of data with the server behind the scenes, so that the entire web page does not have to be reloaded each time the user requests a change. This is intended to increase the web page’s interactivity, speed, functionality, and usability.

The Ajax framework in TVC is very powerful but easy to use and requires very little, from a programming point of view, in order to be used from within your web-pages.

To be able to use the Ajax framework in TVC you have to create an Ajax configuration descriptor, which defines your Ajax services and methods. In order for TVC to know that you have such a descriptor, you need to create a plugin, which implements the getAjaxConfigURL() method, shown below:

public class MyPlugin implements TVCPlugin {
    ...
    public URL getAjaxConfigURL() {
        return getClass().getClassLoader().getResource("com/acme/anapplication/resources/AjaxConfig.xml");
    }
    ...

The returned URL should point to a resource, which contains the Ajax descriptor.

The content of the Ajax descriptor defines one or more services, where each service typically has one or more methods available. A service corresponds to an ordinary Java class, while a method refers to a method inside the service class.

The Ajax Service itself is implemented as a normal Java class.

1.7.1. Ajax Configuration

This chapter describes the Ajax configuration (descriptor) file.

The descriptor file is a XML file, which is structured as the example below:

<?xml version="1.0" encoding="UTF-8"?>
<ajax-config>
     <service name="myFirstAjaxService" class="com.company.anapplication.ajax.MyFirstAjaxService">
        <method name="getCurrentDate" context="no"/>
        <method name="getDate" context="yes" transaction="read"/>
        <method name="storeDate" context="yes" transaction="update"/>
     </service>
     ...
</ajax-config>

The ajax-config is the root element, holding the service element(s).

A service must be associated with a name, which must be a valid Java identifier. Also, the service must be mapped to a class,which has a public-no-arg constructor.

Further on, each service can have one or more service-methods. Each of these methods can be configured to have a so called transactional context available through the attribute context. When a method has been marked to require a transactional context, you can also specify the transaction type that this method requires. (Any of: read, update, none)

Please note that the name of your Ajax service must be unique across all Ajax services. Using the same name as another one has, will overwrite the definition of that Ajax service. E.g. the name can only map to one Ajax service class.

Also note that you can not have multiple methods with the same name. This is not a limitation of the Ajax framework itself, but a limitation in Java Script within the browsers.

1.7.2. Synchronous Invocation

An Ajax service method is normally invoked asynchronously, from the client perspective, unless specified. You can mark a method to execute synchronously, by setting the async attribute to false:

<ajax-config>
    <service ...>
        <method name="getCurrentDate" context="no" async="false"/>
    </service>
</ajax-config>

1.8. Implementing an Ajax Service

The Ajax service class is simple to implement.

The class you create must have a public no-arg constructor and there is no need to implement any interfaces.

Each of the service methods must be public, they are allowed to have any number of arguments in the method signature, it can either return something or not, and it is allowed for the service method to throw any kind of Exception during execution to indicate problems.

Since the Ajax services are called from Java Script code, all arguments passed to the service method as well as any return values from the service method must be converted from/to Java objects into/from Java Script. A large number of conversions can be made through the TVC Ajax framework, but you should definitely take a look into this section for more information about this topic.

1.8.1. Ajax Service Example

Below, you will find a couple of different examples, which all shows how an Ajax Service could be implemented.

Please note that in order to allow an Ajax Service to access the Matrix database, you must configure the service method through the Ajax descriptor that it needs a transactional context. Look at this chapter for more information.

public class MyAjaxService {

    public void doSomething() throws Exception {
        /*
         * Illustrates how to obtain reference to request, session, servlet-context
         * through the AjaxEngine.getAjaxContext()
         */
        AjaxContext ctx === AjaxEngine.getAjaxContext();
        HttpServletRequest request === ctx.getRequest();
        HttpSession session === ctx.getSession();
        ServletContext sctx === ctx.getApplication();
        ...
    }

    public String getString(String in, int i, boolean b) throws Exception {
        ...
        return s;
    }

    public Result getResult(String a, String b, String c, String d) {
        /*
         * Illustrates how to return a complex data structure...
         */
        ...
        return result;
    }

    public static class Result {
        private String value;

        public String getValue() {
            return value;
        }

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

}

1.8.2. Parameter Types

TVC Ajax Framework is capable of handling Literals, XML data, Arrays and Data structures. For more information about each of these types, and what kind of data types they can convert from/to, please look at the list below:

Whenever an Ajax service method is invoked, the arguments passed to the Java Script method is passed to the server. The Ajax framework tries to convert the arguments passed according to the signature on the service method. Look at the following Java Script example, and how it is being mapped on the server side.

<script type="text/javascript">
    function callAjax() {
        myService.simple1("a string", ...);                    // String
        myService.simple2(1234, ...);                          // number
        myService.simple3(true, ...);                          // boolean
        myService.array1([ "a", "b", "c" ], ...);              // Array
        myService.array2([ "a", "b", "c" ], [ 1,2,3,4 ], ...); // Array
        myService.complex({ height: 186, width: 161 }, ...);   // Complex Data Structure
    }
</script>

The corresponding Ajax service, could be implemented as:

public class MyService {
    public void simple1(String s) { ... }
    public void simple2(int i) { ... }
    public void simple3(boolean b) { ... }
    public void array1(String[] arr) { ... }
    public void array2(String[] arr, int[] i) { ... }
    public void complex(Size size) { ... }
}

public class Size {
    private int height, width;

    public Size() {}

    public void setHeight(int height) { this.height === height; }
    public int getHeight() { return height; }

    public void setWidth(int width) { this.width === width; }
    public int getWidth() { return width; }
}

When a service method has executed and returns some value, this value is transfered back to the client and converted to a Java Script object. For example, if you have a method that returns a complex data structure, you will be able to handle such as in the example below:

public Size getSize() {...}

You will be able to handle such a return value from within the Java Script code as in the example below:

<script type="text/javascript">
    function callAjax() {
        myService.getWidth(function(ret,success) {
            if (success) {
                alert("height: " + ret.height + ", width: " + ret.width);
            }
        });
    }
</script>

1.8.3. Using an Ajax service

To use an Ajax service on a JSP page, you have to

  1. Include the tvc-core.tld taglibrary.

    This is needed as we will use the ajaxClient tag from this library.

  2. Define the Ajax service to be used through the <core:ajaxClient service="…"/> tag.

    Please note that this tag should be placed inside the <head> elements in your HTML code.

    This tag will generate dynamic Java Scripts, which contains the stubs for your service methods.

    If you need to use different services, simply add one <core:ajaxClient> tag per service you need.

  3. You might need to build some Java Script function(s) around the generated Java Script stubs, in order to integrate the Ajax services with your page logic.

    Normally, these consists of small wrapper functions, which simply delegates to the service methods, but handles the result of the Ajax method invocation.

Code template:

<%@ taglib uri="/WEB-INF/tvc-core.tld" prefix="core" %>
<html>
    <head>
        <core:ajaxClient service="myService"/>
    ...
Ajax Service Example

Lets say that you have a HTML document, which is structured as the example below.

<html>
    <body>
        <span id="s1"/>
        <button onclick="getCurrentDate()">Test Ajax</button>
    </body>
</html>

Now you would like to invoke an Ajax service method when the button is clicked. This method returns a string containing the current date in ISO format and you want to populate the <span> element with that value.

The complete code for this use case is shown below:

<%@ taglib uri="/WEB-INF/tvc-core.tld" prefix="core" %>
<html>
    <head>
        <core:ajaxClient service="myService"/>
        <script type="text/javascript">
            function showCurrentDate() {
                myService.getCurrentDate(function(ret,success) {
                    if (success) {
                        document.getElementById("s1").innerHTML ==== ret;
                    } else {
                        alert(ret.message);
                    }
                });
            }
        </script>
    </head>
    <body>
        <span id="s1"/>
        <button onclick="testAjax()">Test Ajax</button>
    </body>
</html>

The example above invokes the Ajax method asynchronously. The last argument to the Java Script method generated by the <core:ajaxClient/> tag must in that case be a reference to a Java Script function, which has the following signature: (in this case the function is passed as a closure)

function (ret,success)

The success argument in the function is a boolean, indicating whether or not the Ajax call were successfully completed. If it was successful, then the first argument contains the return value from the Ajax method. Depending on what your method returns, you might use the ret variable as-is or you might need to handle it somehow (remember that the Ajax method can return complex data structures or XML, in addition to simple properties such as strings, numbers etc.)

If the method was marked to be executed synchronously (in the descriptor), then you would not need to pass any function reference to the call. Instead, you could directly assign the value to a variable or use it directly, as the call would block any further processing until it has been carried out.

Example:

    ...
    <script type="text/javascript">
            function showCurrentDate() {
                var s1 ==== document.getElementById("s1");
                s1.innerHTML ==== myService.getCurrentDate();
            }
    </script>

There is no general rule, when to use asynchrous calls vs. synchronous calls. This depends highly upon the use of the Ajax service in your use case. Basically, if your application can be used without the need for blocking anything else, while carrying out the Ajax call, you could use the asynchronous approach.

The complete code required for this example is shown below:

The Ajax descriptor:

<?xml version="1.0" encoding="UTF-8"?>
<ajax-config>
    <service name="myService" class="com.company.anapplication.ajax.MyFirstAjaxService">
        <method name="getCurrentDate" context="no"/>
    </service>
</ajax-config>

The com.company.anapplication.ajax.MyFirstAjaxService class:

package com.company.anapplication.ajax;

import com.technia.tvc.core.db.DateUtils;
import java.util.Date;

public class MyFirstAjaxService {

    public MyFirstAjaxService() {
    }

    public String getCurrentDate() {
        return DateUtils.formatISO8601Date(new Date());
    }
}

1.9. Context Handling

The Context (matrix.db.Context) object is the most essential part of a Java based Matrix implementation, and improper usage of this could and will result in several obscure problems, such as instability issues, performance degradation or other unexpected/strange behavior. The Context object is used to authenticate users, retrieve data from the DB, start/commit/abort transactions and retrieve so called client-tasks (MQL error/warn/notice messages).

The TVC framework hides some complicated issues regarding how to handle the Context, such as creating thread-safe context instances, managing transactions and working with save points. Also, the design of TVC limits the need of passing the Context reference everywhere around the API just because there is a method deep in the chain that needs to query the database. The design of the TVC context handling, follows the transactional context pattern, which works in the similar way as when using a SQL-Connection to access a SQL database.

1.9.1. Transactional Context Pattern

The idea behind this pattern is to avoid always passing in the matrix.db.Context instance to every possible method around the application that might need to query the database. Instead, the method that might need to query the database should be able to retrieve the context through some other mechanism.

E.g. when you work with a standard SQL database, you do not normally have to pass the Connection instance to every possible method around the application, just because that at some point a database call will be made. I.e. the methods requiring access to the database will retrieve the connection from a connection pool or directly from the DriverManager when needed.

Also, the transactional pattern implementation makes the code much more flexible. Just think about the case where you have implemented a method that is called deep in the stack, which previously did not need to query the database, and you now need to query the database from this method. What you have to do in that case, is to change the method signature of all other methods in the chain that calls this method. This might in some cases require a lot of changes to your code base.

This requires that the code that needs the database access only can be executed while there is a transactional context allocated. A transactional context is (mainly) allocated in these cases:

  • During the execution of an Action if the action is a subclass of the NewTxAction.

  • While executing an Ajax service, if the Ajax service has been configured to allocate a transactional context.

  • When executing a JPO (see below for further details).

As no database operations should be made from JSP pages directly, there should not be a need for having a transactional context allocated while executing a JSP page. Instead, the necessary information needed to render a page should be stored in request or session scoped beans.

Whenever a method requires the Context instance, you are able to retrieve this from the com.technia.tvc.core.db.transaction.TxManager. The TxManager keeps a reference to the user’s context in the TxContext class.

If the method that requires a transactional Context is invoked while a transactional context hasn’t been allocated, an Exception will be thrown indicating so.

1.9.2. Transactional Context Pattern - Example

The sequence diagram illustrates how it works internally when working with transactional contexts. The diagram shows what happens in the case when we have a custom action called MyAction. This action is derived directly from the NewTxAction. Our custom action uses a helper class, SomeClass, which does some database calls. All operations marked in green, are those that happens in the TVC framework, while the yellow operations are those related to the custom code.

Basically, the only requirements needed for a custom action are

  • The custom action must be derived (indirectly or not) from the NewTxAction

  • The custom action should define what kind of transaction it needs. One of:

    • Update

    • Read

    • None (should not be used, unless you will handle start/commit on your own)

  • Once invoked, decide what to do. For example, collect request parameters, instantiate some class, perform some operations (in this case, dispatch to the SomeClass class)

  • Return the name of the forward (normally JSP page), which will render the user interface.

  • In case when some problem occur, for example an exception is thrown during execution or the transaction couldn’t be committed, define the target forward for this situation.

image

Each step is explained more in detail:

1

A HTTP request is made (/tvc-action/*)

2

The TVC Servlet dispatches the call to the action mapped to the URL

3-7

The NewTxAction allocates a transactional context using the Context object from the session.

8

Mark the transaction type

9

Dispatch to the execute method of the MyAction

10

Dispatch to SomeClass

11-13

As this class needs to get data from Matrix, it will need to obtain a reference to the Context object

14-16

The first time the Context object is retrieved from a TransactionalContext object (TxContext), the Context is cloned and the transaction is started.

17

Call Matrix…​

18-26

NewTxAction does the post process work, e.g. committing any ongoing transaction and forward to the view

27

When completed, the control is finally forwarded to the JSP page, which renders the user interface

1.9.3. Establish a Transactional Context Within an Action Class

To establish a transactional context, you must ensure that your action class is extending from the base class named:com.technia.tvc.core.actions.NewTxAction, or another action class which is deriving from this. The simple code sample below illustrates how to do so.

package mypackage;

import com.technia.tvc.core.actions.NewTxAction;
import com.technia.tvc.core.db.transaction.TxType;
import com.technia.tvc.core.db.transaction.TxContext;
import com.technia.tvc.core.db.BusinessObjectUtils;
import com.technia.tvc.struts.action.ActionForm;
import com.technia.tvc.struts.action.ActionForward;
import com.technia.tvc.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PromoteAction extends NewTxAction {
    public PromoteAction() {
        /*
         * Set the type of transaction this action requires
         */
        super(TxType.UPDATE); // TxType.READ or TxType.UPDATE or TxType.INACTIVE
    }

    public ActionForward execute(TxContext txc,
                                 ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
                          throws Exception {
        String objectId = request.getParameter("objectId");
        BusinessObjectUtils.promote(objectId);
        return findForwardOrTargetPage(request, mapping, "done");
    }
}

1.9.4. Establish a Transactional Context from a JPO

If you want to allocate a transactional context through out the execution of a JPO, you would typically need to do this according to the example below:

import com.technia.tvc.core.TVCSystem;
import com.technia.tvc.core.db.transaction.TxAction;

public class ${CLASSNAME} {
    /**
     * A TxAction implementation
     */
    private static final class SampleAction implements TxAction {
        private final String[] args;

        private SampleAction(String[] args) {
            this.args = args;
        }

        public Object run() throws Exception {
            // Perform logic here... For example utilize TVC API.
            Map input = (Map) JPO.unpackArgs(args);
            ...
            return new Integer(1); // return something, or null if not of interest.
        }
    };

    /**
     * The JPO method invoked
     */
    public int doSomething(matrix.db.Context ctx, String[] args) throws Exception {
        return ((Integer) TVCSystem.execFromJPO(ctx, new SampleAction(args))).intValue();
    }
}

Since a JPO might be executed outside of the application server JVM (when Matrix is setup in RMI mode), TVC must be initialized before the TVC API can be used. This is also handled when you use the approach as shown below.

1.9.5. Transactional Contexts in Ajax Services

First of all, ensure that you have read the chapter about Ajax in TVC.

A transactional context is simply made available by modifying the Ajax descriptor. Look at the example below for different configuration.

<ajax-config>
    <service name="myAjaxService" class="com.company.ajax.MyAjaxService">
        <method name="getSomething"    context="yes" transaction="read"/>
        <method name="updateSomething" context="yes" transaction="update"/>
        <method name="calculate"       context="no"/>
    </service>
</ajax-config>

A transactional context can be made available by toggling the attribute context. If this value is set to true, you can also specify the transaction type by setting the transaction attribute to one of:

  • read

  • update

1.9.6. Transactional Contexts in JSP Pages

There is a quick and convenient way to establish a transactional context through the execution of a JSP page. The code example below illustrates how to do so.

<%@ taglib uri="/WEB-INF/tvc-core.tld" prefix="core" %>
<core:useContext>
<%
    // do the database calls here
%>
</core:useContext>
From an architectural perspective we strongly discourage developers to write code that access the database layer on a JSP page.

1.9.7. Commonly Functionality Related to Transactional Context

Testing if a Transactional Context is Available

If there is a need to get a reference to the actual matrix.db.Context instance, one can do as below:

if (!TxManager.available()) {
    throw new TVCException("A transactional context isn't available!");
}
Retrieving the Current Context

When you need to get a reference to the actual matrix.db.Context instance, you can retrieve this by:

TxContext txc = TxManager.getLocalTxContext();
Context ctx = txc.getContext();

This might be needed, when calling another API that requires the Context in the method signature - OR if using the Matrix ADK API directly (however, before doing that - take a look in TVC, there might be a good chance that the functionality you want already is implemented there).

Retrieving the User Name
String userName = TxManager.getCurrentUser();
// OR
String userName = TxManager.getLocalTxContext().getUser();
Retrieving the Users Locale
Locale locale = TxManager.getLocalTxContext().getLocale();
Working with Save-Points

During a transaction, there might be a need to add save points, which can be used to abort a certain update operation made to the database without affecting the outer transaction.

TxSavepoint savePoint = TxManager.savepoint();
// do some operation
// if needed, the save point can be aborted:
savePoint.abort();
Obtaining the ClientTaskList

The Client Task list can be obtained by:

ClientTaskList ctl = TxManager.getLocalTxContext().getClientTasks();

Please note that after the client task list has been retrieved, the client tasks has been consumed from the context object.

Performing a Privileged Operation

If you need to do an operation, which the normal user doesn’t have access to do, you can do this by either:

  • Using the TxManager.doPrivileged(TxAction) method.

    public void someMethod(final String objectId) throws TVCException {
        TxManager.doPrivileged(new TxAction() {
            public Object run() throws Exception {
                BusinessObjectUtils.promote(objectId);
                return null;
            }
        });
    }
  • Or through the TxContext

    public void someMethod(final String objectId) throws TVCException {
        TxManager.getLocalTxContext().doPrivileged(new TxAction() {
            public Object run() throws Exception {
                BusinessObjectUtils.promote(objectId);
                return null;
            }
        });
    }

The latter example will perform the operation within the outer transaction (if any), while the first example will be performed independently of any outer transaction. Also, the first example does not require an already existing transactional context to be available.

Performing an Action as a Different User

If you wish to perform an action as a different user, you can do so by calling the doAs(…​) from the existing transactional context.

public void someMethod(final String objectId) throws TVCException {
    TxManager.getLocalTxContext().doAs(new TxAction() {
        public Object run() throws Exception {
            BusinessObjectUtils.promote(objectId);
            return null;
        }
    }, "username", "password", "vault");
}
Explicitly Starting / Aborting / Committing Transactions

There are several methods available from the TxContext class, which allows you to handle the transactions on your own.

public void someMethod(String objectId) throws TVCException {
    ...
    // example 1: force an abort later on
    TxManager.getLocalTxContext().setAbort();

    // example 2: do the abort now!
    TxManager.getLocalTxContext().abort();

    // example 2: try commit the transaction
    TxManager.getLocalTxContext().commit();
}
API References

The following list contains links to the different parts of the API, which is related to Transactional Contexts.

  • com.technia.tvc.core.db.transaction.TxManager

  • com.technia.tvc.core.db.transaction.TxContext

  • com.technia.tvc.core.db.transaction.TxAction

  • com.technia.tvc.core.db.transaction.TxSavepoint

  • com.technia.tvc.core.actions.NewTxAction

  • com.technia.tvc.core.TVCSystem

1.10. System Parameters

The behavior of TVC is configurable by applying so called system parameters. A system parameter is a way for allowing changing the default behavior of certain aspects within the TVC logic. When TVC is running as a part of the AEF environment, some system parameters are picked up from the AEF configuration, hence those are not needed to configure. The following list shows those parameters that are automatically detected:

  • RMI Host and Port (or if RIP mode is being used)

  • Input Date Format

  • Matrix Date Format

When TVC is running in an environment that lacks the AEF, then these parameters must be defined.

The different TVC components provides a large set of different parameters that could be manipulated. Uncontrolled changes to some of these might result in decreased performance, or unpredicted behavior.

1.10.1. Applying a System Parameter

A system parameter is typically defined in the deployment description of your application, the web.xml file. A system parameter is defined as init-paramswithin the definition of the TVC servlet.

    <servlet>
        <servlet-name>tvcaction</servlet-name>
        <servlet-class>com.technia.tvc.core.TVCServlet</servlet-class>
        <init-param>
            <param-name>${NAME}</param-name>
            <param-value>${VALUE}</param-value>
        </init-param>
    </servlet>

Where the ${NAME} is the name of the system parameter, and ${VALUE} is the value to apply.

1.10.2. Applying a System Parameter - RMI

If you are using TVC code from JPO’s and your environment uses RMI mode, then settings made into the standard deployment descriptor can not be picked up from the RMI server.

The following list shows the environment variables that can be set to define a system parameter, which otherwise would be set through an init-parameter:

TVC_PRODUCTION_MODE
TVC_PLUGINS
TVC_LOGLEVEL
TVC_DATE_FORMAT
TVC_INPUT_DATE_FORMAT