Table of Contents

Chapter 7: Controllers
Environment variables
Session objects
The controller and its actions
Controllers inheritance
List mode actions
Overwriting default search
Initialize a module with an action
Calling another module
Changing the module of current view
Go to a JSP page
Generating a custom report with JasperReports
Uploading and processing a file from client (multipart form)
Override the default controllers (new in v2.0.3)
All action types

Chapter 7: Controllers

The controllers are used for defining actions (buttons, links, images) that final user can click. The controllers are defined in the controllers.xml file that has to be 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 an 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 its value can be overwritten in each module. Its 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 -->
</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).
  3. action (several, required): Implements the logic to execute when the final user clicks a button or link.
The controllers consist of actions, and actions are the main things. Here is its syntax:
<action
    name="name"                                          <!--  1 -->
    label="label"                                        <!--  2 -->
    description="description"                            <!--  3 -->
    mode="detail|list|ALL"                               <!--  4 -->
    image="image"                                        <!--  5 -->
    class="class"                                        <!--  6 -->
    hidden="true|false"                                  <!--  7 -->
    on-init="true|false"                                 <!--  8 -->
    on-each-request="true|false"                         <!--  9  New in v2.1.2 -->
    before-each-request="true|false"                     <!-- 10  New in v2.2.5 -->
    by-default="never|if-possible|almost-always|always"  <!-- 11 -->
    takes-long="true|false"                              <!-- 12 -->
    confirm="true|false"                                 <!-- 13 -->
    keystroke="keystroke"                                <!-- 14  New in v2.0.1 -->
>
    <set ... /> ...                                      <!-- 15 -->
    <use-object ... /> ...                               <!-- 16 -->
</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. 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.
  6. class (optional): Implements the logic to execute. Must implement IAction interface.
  7. 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.
  8. 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.
  9. 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.
  10. 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.
  11. 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 never.
  12. 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.
  13. 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.
  14. 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".
  15. 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.
  16. 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 and use-object) 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"/>
    </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"/>
    </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"/>
    </action>
</controller>
Now you can include this controller into the module that you want; this is made 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 these defined by you in the controller named Remarks. The top button bar of the module will have this aspect:
controllers_en010.jpg
Ant the bottom button bar:
controllers_en020.jpg
You can note as actions with image are located on top and actions without image are located at bottom.
You can write 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 base class. ViewBaseAction has the property view of type View. This joined to the next declaration in action...
<use-object name="xava_view"/>
...allows 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().
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.

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"/>
    <action name="new" image="images/new.gif"
        class="org.openxava.test.actions.CreateNewFamilyAction">
    <use-object name="xava_view"/>
    </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.

List mode actions

You can write actions that apply to several objects. These actions are usually are 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
        if (selectedOnes != null) {
            for (int i = 0; i < selectedOnes.length; i++) {
                Map key = (Map)
                    getTab().getTableModel().getObjectAt(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, 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() 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"/>
    </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:
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-create=”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"/>
        <use-object name="xava_tab"/>
    </action>
    ...
</controller>
And in your action:
package org.openxava.test.actions;
 
import org.openxava.actions.*;
import org.openxava.tab.*;
 
/**
 * @author Javier Paniza
 */
 
public class InitDefaultYearTo2002Action extends BaseAction {
 
    private int defaultYear;
    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"/>
    </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
    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.
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:

Changing the module of current view

As an alternative to change the module you can choose 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 = getCollectionElementView().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

OpenXava allows the final user to generate their own reports from the list model. The user can perform filtering, ordering, adding/removing fields, changing the positions of the fields and then generate a PDF report of the list.
But in all non-trivial business application you need to create programatically your own reports. You can do that easily by using JasperReports and then by integrating the reports into your OpenXava application with the action JasperReportBaseAction.
In the first place you need to design your report with JasperReports, you can use iReport an excellent designer for JasperReports.
Then you can write your action to print the report in this way:
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, but still supported
            //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:
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 find more examples of JasperReport actions in the OpenXavaTest project, as InvoiceReportAction for printing an Invoice.

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
    }
 
    public String[] getNextControllers() {                                      // 3
        return new String [] { "LoadImage" };
    }
 
    public String getCustomView() {                                             // 4
        return "xava/editors/changeImage";
    }
 
    public boolean isLoadFile() {                                               // 5
        return true;
    }
 
    ...
}
An ILoadFileAction (1) action is also an INavigationAction action that allows you to navigate to another controller (3) and to a custom view (4). The new controller (3) usually will have an action of type IProcessLoadedFileAction. The method isLoadFile() (5) 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 (4) a JSP with your own form to upload the file.
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>
    <action name="hideDetail"                                               <!-- 1 -->
        class="org.openxava.test.actions.MyHideDetailElementInCollection">
    </action>
    <action name="save"
        class="org.openxava.actions.SaveElementInCollectionAction">
        <use-object name="xava_view"/>
    </action>
    <action name="remove"
        class="org.openxava.actions.RemoveElementFromCollectionAction">
        <use-object name="xava_view"/>
    </action>
    <action name="edit"
        class="org.openxava.actions.EditElementInCollectionAction">
        <use-object name="xava_view"/>
    </action>
    <action name="view"
        class="org.openxava.actions.EditElementInCollectionAction">
        <use-object name="xava_view"/>
    </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.

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:
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.