Reference guide: Model | View | Tabular data | Object/relational mapping | Controllers | Application | Customizing

Controllers

Controllers are used to define actions (buttons, links, images) that the final user can click. The controllers are defined in the controllers.xml file that is in the xava directory of your project.
The actions are not defined in components because there are a lot of generic actions that can be applied to any component.
In OpenXava/xava you have a default-controllers.xml that contains a group of generic controllers that can be used in your applications.
The controllers.xml file contains an element of type <controllers/> with the syntax:
<controllers>
    <env-var ... /> ...       <!-- 1 -->
    <object ... /> ...        <!-- 2 -->
    <controller ... /> ...    <!-- 3 -->
</controllers>
  1. env-var (several, optional): Variable that contains configuration information. This variable can be accessed from the actions and filters, and its value can be overwritten in each module.
  2. object (several, optional): Defines Java object with session scope; that is objects that are created for a user and exist during his session.
  3. controller (several, required): A controller is a group of actions.

Environment variables

The environment variables contain configuration information. These variables can be accessed from the actions and filters, and their value can be overwritten in each module. Their syntax is:
<env-var
    name="name"              <!-- 1 -->
    value="value"            <!-- 2 -->
/>
  1. name (required): Name of the environment variable in uppercase and using underscore to separate words.
  2. value (required): Value for the environment variable.
These are some example:
<env-var name="MYAPPLICATION_DEFAULT_YEAR" value="2007"/>
<env-var name="MYAPPLICATION_COLOR" value="RED"/>

Session objects

The Java objects declared in controllers.xml have session scope; that is, they are objects that are created for a user and exist during his session. It's syntax is:
<object
    name="objectName"         <!-- 1 -->
    class="objectType"        <!-- 2 -->
    value="initialValue"      <!-- 3 -->
    scope="module|global"     <!-- 4  New in v2.1 -->
/>
  1. name (required): Name of the object, usually you use the application name as prefix to avoid name collision in large projects.
  2. class (required): Full qualified Java class for this object.
  3. value (optional): Initial value for the object.
  4. scope (optional): (New in v2.1) The default value is module. If you use module scope each module will have its own copy of this object. If you use global scope the same object will be shared by all modules of all OpenXava applications (running in the same .war).
Defining session objects is very easy, you can see the defined ones in OpenXava/xava/default-controllers.xml:
<object name="xava_view" class="org.openxava.view.View"/>
<object name="xava_referenceSubview" class="org.openxava.view.View"/>
<object name="xava_tab" class="org.openxava.tab.Tab"/>
<object name="xava_mainTab" class="org.openxava.tab.Tab"/>
<object name="xava_row" class="java.lang.Integer" value="0"/>
<object name="xava_language" class="org.openxava.session.Language"/>
<object name="xava_newImageProperty" class="java.lang.String"/>
<object name="xava_currentReferenceLabel" class="java.lang.String"/>
<object name="xava_activeSection" class="java.lang.Integer" value="0"/>
<object name="xava_previousControllers" class="java.util.Stack"/>
<object name="xava_previousViews" class="java.util.Stack"/>
These objects are used by OpenXava in order to work, although it is quite normal that you use some of these from your actions. If you want to create your own objects you can do it in your controllers.xml in the xava directory of your project.

The controller and its actions

The syntax of controller is:
<controller
    name="name"                <!-- 1 -->
>
    <extends ... /> ...        <!-- 2 -->
    <action ... /> ...         <!-- 3 -->
    <subcontroller .../> ...   <!-- 4 New in v4.8 -->
</controller>
  1. name (required): Name of the controller.
  2. extends (several, optional): Allows to use multiple inheritance, to do this the controller inherits all actions from other controller(s). Add the excluded-actions (new in v5.8) attribute to prevent certain actions to be inherited.
  3. action (several, required): Implements the logic to execute when the final user clicks a button or link.
  4. subcontroller (several, optional): (New in v4.8) Allows grouping several actions that are shown in a drop-down button.
Since version 5.6 actions and subcontrollers can be intercalated.
The controllers consist of actions, and actions are the main things. Here is the syntax:
<action
    name="name"                                          <!--  1 -->
    label="label"                                        <!--  2 -->
    description="description"                            <!--  3 -->
    mode="detail|list|ALL"                               <!--  4 -->
    icon="icon"                                          <!--  5  New in v5.4 -->
    image="image"                                        <!--  6 -->
    class="class"                                        <!--  7 -->
    hidden="true|false"                                  <!--  8 -->
    on-init="true|false"                                 <!--  9 -->
    on-each-request="true|false"                         <!-- 10  New in v2.1.2 -->
    before-each-request="true|false"                     <!-- 11  New in v2.2.5 -->
    after-each-request="true|false"                      <!-- 12  New in v4.0.1 -->
    by-default="never|almost-never (new in v4m6)|
        if-possible|almost-always|always"                <!-- 13 -->
    takes-long="true|false"                              <!-- 14 -->
    confirm="true|false"                                 <!-- 15 -->
    keystroke="keystroke"                                <!-- 16  New in v2.0.1 -->
    show-dialog="true|false"                             <!-- 17  Only in v4m1 -->
    hide-dialog="true|false|default"                     <!-- 18  Only in v4m1 -->
    in-each-row="true|false"                             <!-- 19  New in v4m4 -->
    process-selected-items="true|false"                  <!-- 20  New in v5.7 -->
    available-on-new="true|false"                        <!-- 21  New in v5.8 -->
>
   <set ... /> ...                                       <!-- 22 -->
   <use-object ... /> ...                                <!-- 23 -->
