Creating a New JavaRosa Powered Application

Error: Failed to load processor TracNav
No macro or processor named 'TracNav' found

JavaRosa is essentially a platform that was written to facilitate the creation of a certain set of applications using a standardized set of features and data formats. Writing a new application generally involves the following steps

  1. Determining which modules are necessary for the application, and obtaining them from SVN
  2. Setting up a new project and MIDlet definition.
  3. Setting up the build environment
  4. Creating a new shell to control the workflow of your application
  5. Integrate module code

In general, the org.javarosa.demo project can be used as a template of a successfully implemented JavaRosa application.

Important Background

Documents: Getting Started, Glossary, J2ME Introduction

Terms: Shell, Activity, Module, J2me Polish, Service, JavaRosa Service Provider (JRSP)

Interfaces: IShell, IActivity

Projects : org.javarosa.core, org.javarosa.core.demo, org.javarosa.core.model, org.javarosa.xform

Determining Necessary Modules

The first step in creating a new application is to determine what set of JavaRosa functionality will need to be used. A list of available modules and packages for JavaRosa is available here, although newer packages might not be listed yet. The SVN branch 'dev' should contain a list of all available projects.

Almost all applications will need the org.javarosa.core and org.javarosa.core.model modules. The org.javarosa.xform module is also generally used, as is the org.javarosa.formmanager' project.

In general, if there is a serious piece of functionality necessary for the new application that is not available in an existing module and activity, it should be created independently of the new application and then integrated in. This allows developers to maximize the amount of re-usable code in JavaRosa, and avoid ever implementing something that has already been created.

The tutorial on how to create a new Activity can be found here. New activities can be tested out by placing them in the org.javarosa.demo entry point project.

After new activities are fully working as tested by running them from the Demo project, the Demo project should continue to reference them. This allows the Demo project to contain a functional example of how to implement all of JavaRosa's available functionality, so that developers can see how to integrate different modules into their applications.

Setting Up a New Project and MIDlet

After collecting all of the projects necessary for the features the new application will require, including any new modules or activities that had to be created, a new project should be made to house the application's entry point. The New Module Tutorial instructions can be followed to make a new project for the entry point, just ignore the section about integrating your code in a building project.

After the new project is created, a MIDlet class should be defined as the entry point of the new application. This can be an extremely simple class, an example follows.

public class ExampleMidlet extends MIDlet {
	IShell shell = null;
	
	
	protected void destroyApp(boolean arg0) throws MIDletStateChangeException {

	}

	protected void pauseApp() {

	}

	protected void startApp() throws MIDletStateChangeException {
		/*
		 * Duplicate this class and change the following line to 
		 * create a custom midlet to launch from 
		 */
		//shell = We will fill this out as soon as the Shell is created

		// Do NOT edit below
		JavaRosaServiceProvider.instance().initialize();
		JavaRosaServiceProvider.instance().setDisplay(Display.getDisplay(this));
                System.out.println("MIDlet run!"); // Remove once shell is written
		//shell.setMIDlet(this);
		//shell.run();
	}
}

The steps that need to be taken in the MIDlet are to initialize the JRSP, assign the MIDlet's display to the JRSP, create an instance of the primary shell (which has not been created yet), store the MIDlet reference in the shell, and run the shell.

Setting up the Build Environment

The JavaRosa build platform is built on top of J2ME Polish and Adobe Ant. To a limited degree it is possible to use build JavaRosa applications using Antenna instead, but that is unsupported as of this time.

Before you are able to build, you will have to set up your environment following the Getting Started guide.

Once your build environment is configured (as is tested by being able to run the Demo project), you can create your project's build files. In general, projects should have two build files, a build.xml Ant build file, and a build.properties Ant properties file that will be used to address properties for device specific builds.

Templates of these two files can be found in the demo project. It is generally encouraged to use the Demo project's build files as a template, given that J2ME Polish build files are fairly complicated. Assuming that those will be used, they should be copied to the new project, and the following changes can be made to use them to build it.

For the build.xml file

  • In the <sources> element, configure only the <source dir="..."> elements necessary to include the projects that were deemed necessary in the first step of this guide.
  • In the "RunJ2MEUnitTests" target, remove any tests that refer to projects not included in the last step.

For the build.properties file

  • Set the app.class to the fully qualified name of your MIDlet's class.
  • Set the other app.* properties appropriately for your application.
  • Configure the application-specific javarosa properties (javarosa.dev.shortcuts, etc) to valid values for your application.

After these changes, running the build.xml file should execute your MIDlet, which at this point should not start an application, but should produce a System.out call if the MIDlet code in this tutorial was used.

Creating a new shell to control the workflow of your application

Now that your new application is running, it is time to create a shell to control flow between the different modules available. Again, the code in the javarosa.demo project is a good jumping off point for creating a new shell, but here are some basic tips on implementing the appropriate methods:

  • run() - The run method is the main entry point for the shell. Calling run should hand off control of the application's workflow to that shell. In general, this method is where code should be placed which instantiates the shell and connects various resources that the application will need, such as Services and Data Connections.

  • returnFromActivity(IActivity activity, String returnCode, Hashtable returnArgs) - The returnFromActivity is invoked by Activities which wish to return control of the application's workflow to the shell. This method should always either launch a new Activity, or exit the current shell. Depending on the activity's return code, the current activity might be either terminated or suspended, to be resumed later. The WorkflowStack class in the core namespace should help with stacking activities, and some general return codes are available in the core namespace's Constants class.
  • exitShell() - Exits the current shell by either terminating the application, or by handing control over to another shell.
  • setDisplay(IActivity callingActivity, Displayable display) - The setDisplay method is a way of managing access to the current application's display, such that only the currently executing Activity is able to set the display on the device. This method does not strictly need to enforce this specific functionality, but if used properly should prevent anything that does not have control of the current workflow from commandeering the display. At the very least, it should set the display to be the one passed in in every case when it would be valid to do so.
  • setMIDlet(MIDlet midlet) - Sets the shell's current MIDlet. Useful for storing a reference to the executing assembly so that the Shell is able to terminate the application.

Integrating Module Code

Many JavaRosa modules contain elements such as Services or API's that need to be initialized manually. For instance, the XForms parser requires any custom tags to be registered before it is able to gracefully handle them when creating FormDef's. The final step before the application is fully formed is to find and handle all of these registrations.

One extremely common step is the need to register the available RMS Providers. Another is registering new XForms data types and Chatterbox Widgets.

The run method of the Shell is a reasonable place to put much of this logic. The shell's constructor is another good place for such code.

As always, the Demo project contains good examples of registering various elements necessary for the Application to function.

Conclusion

Creating a new JavaRosa application is largely accomplished by identifying the necessary components, finding the appropriate manner in which to connect them, and then defining a workflow for allowing users to move between each component. Adding new functionality to a new application is detrimental, because it isolates code in a location in which it is difficult and undesirable for other developers to re-use.

The bulk of an Application's code should be located in the aptly named 'Shell', defining only the outline for how the application should behave, without implementing any of the functionality internally.