Add Custom Java Code

From JRapid

Jump to: navigation, search

Contents

Introduction

JRapid provides both ease of use and incredible flexibility. While the easy-to-use interface automatically provides a large percentage of the code that you will need, there are many cases in which you may need to add custom logic to your application. This can be done easily and is kept separate from the auto-generated code through the use of abstract classes (which contain the automatic code), and the inheriting class into which you can add your specific logic.

NOTE: Projects hosted on Shared Development Servers do not allow the uploading of custom Java code. If this is
your case and you're not sure about upgrading your server yet, you may test your customizations by
running the application in a local development environment.

There are architecture documents providing an overview of how the abstraction organizes your code base. Existing methods can be overwritten or new ones added to the extending class to provide additional functionality. The methods contained in the abstract classes should not be edited. They are automatically generated and changes there will be lost.

The most convenient way to add custom Java code is by using Eclipse and the JRapid Plugin for Eclipse, although you can also edit the source code in JRapid Designer using the Files view. After making your changes in Eclipse simply synchronize to add the changes to your project's remote instance.

Common scenarios for adding custom code

Add instance methods to entities

Editing the <EntityName> class, you may add or override methods.

If you have a Car entity with Make and Model properties you might want to add a custom getter to produce the full name for an item.

    <entity label="Car" menu="Cars" name="Car">
        <property display="secondary" entity="Make" label="Make" name="make"/>
        <property display="primary" label="Model" name="model"/>
    </entity>

    <entity label="Make" menu="Cars" name="Make">
        <property display="primary" label="Name" name="name"/>
    </entity>

In the Car.java file in the entities package

public class Car extends CarAbstract  {
	public String getFullName() {
		return super.getMake().getName() + " " + super.getModel();
	}
}

Default values

Defaultsets already give you several ways to initialize values but sometimes a more complex value must be set. This flexibility is indispensable if, for example, you have to automatically set the expiration date for a country as one year in the future if it is not entered. This can be done very easily in one method and can be extended if say, a change requires that the expiration date be different for US companies than for foreign ones.

public class Company extends CompanyAbstract  {	
	public void setActive(Boolean active) {
		super.setActive(active);
		
		if(active == true) {
			// Create a calendar to set the new expiration
			GregorianCalendar gc = new GregorianCalendar();
			Date d = new Date();
			gc.setTime(d);
			
			// If there is no expiration or it's previous to now add one year to now
			// Otherwise the existing expiration will be used
			if(getExpiration() == null || getExpiration().before(gc)) {
				gc.add(Calendar.YEAR, 1);
				super.setExpiration(gc);
			}
		}
		else {
			super.setExpiration(null);
		}
	}
}

Once you synchronize and regenerate, you will see the following:

Date Example 1

Creating a record and then bringing it up in the list will show you that the expiration date has been created by default for the company:

Date Example 2

Please note that this sets the value in the object even before persistence, so upon clicking on the active check box, the expiration date check box will not be affected on the form.

Customize find and store processes

Services can also be extended for fine-grained control of find() and store() methods accessing your entities. The find and store methods can be found in <EntityName>Services class. As usual, it is important not to edit the abstract class as your changes will be overwritten by the next update.

This can be helpful for:

  • Setting complex initial values to properties as above
  • Validating input data in the server side before storing
  • Asking for confirmation before certain operations
  • Executing custom code for accessing external resources or services, or creating and storing other related object instances

The above example where the Company has its expiration date set based on whether it is active can also be done from the <Entity>Services class.

public class CompanyServices extends CompanyServicesAbstract {
	/*** Extend store(String id, <Entity> voobj in abstract class. */
	public Object store(String id, Company voobj) {
		if(voobj.getActive()) {
			GregorianCalendar gc = new GregorianCalendar();
			Date d = new Date();
			gc.setTime(d);
			
			// If there is no expiration or it's previous to now add one year to now
			// Otherwise the existing expiration will be used
			if(voobj.getExpiration() == null || voobj.getExpiration().before(gc)) {
				gc.add(Calendar.YEAR, 1);
				voobj.setExpiration(gc);
			}
		}
		else {
			voobj.setExpiration(null);
		}
		
		return super.store(id, voobj);
	}	
}

This will result in virtually identical functionality to above and comes down to a matter of taste and where you prefer the logic to go. The only difference between the two approaches is that the <Entity> approach sets the expiration slightly earlier and the service waits until the front end calls a service to persist the object.

Add new methods and expose them through the Controller

The controller exposes many different services. This allows you to leverage the infrastructure you already have to flexibly provide new services.

The default services are in the MainControllerAbstract as shown below for an example providing a service returning to the user all active companies by name and address.

@Override
	protected void init(FrontController controller) {
			// main for Company
			controller.registerXmlRpcService(CompanyServices.class, "removeMany", "Company.remove");
			controller.registerXmlService(GET, "/Company", CompanyServices.class, "findPage", new DefaultMarshaller(), true);
			controller.registerXmlService(GET, "/Company/([0-9,]+)" , CompanyServices.class, "find", new DefaultMarshaller(true), false);
			controller.registerXmlService(POST, "/Company/([0-9,]+)", CompanyServices.class, "store", new DefaultMarshaller(true, "com/libraryproject/xml/CompanyFull.xml"), false);
			...

Place a single line defining your new service in the extending class, MainController:

@Override
	protected void init(FrontController controller) {
		super.init(controller);
		
		controller.registerXmlService(GET, "/Company/state/([0-9,]+)" , CompanyServices.class, "findAccountState", new DefaultMarshaller());
	}

This method then relies on a method in your CompanyServices class to provide the value returned to the user. The following method takes the company parameter and adds up the invoices associated with it.

       public String findAccountState(String id) { 
		Company c = Company.DAO().findById(id);
	
		//Find account state for company
		double total = c.getAccountState();

		return c.getName() + ":" + total;
	}

The new method is exposed at: http://<JRapidServer>/<ProjectName>/xml/<Entity>/<MethodName>/<Parameter>.

If there is no parameter, leave off the last "/". In my example the query string looks like the following:

http://demo.jrapid.com/libraryproject/xml/Company/state/1

This returns:

<String>JRapid:165.25</String>

See also

Personal tools