</action>
  1. name (required): Action name that must be unique within its controller, but it can be repeated in other controllers. When you reference an action always use the format ControllerName.actionName.
  2. label (optional): Button label or link text. It's much better to use i18n files.
  3. description (optional): Description text of the action. It's much better to use i18n files.
  4. mode (optional): Indicates in which mode the action has to be visible. The default value is ALL, that means that this action is always visible.
  5. icon (optional): (New in v5.4) Icon id from Material Design Icons. For example, if you write icon="bell" a bell will be used as icon for you action and you don't need to include any image. Since v5.4 this is the preferred way to define an icon instead of image.
  6. image (optional): URL of the image associated with this action. In the current implementation if you specify an image, it is shown to user in link format. Until v4.1.x the root for the URL was xava, it was needed to use images/ as prefix. Since v4.2 the root depend on the style and in the default style it is xava/images, so it is not longer needed to use the images/ as prefix, though it is still supported for not breaking backward compatibility. If you define both image and icon, icon has preference over image, though you can change this behavior with useIconsInsteadOfImages=false in xava.properties. Inside portals image has always preference.
  7. class (optional): Implements the logic to execute. Must implement IAction interface.
  8. hidden (optional): A hidden action is not shown in the button bar, although it can be used in all other places, for example to associate it to an event, as action of a property, in collections, etc. The default is false.
  9. on-init (optional): If you set this property to true, then the action will be executed automatically on initiating the module. The default is false.
  10. on-each-request (optional): (New in v2.1.2) If you set this property to true, then the action will be executed automatically on each request of the user, that is, on first module execution and before each user action execution. In the moment of execution all OpenXava session objects are setup and ready to use. That is, from this action you can use xava_view and xava_tab. The default is false. Using in conjunction with mode you can discriminate the execution of this action for a concrete mode (list or detail) (new in v3.0.2).
  11. before-each-request (optional): (New in v2.2.5) If you set this property to true, then the action will be executed automatically before each request of the user, that is, on first module execution and before each user action execution, but before the OpenXava session objects are setup and ready to use. That is, from this action you cannot use xava_view or xava_tab. The default is false. Using in conjunction with mode you can discriminate the execution of this action for a concrete mode (list or detail) (new in v3.0.2).
  12. after-each-request (optional): (New in v4.0.1) If you set this property to true, then the action will be executed automatically after each request of the user, that is, on first module execution and before each user action execution. The default is false. Using in conjunction with mode you can discriminate the execution of this action for a concrete mode (list or detail).
  13. by-default (optional): Indicates the weight of this action on choosing the action to execute as the default one. The default action is executed when the user presses ENTER. The default is almost-never (until v4m5 was never).
  14. takes-long (optional): If you set it to true, then you are indicating that this action takes long time in executing (minutes or hours). In the current implementation OpenXava shows a progress bar. The default is false.
  15. confirm (optional): If you set it to true, then before executing the action a dialog is shown to the user to ask if he is sure to execute it. The default is false.
  16. keystroke (optional): (New in v2.0.1) Defines a keystroke that the user can press for executing this action. The possible values are the same as for javax.swing.KeyStroke. Examples: "control A", "alt x", "F7".
  17. show-dialog (optional): (Only in v4m1, not available since v4m2) If true after the action execution the current user interface will be show inside a modal dialog. The default is false.
  18. hide-dialog (optional): (Only in v4m1, not available since v4m2) If true then if currently there is a modal dialog shown then it will be hidden. The default value is default, it means that the dialog will be hidden only when the action name is "cancel" or it is the default action.
  19. in-each-row (optional): (New in v4m4) If true and this action is shown in list mode or in a collection the action will be shown in each row. The action must have an int row property (TabBaseAction and CollectionBaseAction already have the row property). The default is false.
  20. process-selected-items (optional): (New in v5.7) If true this action processes the selected rows in the list. In this way OpenXava can hide this action if selecting items is not available. The default is false.
  21. available-on-new (optional): (New in v5.8) If false this action is not available when the user is creating a new entity (when he clicks on New). The default is true.
  22. set (several, optional): Sets a value of action properties. Thus the same action class can be configured in different ways and it can be used in several controllers.
  23. use-object (several, optional): Assigns a session object to an action property just before executing the action. After the execution the property value is put back into the context again (update the session object, thus you can update even immutable objects).
Actions are short life objects, when a user clicks a button, then the action object is created, configured (with set, use-object or @Inject) and executed. After that the session objects are updated, and finally the action object is discarded.
A plain controller might look like this:
<controller name="Remarks">
    <action name="hideRemarks"
        class="org.openxava.test.actions.HideShowPropertyAction">
        <set property="property" value="remarks" />
        <set property="hide" value="true" />
        <use-object name="xava_view"/>  <!-- Not needed since v4m2 -->
    </action>
    <action name="showRemarks" mode="detail"
        class="org.openxava.test.actions.HideShowPropertyAction">
        <set property="property" value="remarks" />
        <set property="hide" value="false" />
        <use-object name="xava_view"/>  <!-- Not needed since v4m2 -->
    </action>
    <action name="setRemarks" mode="detail"
        class="org.openxava.test.actions.SetPropertyValueAction">
        <set property="property" value="remarks" />
        <set property="value" value="Hell in your eyes" />
        <use-object name="xava_view"/>  <!-- Not needed since v4m2 -->
    </action>
</controller>
Now you can include this controller in the module that you want; this is done by editing in xava/application.xml the module in which you can use these actions:
<module name="Deliveries">
    <model name="Delivery"/>
    <controller name="Typical"/>
    <controller name="Remarks"/>
</module>
Thus you have in your module the actions of Typical (CRUD and printing) plus those defined by you in the controller named Remarks. The top button bar of the module will have this aspect:
controllers_en010.jpg
And the bottom button bar:
controllers_en020.jpg
Note that actions with image/icon are located at the top of the page, and actions without image/icon are located at the bottom of the page.
You can write the code for hideRemarks like this:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
 
public class HideShowPropertyAction extends ViewBaseAction {    // 1
 
    private boolean hide;
    private String property;
 
    public void execute() throws Exception {                    // 2
        getView().setHidden(property, hide);                    // 3
    }
 
    public boolean isHide() {
        return hide;
    }
 
    public void setHide(boolean b) {
        hide = b;
    }
 
    public String getProperty() {
        return property;
    }
 
    public void setProperty(String string) {
        property = string;
    }
 
}
An action must implement IAction, but usually it extends from a base class that implements this interface. The base action more basic is BaseAction that implements most of the method of IAction except execute(). In this case you use ViewBaseAction as the base class. ViewBaseAction has the property view of type View. This, joined to the next declaration in action...
<use-object name="xava_view"/>  <!-- Not needed since v4m2 -->
...allows you to manage the view (the user interface) from an action using view.
The <use-object /> gets the session object xava_view and assigns it to the property view (removing the prefix xava_, in general removes the prefix myapplication_ before assigning object to property) of your action just before calling execute(). Though since v4m2 is not needed to use <use-object /> to inject xava_view because ViewBaseAction already inject it using @Inject.
Now inside the execute() method you can use getView() as you want (3), in this case for hiding a property. You can see all View possibilities in the JavaDoc of org.openxava.view.View.
With...
<set property="property" value="remarks" />
<set property="hide" value="true" />
you can set constant values to the properties of your action.

Subcontroller (new in v4.8)

With the subcontroller we can grouping several actions and show them in a drop-down button.
Here is its syntax:
<subcontroller
    controller="controllerName"       <!-- 1 -->
    image="image"                     <!-- 2 -->
    icon="icon"                       <!-- 3  New in v5.4 -->
    mode="detail|list|ALL" />         <!-- 4 -->
  1. controller: (required) Controller's name that we reference
  2. image: (optional) URL of the image associated with this subcontroller
  3. icon: (optional): (New in v5.4) Icon id from Material Design Icons. For example, if you write icon="bell" a bell will be used as icon for you subcontroller and you don't need to include any image. Since v5.4 this is the preferred way to define an icon instead of image.
  4. mode: (optional) The default value is ALL. Indicates in which mode the action has to be visible.
In your controllers.xml you can put:
<controllers>
    <controller name="Color">
        <action name="seeMessage"
                class="org.openxava.test.actions.SeeMessageAction"/>
        <subcontroller
                controller="ColorSub" <!-- 1 -->
                icon="menu"/>
    </controller>
</controllers>
You obtain this:
subcontroller.GIF

You can put the controller name in i18n files.

Dependency injection

Using Dependency injection the value of a field or property is set by the framework, not by the developer.

@Inject (JSR-330) (new in v4m2)

Since v4m2 OpenXava supports JSR-330, the Java standard for dependency injection. In order to inject a session object in an action you only have to annotate a field with @javax.inject.Inject . Thas is, if you have a session object and an action in your controllers.xml as following:
<controllers>
    <object name="xavatest_activeYear" class="java.lang.Integer" value="2010" scope="global"/>
 
    <controller name="ActiveYear">
        <action name="change"
                   class="org.openxava.test.actions.ChangeActiveYearAction"/>
    </controller>
</controllers>
 
In order to inject the xavatest_activeYear object into ChangeActionYearAction you have to use @Inject, in this way:
public class ChangeActiveYearAction extends ViewBaseAction {
 
    @Inject
    private int activeYear; // Getter and setter are not needed
 
    public void execute() throws Exception {
         assert activeYear == 2010; // The value from session object
         activeYear = 2012; // It changes the value of session object
    }
 
}
Thus the xavatest_activeYear object is injected in the activeYear field before call to execute(); and after execute() execution the value of the activeYear field is assigned back to xavatest_actionYear. So, you can change session objects state from inside an action, even with immutable and primitive types.
As you see, the default name of the session object to inject is the name of the attribute ignoring the prefix (ignoring xavatest_ in this case). Although you can use @Named (also JSR-330 standard) to specify a different name for session object and field:
public class ChangeActiveYearAction extends ViewBaseAction {
 
    @Inject @Named("xavatest_activeYear")
    private int year; // getter and setter are not needed
 
    public void execute() throws Exception {
        assert year == 2010; // The value from xavatest_actionYear session object
        year = 2012; // It changes the value of xavatest_actionYear
    }
 
}
In this way the xavatest_activeYear session object is injected into the year field of the action.

Using <use-object /> for dependency injection

Dependency injection has been in OpenXava since its very beginning. The traditional way to inject session objects in an action is by means of <use-object /> in <action/> declaration. If you are using a version previous to v4m2, you must write the above example in using <use-object/> in controllers.xml:
<controllers>
    <object name="xavatest_activeYear" class="java.lang.Integer" value="2010" scope="global"/>
 
    <controller name="ActiveYear">
        <action name="change"
                   class="org.openxava.test.actions.ChangeActiveYearAction">
        <use-object name="xavatest_activeYear"/>
        </action>
    </controller>
</controllers>
And writing your class without @Inject, and using getter and setter:
public class ChangeActiveYearAction extends ViewBaseAction {
 
    private int activeYear; // No @Inject, we are using <use-object/>
 
    public void execute() throws Exception {
         assert activeYear == 2010; // The value from session object
         activeYear = 2012; // It changes the value of session object
    }
 
    public void setActiveYear(int activeYear) {  // Setter and...
         this.activeYear = activeYear;
    }
 
    public int getActiveYear() { // getter are needed
         return activeYear;
    }
 
}
You can use action-property attribute to specify a different name for session object and field:
<action name="change"
   class="org.openxava.test.actions.ChangeActiveYearAction">
      <use-object name="xavatest_activeYear" action-property="year"/>
</action>
And then you have to write the action in this way:
public class ChangeActiveYearAction extends ViewBaseAction {
 
    private int year; // No @Inject, we are using <use-object/>
 
    public void execute() throws Exception {
        assert year == 2010; // The value from xavatest_actionYear session object
        year = 2012; // It changes the value of xavatest_actionYear
    }
 
    public void setYear(int year) {  // Setter and...
         this.year = year;
    }
 
    public int getYear() { // getter are needed
         return year;
    }
 
}
You see how action-property is the counterpart of @Name of JSR-330.


You can use both <use-object/> and @Inject for dependency injection, but given that JSR-330 is the Java standard it's the favorite one.
(New in v4.7) If the session object to be injected has a property named request, this property is populated with the current HttpServletRequest before injecting the object in the action. It works in this way for objects injected using <use-object/> or @Inject.

Controllers inheritance

You can create a controller that inherits all actions from one or more controllers. An example of this is the generic controller called Typical, this controller is in OpenXava/xava/default-controllers.xml:
<controller name="Typical">
    <extends controller="Print"/>
    <extends controller="CRUD"/>
</controller>
When you assign the controller Typical to a module this module will have available all actions of Print controller (to generate PDF reports and export to Excel) and CRUD controller (to Create, Read, Update and Delete)
You can use inheritance to refine the way a standard controller works, e. g. like this:
<controller name="Family">
    <extends controller="Typical" excluded-actions="refresh, delete"/>
    <action name="new" icon="library-plus"
        class="org.openxava.test.actions.CreateNewFamilyAction">
        <use-object name="xava_view"/>  <!-- Not needed since v4m2 -->
    </action>
</controller>
As you see the name of your action new matches with an action in Typical controller (in reality in CRUD controller from which extends Typical). In this case the original action is ignored and your action is used. Thus you can put your own logic to execute when a final user clicks the 'new' link. Also you can prevent that certain actions would be inherited using the excluded-action attribute (new in v5.8), in this case Family will not include refresh and delete, normally available from CRUD via Typical.

List mode actions

You can write actions that apply to several objects. These actions are usually shown in list mode only and normally have effects on the objects chosen by user only.
An example can be:
<action name="deleteSelected" mode="list"                    <!-- 1 -->
    confirm="true"                                           <!-- 2 -->
    class="org.openxava.actions.DeleteSelectedAction">
</action>
You set mode=”list” in order to show it only in list mode (1). Since this action deletes records you require that the user must confirm explicitly before the action is executed (2). It's not needed to include a <use-object/> for xava_tab (new in v2.1.4).
The action source code:
package org.openxava.actions;
 
import java.util.*;
 
import org.openxava.model.*;
import org.openxava.validators.*;
 
/**
* @author Javier Paniza
*/
 
public class DeleteSelectedAction extends TabBaseAction implements IModelAction {     // 1
    private String model;
 
    public void execute() throws Exception {
        // int [] selectedOnes = getTab().getSelected();                              // 2
        // int [] selectedOnes = getSelected();  // New in v4m4. Deprecated in v4.7   // 2
        Map [] selectedOnes = getSelectedKeys(); // New in 4.7                        // 2
        if (selectedOnes != null) {
            for (int i = 0; i < selectedOnes.length; i++) {
                Map key = selectedOnes[i];
                try {
                    MapFacade.remove(model, key);                                     // 3
                }
                catch (ValidationException ex) {
                    addError("no_delete_row", new Integer(i), key);                   // 4
                    addErrors(ex.getErrors());
                }
                catch (Exception ex) {
                    addError("no_delete_row", new Integer(i), key);
                }
            }
            getTab().deselectAll();                                                  // 5
            resetDescriptionsCache();                                                // 6
        }
    }
 
    public void setModel(String modelName) {                                         // 7
        this.model = modelName;
    }
}
This action is a standard action of OpenXava, but it allows you to see the things that you can do within an action in list mode. You can observe (1) how the action extends from TabBaseAction and implements IModelAction. Since it extends from TabBaseAction (new in v2.1.4) it has a group of utilities and you don't need to implement all methods of IAction; and as it implements IModelAction this action has a method called setModel() (7) that receives the model name (the name of OpenXava component) before executing it.
You can access to the Tab using the getTab() method (2); this method is implemented in TabBaseAction and it allows you to access to the xava_tab session object. By means of getTab() you are allowed to manage the list of displayed objects. For example, with getTab().getSelected() (2) you obtain the indexes of selected rows, though since v4m4 is better to use getSelected() instead which is a method from TabBaseAction. Since v4.7 getSelected() is deprecated instead we use getSelectedKeys() that returns the keys map. With getTab().getTableModel() a table model to access to data, and with getTab().deselectAll() you deselect the rows. You can take a look of org.openxava.tab.Tab JavaDoc for more details on its possibilities.
Something very interesting you can see in this example is the use of MapFacade (3). MapFacade allows you to access the data model using Java maps (java.util.Map). This is useful, if you get data from Tab or View in Map format and you want to update the model (and therefore the database) with it, or vice versa. All generic classes of OpenXava use MapFacade to manage the model and you also can use MapFacade. As general design tip: working with maps is useful in the case of generic logic, but if you need to program specific things it is better to use directly the object of model layer. For more details have a look at the JavaDoc of org.openxava.model.MapFacade.
You see here how to display messages to the user with addError(). The addError() method receives the id of an entry in your i18n files and the argument to send to the message. The added messages are displayed to the user as errors. If you want to add warning or informative messages you can use addMessage(), addInfo() (new in v4.3) or addWarning() (new in v4.3) whose behavior is like addError(). The i18n files that hold errors and messages must be called MyProject-messages.properties and the language sufix (_en, _ca, _es, _it, etc). You can see the examples in OpenXavaTest/xava/i18n. All not caught exceptions produces a generic error messages, except if the not caught exception is of the type ValidationException. In this case the message exception is displayed.
The resetDescriptionsCache() (6) method deletes all cache entries used by OpenXava to display descriptions list (combos). It's a good idea to call it whenever data is updated.
You can see more possibilities in org.openxava.actions.BaseAction and org.openxava.actions.TabBaseAction JavaDoc.
Since v2.1.4 this type of actions can also be used as @ListAction (<list-action/> of a <collection-view/>).

Overwriting default search

When a module is shown in list mode and the user clicks to display a detail, then OpenXava searches the corresponding object and displays it in detail. Now, if in detail mode the user fills the key fields and clicks on search (the binoculars), it also does the same. And when the user navigates by the records clicking the next or previous buttons then it does the same search. How can you customize this search? Let's see that:
You only need to define the module in xava/application.xml this way:
<module name="Deliveries">
    <env-var name="XAVA_SEARCH_ACTION" value="Deliveries.search"/>
    <model name="Delivery"/>
    <controller name="Typical"/>
    <controller name="Remarks"/>
    <controller name="Deliveries"/>
</module>
You see how it is necessary to define an environment variable named XAVA_SEARCH_ACTION that contains the action that you want to use for searching. This action is defined in xava/controllers.xml:
<controller name="Deliveries">
    <action name="search" mode="detail"
        by-default="if-possible" hidden="true"
        class="org.openxava.test.actions.SearchDeliveryAction"
        keystroke="F8">
        <use-object name="xava_view"/>  <!-- Not needed since v4m2 -->
    </action>
    ...
</controller>
And its code:
package org.openxava.test.actions;
 
import java.util.*;
 
import org.openxava.actions.*;
import org.openxava.util.*;
 
/**
 * @author Javier Paniza
 */
 
public class SearchDeliveryAction extends SearchByViewKeyAction {      // 1
 
    public void execute() throws Exception {
        super.execute();                                               // 2
        if (!Is.emptyString(getView().getValueString("employee"))) {
            getView().setValue("deliveredBy", new Integer(1));
            getView().setHidden("carrier", true);
            getView().setHidden("employee", false);
        }
        else {
            Map carrier = (Map) getView().getValue("carrier");
            if (!(carrier == null || carrier.isEmpty())) {
                getView().setValue("deliveredBy", new Integer(2));
                getView().setHidden("carrier", false);
                getView().setHidden("employee", true);
            }
            else {
                getView().setHidden("carrier", true);
                getView().setHidden("employee", true);
            }
        }
    }
 
}
In this action you have to search the database (or through EJB2, EJB3 JPA or Hibernate) and fill the view. Most times it is better that it extends SearchByViewKeyAction (1) and within execute() write a super.execute() (2).
OpenXava comes with 3 predefined search actions:
  • CRUD.searchByViewKey: This is the default one. It does a search using the key values in the view, it executes no event.
  • CRUD.searchExecutingOnChange: Works as searchByViewKey but it executes the @OnChange/on-change actions after search data.
  • CRUD.searchReadOnly: Works as searchByViewKey but it set the detail view to not editable state on searching. Useful for creating read only modules.
If you want that the @OnChange/on-change actions will be executed on search then you must define your module this way:
<module name="Products3ChangeActionsOnSearch">
    <env-var name="XAVA_SEARCH_ACTION" value="CRUD.searchExecutingOnChange"/>
    <model name="Product3"/>
    <view name="WithDescriptionsList"/>
    <controller name="Typical"/>
    <controller name="Products3"/>
    <mode-controller name="Void"/>
</module>
As you see, simply by setting the value of the XAVA_SEARCH_ACTION environment variable.

Initialize a module with an action

By setting on-init=”true” when you define an action, you configure that this action will be executed automatically when the module is executed for the first time. This is a chance to initialize the module. Let's see an example. In your controllers.xml you write:
<controller name="Invoices2002">
    <action name="init" on-init="true" hidden="true"
        class="org.openxava.test.actions.InitDefaultYearTo2002Action">
        <!-- <use-object name="xavatest_defaultYear"/>
           Since v4m2 you can use @Inject instead -->
        <!-- <use-object name="xava_tab"/>
           Since v4m2 you can use @Inject instead -->
    </action>
    ...
</controller>
And in your action:
package org.openxava.test.actions;
 
import javax.inject.*;
import org.openxava.actions.*;
import org.openxava.tab.*;
 
/**
 * @author Javier Paniza
 */
 
public class InitDefaultYearTo2002Action extends BaseAction {
 
    @Inject  // Since 4m2, if you do not use <use-object />
    private int defaultYear;
 
    @Inject  // Since 4m2, if you do not use <use-object />
    private Tab tab;
 
    public void execute() throws Exception {
        setDefaultYear(2002);                     // 1
        tab.setTitleVisible(true);                // 2
        tab.setTitleArgument(new Integer(2002));  // 3
    }
 
    public int getDefaultYear() {
        return defaultYear;
    }
 
    public void setDefaultYear(int i) {
        defaultYear = i;
    }
 
    public Tab getTab() {
        return tab;
    }
 
    public void setTab(Tab tab) {
        this.tab = tab;
    }
 
}
In this action you set the default year to 2002 (1), you make the title list visible (2) and you assign a value as an argument to that title (3). The list title is defined in the i18n files, usually it's used for reports, but you can show it in list mode too.

Calling another module

Sometimes it's convenient to call programmatically one module from another one. For example, imagine that you want to show a list of customers and when the user clicks on one customer, then a list of its invoices is displayed and the user can choose an invoice to edit. One way to obtain this effect is to have a module with only list mode and when the user clicks on a detail, the user is directed to an invoices module that shows only the invoices of the chosen customer. Let's see it. First you need to define the module in application.xml this way:
<module name="InvoicesFromCustomers">
    <env-var name="XAVA_LIST_ACTION" value="Invoices.listOfCustomer"/>  <!-- 1 -->
    <model name="Customer"/>
    <controller name="Print"/>
    <controller name="ListOnly"/>                                       <!-- 2 -->
    <mode-controller name="Void"/>                                      <!-- 3 -->
</module>
In this module only the list is shown (without detail part), for this you set the mode controller to Void (3) thus 'detail' and 'list' links are not displayed; and also you add a controller called ListOnly (2) in order to show the list mode, and only the list mode (if you only set the mode controller to Void the detail, and only the detail is displayed). Moreover you declare the variable XAVA_LIST_ACTION to define your custom action. When the user clicks the link in each row, then your own action will be executed. You must declare this action in controllers.xml:
<controller name="Invoices">
    <action name="listOfCustomer" hidden="true"
        class="org.openxava.test.actions.ListCustomerInvoicesAction">
        <!-- <use-object name="xava_tab"/>
          Since v4m2 you can use @Inject instead -->
    </action>
    ...
</controller>
And the action code:
package org.openxava.test.actions;
 
import java.util.*;
 
import org.openxava.actions.*;
import org.openxava.controller.*;
import org.openxava.tab.*;
 
/**
 * @author Javier Paniza
 */
public class ListCustomerInvoicesAction extends BaseAction
    implements IChangeModuleAction,                                           // 1
               IModuleContextAction {                                         // 2
 
    private int row;                                                          // 3
 
    @Inject  // Since v4m2, if you do not use <use-object />
    private Tab tab;
    private ModuleContext context;
 
    public void execute() throws Exception {
        Map customerKey = (Map) tab.getTableModel().getObjectAt(row);         // 4
        int customerNumber = ((Integer) customerKey.get("number")).intValue();
        Tab invoiceTab = (Tab)
        context.get("OpenXavaTest", getNextModule(), "xava_tab");             // 5
        invoiceTab.setBaseCondition("${customer.number} = "+customerNumber);  // 6
    }
 
    public int getRow() {                                                     // 3
        return row;
    }
    public void setRow(int row) {                                             // 3
        this.row = row;
    }
 
    public Tab getTab() {
        return tab;
    }
    public void setTab(Tab tab) {
        this.tab = tab;
    }
 
    public String getNextModule() {                                           // 7
        return "CustomerInvoices";
    }
 
    public void setContext(ModuleContext context) {                           // 8
        this.context = context;
    }
 
    public boolean hasReinitNextModule() {                                    // 9
        return true;
    }
 
}
In order to change to another module the action implements IChangeModuleAction (1) thus forces the action to have a method called getNextModule() (7). This will indicate to which module OpenXava will switch after executing this action. The method hasReinitNextModule() (9) indicates, whether you want that the target module has re-initiated on changing to it.
On the other hand this action implements IModuleContextAction (2) too and therefore it receives an object of type ModuleContext with the method setContext() (8). ModuleContext allows you to access the session objects of others modules. This is useful to configure the target module before changing to it.
Since v4m1 BaseAction implements IModuleContextAction, so you only need to use getContext() from your execute() method:
public class ListCustomerInvoicesAction extends BaseAction
    implements IChangeModuleAction,
//           IModuleContextAction { // Not needed since v4m1
 
...
// private ModuleContext context; // Not needed since v4m1
 
public void execute() throws Exception {
    ...
    Tab invoiceTab = (Tab)
    getContext().  // You can use getContext() from BaseAction
        get("OpenXavaTest", getNextModule(), "xava_tab");
    ...
}
...
// public void setContext(ModuleContext context) {  // Not needed since v4m1
//    this.context = context;
// }
...
}
Another detail is that the action specified in XAVA_LIST_ACTION must have a property named row (3); before executing the action this property is filled with the row number that user has clicked.
If you keep in mind the above details it is easy to understand the action:
  • Gets the key of the object associated to the clicked row (4), to do this it uses the tab of the current module.
  • Accesses to the tab of the target module using context (5).
  • Sets the base condition of the tab of target module using the key obtained from current tab.

Showing a new view (new in v4m2)

As an alternative to change the module you can choose to show a new view. This is easy, you only need to use the APIs available from View. There are methods such as showNewView(), showView() and returnToPreviousView(). An example:
public class ViewCustomerFromInvoiceAction extends ViewBaseAction {
 
    public void execute() throws Exception {
        try {
            Object number = getView().getValue("customer.number"); // 1
            Map key = new HashMap();
            key.put("number", number);
            showNewView();                                         // 2
            getView().setModelName("Customer");                    // 3
            getView().setValues(key);                              // 4
            getView().findObject();                                // 5
            getView().setKeyEditable(false);
            getView().setEditable(false);
            setControllers("Return");                              // 6
        }
        catch (ObjectNotFoundException ex) {
            getView().clear();
            addError("object_not_found");
        }
        catch (Exception ex) {
            ex.printStackTrace();
            addError("system_error");
        }
    }
 
}
This is the code of an action that allows to visualize an object of another type. First you have to memorize the key of of the object to read (1). After this, you show a new view (2) by means of showNewView(). It creates a new view and set it as the default one, so it is displayed. After it any reference to getView() will be for the new one (3). Finally you fill the key values (4) and use findObject() (5) to load all data in the view. Also we use setControllers() (6) to set a new group of actions to show.
When using showNewView() the current view is stored in a stack, and it would be back to the user interface calling to returnToPreviousView() method.

Changing the model of current view

If you work with an OX version older than v4m2 an alternative to show a new view is changing the model of the current view. This is easy, you only need to use the APIs available in View. An example:
public void execute() throws Exception {
    try {
        setInvoiceValues(getView().getValues());               // 1
        Object number = getView().getValue("product.number");
        Map key = new HashMap();
        key.put("number", number);
        getView().setModelName("Product");                     // 2
        getView().setValues(key);                              // 3
        getView().findObject();                                // 4
        getView().setKeyEditable(false);
        getView().setEditable(false);
    }
    catch (ObjectNotFoundException ex) {
        getView().clear();
        addError("object_not_found");
    }
    catch (Exception ex) {
        ex.printStackTrace();
        addError("system_error");
    }
}
This is an extract of an action that allows to visualize an object of another type. First you need to memorize the current displayed data (1), to restore it on returning. After this, you change the model of view (2), this is the important part. Finally you fill the key values (3) and use findObject() (4) to load all data in the view.
When you use this technique you have to keep in mind that each module has only one xava_view object active at a time, thus if you wish to go back you have the responsibility of restoring the original model in the view and restoring the original data.

Go to a JSP page

The automatic view generator of OpenXava is good for most cases, but it can be required to display a JSP page hand-written by you. You can do this with an action like this:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
 
/**
 * @author Javier Paniza
 */
public class MySearchAction extends BaseAction implements INavigationAction {  // 1
 
    public void execute() throws Exception {
    }
 
    public String[] getNextControllers() {                                     // 2
        return new String [] { "MyReference" } ;
    }
 
    public String getCustomView() {                                            // 3
        return "doYouWishSearch.jsp";
    }
 
    public void setKeyProperty(String s) {
    }
 
}
In order to go to a custom view (in this case a JSP page) your action has to implement INavigationAction (ICustomViewAction is enough). This way you can indicate with getNextControllers() (2) the next controllers to use and with getCustomView() (3) the JSP page to display (3).

Generating a custom report with JasperReports

By default, OpenXava includes actions that allow end-users to generate PDF reports and export to Excel, as well as generate their own reports from the list model by filtering, ordering, adding/removing fields, changing the positions of the fields and then generating a PDF report from the list.
Note that these actions are executed in a browser pop-up. If your browser is configured to block pop-ups, you must add the hostname of your OpenXava instance to your browser's list of pop-up exceptions (usually 'localhost' or '127.0.0.1'), otherwise the actions will not execute.
These actions are sufficient for simple reporting requirements, but in all non-trivial business applications you need to create your own customised reports using JasperReports and then integrating the reports into your OpenXava application using JasperReportBaseAction.
You can use iReport Designer, an excellent report development tool for JasperReports, to create the JRXML file that contains the definition of the report layout, then write your action to print the report like this:
package org.openxava.test.actions;
 
import java.util.*;
 
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.*;
 
import org.openxava.actions.*;
import org.openxava.model.*;
import org.openxava.test.model.*;
import org.openxava.util.*;
import org.openxava.validators.*;
 
/**
 * Report of products of the selected subfamily. <p>
 *
 * Uses JasperReports. <br>
 *
 * @author Javier Paniza
 */
public class FamilyProductsReportAction extends JasperReportBaseAction {           // 1
 
    private ISubfamily2 subfamily;
 
    public Map getParameters() throws Exception  {                                 // 2
        Messages errors =
            MapFacade.validate("FilterBySubfamily", getView().getValues());
        if (errors.contains()) throw new ValidationException(errors);              // 3
        Map parameters = new HashMap();
        parameters.put("family", getSubfamily().getFamily().getDescription());
        parameters.put("subfamily", getSubfamily().getDescription());
        return parameters;
    }
 
    protected JRDataSource getDataSource() throws Exception {                      // 4
        return new JRBeanCollectionDataSource(getSubfamily().getProductsValues());
    }
 
    protected String getJRXML() {                                                  // 5
        return "Products.jrxml"; // To read from classpath
        // return "/home/javi/Products.jrxml"; // To read from file system
    }
 
    private ISubfamily2 getSubfamily() throws Exception {
        if (subfamily == null) {
            int subfamilyNumber = getView().getValueInt("subfamily.number");
            // Using JPA, the usual with OX3
            subfamily = XPersistence.getManager().find(
                Subfamily2.class, new Integer(subfamilyNumber));
           // Using Hibernate, the usual with OX2, but still supported
           // subfamily = (ISubfamily2)
           //     XHibernate.getSession().get(
           //        Subfamily2.class, new Integer(subfamilyNumber));
           // Using EJB2, the usual with OX1, not supported since v4
           //subfamily =     Subfamily2Util.getHome().
           //    findByPrimaryKey(new Subfamily2Key(subfamilyNumber));
        }
        return subfamily;
    }
 
}
Your action has to extend JasperReportBaseAction (1) and it has to overwrite the next three method:
  • getParameters() (2): A Map with the parameters to send to the report, in this case we validate the input data (using MapFacade.validate()) before (3).
  • getDataSource() (4): A JRDataSource with data to print. In this case it is a collection of JavaBeans obtained calling a method of the model object. If you use EJB CMP2 Entities be careful and do not loop over an EJB2 Entity collection inside this method, as in this case is better only one EJB call to obtain all data.
  • getJRXML() (5): The XML with the JasperReports design, this file can be in the classpath. You may have a source code folder called reports in your project to hold these files. Other option is put this file in the file system (new in v2.0.3), this is achieved by specifying the full path of file, for example: /home/javi/Products.jrxml or c:\\reports\\Products.jrxml (starting with windows driver letter recognized as absolute path since v3.0.3).
By default the report is displayed in a popup window, but if you wish the report in the current window, then you can overwrite the method inNewWindow().
You can merge several reports in one PDF (new in v5.0). Your action has to extend JasperConcatReportBaseAction. Useful when you need to concatenate several reports with different page formats (landscape, portrait).
You can find more examples of JasperReport actions in the OpenXavaTest project, as InvoiceReportAction for printing an Invoice and MovieReportAction to concatenate reports.

Uploading and processing a file from client (multipart form)

This feature allows you to process in your OpenXava application a binary file (or several) provided by the client. This is implemented in a HTTP/HTML context using HTML multipart forms, although the OpenXava code is technologically neutral, hence your action will be portable to another environment with no recoding.
In order to upload a file the first step is creating an action to direct to a form where the user can choose his file. This action must implements ILoadFileAction in this way:
public class ChangeImageAction extends BaseAction implements ILoadFileAction {  // 1
    ...
    public void execute() throws Exception {                                    // 2
        showDialog();                                                           // 3
    }
 
    public String[] getNextControllers() {                                      // 4
        return new String [] { "LoadImage" };
    }
 
    public String getCustomView() {                                             // 5
        return "xava/editors/changeImage";
    }
 
    public boolean isLoadFile() {                                               // 6
        return true;
    }
 
    ...
}
An ILoadFileAction (1) action is also an INavigationAction action that allows you to navigate to another controller (4) and to a custom view (5). The new controller (4) usually will have an action of type IProcessLoadedFileAction. The method isLoadFile() (6) returns true in case that you want to navigate to the form to upload the file, you can use the logic in execute() (2) to determine this value. The custom view is (5) a JSP with your own form to upload the file. Optionally you can call showDialog() (3) if you want the form to choose files would be in a dialog.
An example of a JSP for a custom view is:
<%@ include file="../imports.jsp"%>
 
<jsp:useBean id="style" class="org.openxava.web.style.Style" scope="request"/>
 
<table>
    <th align='left' class=<%=style.getLabel()%>>
        <fmt:message key="enter_new_image"/>
    </th>
    <td>
        <input name = "newImage" class=<%=style.getEditor()%> type="file" size='60'/>
    </td>
</table>
As you see, the HTML form is not specified, because the OpenXava module already has the form.
The last piece is the action for processing the uploaded files:
public class LoadImageAction extends BaseAction
    implements INavigationAction, IProcessLoadedFileAction {          // 1
 
    private List fileItems;
    private View view;
    private String newImageProperty;
 
    public void execute() throws Exception {
        Iterator i = getFileItems().iterator();                       // 2
        while (i.hasNext()) {
            FileItem fi = (FileItem)i.next();                         // 3
            String fileName = fi.getName();
            if (!Is.emptyString(fileName)) {
                getView().setValue(getNewImageProperty(), fi.get());  // 4
            }
        }
    }
 
    public String[] getNextControllers() {
        return DEFAULT_CONTROLLERS;
    }
 
    public String getCustomView() {
        return DEFAULT_VIEW;
    }
 
    public List getFileItems() {
        return fileItems;
    }
 
    public void setFileItems(List fileItems) {                        // 5
        this.fileItems = fileItems;
    }
    ...
}
The action implements IProcessLoadedFileAction (1), thus the action must have a method setFileItem() (5) to receive the list of uploaded files. This list can be processed in execute() (2). The elements of the collection are of type org.apache.commons.fileupload.FileItem (4) (from fileupload project of apache commons). Only calling to get() (4) in the file item you will access to the content of the uploaded file.

Override the default controllers (new in v2.0.3)

The controllers in OpenXava/xava/default-controllers.xml (before v2.0.3 it was OpenXava/xava/controllers.xml) are used by OpenXava to give to application a default behavior. Many times the easier way to override the default behavior of OpenXava is creating our own controllers and use them in our applications, that is you can create a controller called MyTypical, and using it in your modules instead of Typical that comes with OpenXava.
Another option is override a default controller of OpenXava. In order to override a default controller you only need to create in your application a controller with the same name of default one. For example, if you want refine the collections behavior for your application then you have to create a Collection controller in your xava/controllers.xml, as following:
<controller name="Collection">
    <action name="new"
        class="org.openxava.actions.CreateNewElementInCollectionAction"/>
    <action name="hideDetail"                                               <!-- 1 -->
        class="org.openxava.test.actions.MyHideDetailElementInCollection"/>
    <action name="save"
        class="org.openxava.actions.SaveElementInCollectionAction">
        <use-object name="xava_view"/>  <!-- Not needed since 4m2 -->
    </action>
    <action name="remove"
        class="org.openxava.actions.RemoveElementFromCollectionAction">
        <use-object name="xava_view"/>  <!-- Not needed since 4m2 -->
    </action>
    <action name="edit"
        class="org.openxava.actions.EditElementInCollectionAction">
        <use-object name="xava_view"/>  <!-- Not needed since 4m2 -->
    </action>
    <action name="view"
        class="org.openxava.actions.EditElementInCollectionAction">
        <use-object name="xava_view"/>  <!-- Not needed since 4m2 -->
    </action>
</controller>
In this case we only override the behavior of hideDetail (1) action. But we must declare all actions of the original controller, because OpenXava rely on all these actions for working; we cannot remove or rename actions.

Showing a modal dialog (new in v4m2)

You can show a dialog calling to showDialog() method of ViewBaseAction. We suppose the current view contains an address (embeddable), and we want an action to show a dialog to fill the full address in only one line.
The action declaration in controllers.xml can be:
<controller name="Address">
    <action name="addFullAddress"
        class="org.openxava.test.actions.GoAddFullAddressAction"/>
</controller>
Here you have the action code:
public class GoAddFullAddressAction extends ViewBaseAction {  // 1
 
    public void execute() throws Exception {
        showDialog();                                         // 2
        getView().setTitleId("entry_full_address");           // 3
        // getView().setTitle("Entry the full address");      // 4
        getView().setModelName("OneLineAddress");             // 5
        setControllers("AddFullAddress", "Dialog");           // 6
        // addActions("AddFullAddress.add", "Dialog.cancel"); // 7
    }
 
}
Basically, it shows a new view inside a dialog (2) , set its title (3), its content (5) and the dialog buttons (6).
It must extend ViewBaseAction (1) to use showDialog().
The dialog buttons are specified by means of setControllers (6) or addActions (7). The 'Dialog' controller contains a default 'cancel' action. Though you can specify your own cancel action, indeed if you have an action named 'cancel' it will be executed automatically when the user will close the dialog window.
The title can be set using View.setTitleId() (3), in this case you indicate a id from i18n files (labels or messages), or you can use View.setTitle() (4) to put the title literally. If you do not specify title OpenXava generates one from the action description.
To set the dialog content we use View.setModelName() to assign an entity or transient class to de current view. In our case it's a transient class, OneLineAddress:
public class OneLineAddress {
 
    private String fullAdress;
 
    public String getFullAdress() {
        return fullAdress;
    }
 
    public void setFullAdress(String fullAdress) {
        this.fullAdress = fullAdress;
    }
 
}
Just one property. So the dialog will have only a label with a text field to entry the full address.
Let's see the AddFullAddress controller that defines the dialog buttons:
<controller name="AddFullAddress">
    <action name="add"
        class="org.openxava.test.actions.AddFullAddressAction"/>
</controller>
As you can see we declare the main action of the dialog, whose code is:
public class AddFullAddressAction extends ViewBaseAction {
 
    public void execute() throws Exception {
        String fullAddress = getView().getValueString("fullAdress"); // 1
        String [] tokens = fullAddress.split(" ");
        View addressView = getPreviousView().getSubview("address");  // 2
        String [] properties = { "state.id", "city", "zipCode", "street" };
        int iTokens = tokens.length;
        for (int iProperties = 0; iProperties < 4 && iTokens > 0; iProperties++) {
            addressView.setValue(properties[iProperties], tokens[--iTokens]);
        }
        StringBuffer street = new StringBuffer();
        for (int i = 0; i <= iTokens; i++) {
            street.append(tokens[i]);
            street.append(' ');
        }
        addressView.setValue("street", street.toString().trim());
 
        closeDialog();                                      // 3
    }
 
}
Note how using getView() (1) you can access to the dialog content, because now the current view is the dialog. Also you can access to the previous view (2) (the view back, the main view of the module in this case) to manage it, using getPreviousView(). To discard the dialog view and set as current view the previous one we call to closeDialog() (3).

Showing a modal dialog (only in v4m1)

Note: This way to use dialogs is not available since v4m2
Showing a dialog is declarative. You can take any of your existing actions, marking it as show-dialog="true" in its <action /> declaration, and when the action will be executed a dialog will be shown.
Let's make an example. We suppose the current view contains an address (embeddable), and we want an action to show a dialog to fill the full address in only one line.
The action declaration in controllers.xml can be:
<controller name="Address">
 
    <action name="addFullAddress"
        show-dialog="true"                         <!-- 1 -->
        class="org.openxava.test.actions.GoAddFullAddressAction">
        <use-object name="xava_view"/>             <!-- 2 -->
        <use-object name="xava_previousViews"/>    <!-- 3 -->
    </action>
 
</controller>
Just show-dialog="true" is enough to show the dialog. By default the dialog shows the current view, so we want to change the current view in order to define the dialog content, and when the dialog will be close we'll restore the original view. To do this we inject xava_view (2) to manager the current view and xava_previousViews to navigate to a new view and then return back.
Here you have the action code:
public class GoAddFullAddressAction
    extends ViewBaseAction                                // 1
    implements IChangeControllersAction {                 // 2
 
    public void execute() throws Exception {
        showNewView();                                    // 3
        getView().setTitleId("entry_full_address");       // 4
        // getView().setTitle("Entry the full address");
        getView().setModelName("OneLineAddress");         // 5
    }
 
    public String[] getNextControllers() throws Exception {
        return new String [] { "AddFullAddress" };        // 2
    }
 
}
Basically, it shows a new view (3), set its title (4), its content (5) and the dialog buttons (1).
It must extend ViewBaseAction (1) to use showNewView() (that uses xava_previousViews) and getView() (that uses xava_view), and it implements IChangeControllersAction (2) to define the action buttons, in this case the actions from AddFullAddress controller.
The title can be set using View.setTitleId() (4) in this case you indicate a id from i18n files (labels or messages), or you can use View.setTitle() to put the title literally. If you do not specify title OpenXava generates one from the action description.
To set the dialog content we use View.setModelName() to assign an entity or transient class to de current view. In our case it's a transient class, OneLineAddress:
public class OneLineAddress {
 
    private String fullAdress;
 
    public String getFullAdress() {
        return fullAdress;
    }
 
    public void setFullAdress(String fullAdress) {
        this.fullAdress = fullAdress;
    }
 
}
Just one property. So the dialog will have only a label with a text editor to entry the full address.
Let's see the AddFullAddress controller that defines the dialog buttons:
<controller name="AddFullAddress">
 
    <action name="add" hide-dialog="true"          <!-- 1 -->
        class="org.openxava.test.actions.AddFullAddressAction">
        <use-object name="xava_previousViews"/>      <!-- 2 -->
        <use-object name="xava_view"/>
    </action>
 
    <action name="cancel"                            <!-- 3 -->
        class="org.openxava.actions.CancelAction">
        <use-object name="xava_previousViews"/>      <!-- 2 -->
        <use-object name="xava_view"/>
    </action>
 
</controller>
As you can see the dialog will have two buttons: "add" and "cancel". Both actions hide the dialog on finish, "add" because declare hide-dialog="true" and "cancel" because its name (3); actions named "cancel" hide by default the dialog. Both actions inject xava_previousViews (2) to return back to previous view.
The CancelAction is included in OpenXava. The AddFullAddressAction code is as following:
public class AddFullAddressAction extends ViewBaseAction {
 
    public void execute() throws Exception {
        String fullAddress = getView().getValueString("fullAdress"); // 1
        String [] tokens = fullAddress.split(" ");
        View addressView = getPreviousView().getSubview("address");  // 2
        String [] properties = { "state.id", "city", "zipCode", "street" };
        int iTokens = tokens.length;
        for (int iProperties = 0; iProperties < 4 && iTokens > 0; iProperties++) {
            addressView.setValue(properties[iProperties], tokens[--iTokens]);
        }
        StringBuffer street = new StringBuffer();
        for (int i = 0; i <= iTokens; i++) {
            street.append(tokens[i]);
            street.append(' ');
        }
        addressView.setValue("street", street.toString().trim());
 
        returnToPreviousView();                                      // 3
}
 
Note how using getView() (1) you can access to the dialog content, because now the current view is the dialog. Also you can access to the previous view (2) (the view back, the main view of the module in this case) to manage it, using getPreviousView(). To discard the dialog view and set as current view the previous one we call to returnToPreviousView() (3).
This view navigation logic is not specific for dialogs, indeed you can remove the show-dialog="true" and see how it works fine, but without dialog.

All action types

You have seen until now that the behavior of your actions depends on which interfaces they implement. Next the available interfaces for actions are enumerated:
  • IAction: Basic interface to be implemented by all actions.
  • IChainAction: Allows you to chain actions, that is when the execution of the action finishes, then the next action is executed immediately.
  • IChainActionWithArgv: (New in v2.2) It's a refinement of IChainAction. It allows to send as arguments values for filling properties of the chained action before execute it.
  • IChangeControllersAction: To change the controller (the actions) available to user. New in v4m2: You can just use the setControllers(), returnToPreviousControllers(), setDefaultControllers(), addActions(), removeActions() and clearActions() methods of BaseAction instead of implementing directly this interface.
  • IChangeModeAction: To change the mode, from list to detail or vice versa. New in v4m1: You can just use the setNextMode() method of BaseAction instead of implementing directly this interface.
  • IChangeModuleAction: To change the module.
  • ICustomViewAction: To use as view your custom JSP.
  • IForwardAction: Redirects to an internal URI in the same application, like a JSP or Servlet, or to an absolute URL in internet (absolute URL new in v4m1). It is not like ICustomViewAction; ICustomViewAction puts your JSP inside the user interface generated by OpenXava (that can be inside a portal), while IForwardAction redirects completely to the specified URI.
  • IHideActionAction, IHideActionsAction: Allows to hide an action or a group of actions in the User Interface (new in v2.0). New in v4m2: You can just use the removeActions() and clearActions() methods of BaseAction instead of implementing directly these interfaces.
  • IJDBCAction: Allows to use JDBC in an action directly. It receives an IConnectionProvider. Works like an IJDBCCalculator (see chapter 3).
  • ILoadFileAction: Navigates to a view that allows the final user to load a file.
  • IModelAction: An action that receives the model name.
  • IModuleContextAction: Gets a ModuleContext in order to access the session objects of other modules. New in v4m1: You can just use the getContext() method of BaseAction instead of implementing directly this interface.
  • IMultipleForwardAction: (New in v4.3) Redirects to several internal URIs in the same application, like JSPs or Servlets, or to several absolute URLs in internet.
  • INavigationAction: Extends from IChangeControllersAction and ICustomViewAction.
  • IOnChangePropertyAction: This interface must be implemented by the actions that react to the value change event in the user interface.
  • IProcessLoadedFileAction: Processes a list of files uploaded from client to server.
  • IPropertyAction: This action is associated to an property (displayed in User Interface), before execute it the property name and the container view is injected (new in v2.0.2).
  • IRemoteAction: Useful when you use EJB2. Well used it can be a good substitute for a SessionBean.
  • IRequestAction: Receives a servlet request. This type of actions links your application to the Servlet/JSP technology, hence it is better avoiding it. But sometimes a little bit of flexibility is needed. New in v4m1: You can just use the getRequest() method of BaseAction instead of implementing directly this interface.
  • IShowActionAction, IShowActionsAction: Allows to show an action or a group of actions previously hidden in an IHideAction(s)Action (new in v2.0). New in v4m2: You can just use the addActions() method of BaseAction instead of implementing directly these interfaces.
Many times instead of implementing directly these interfaces your action can extend from a base action, such as BaseAction that already implement them.
If you wish to learn more about actions the best thing you can do is to have a look at the JavaDoc API of the package org.openxava.actions and to try out the examples of OpenXavaTest project.
excluded-actions