Best Practices
In this section you'll find a listing of "Best Practices" from RIFE developers. The information listed below should help new and old developers design and implement their RIFE application in the most efficent and stable manner possible. Readers might also find the tips and tricks section useful.
Site Design
Sessions
Manually storing temporary application state in sessions is disfavored in RIFE web applications (see why). A standard RIFE application indicates what the values are that should be passed around and RIFE does what is needed by adding the required information to the query string or forms. This doesn't mean that the state itself has to be stored in there. The state storage is configurable and it's easy to store data in a session if you need to hide sensitive data, have large objects, etc. (click here).
If you do need to access the HttpSession, then you still can do so, but you'll have to explicitly tell RIFE that you want access to the RAW servlet API:
setProhibitRawAccess(false);
HttpSession session = getHttpServletRequest().getSession();
The reason why this is shielded, is that it's possible to break RIFE's web engine features when the HttpServletRequest and HttpServletResponse are directly accessed. Additionally, people might continue to rely on their existing habits with regards to working with parameters and URLs if this is immediately available. The additional step at least indicates that RIFE has other mechanisms that might be better suited.
Configuration
Annotations
If you are using Java 5, and you don't plan to make frequent changes to the structure of your site, you might find annotations easier to maintain than the XML configuration. All the element-level configuration you can do in the XML configuration can also be done using annotations, with the additional benefit that in many cases you can use references to your element implementations' java.lang.Class objects rather than using element IDs. This is nice because you get IDE support: autocompletion, auto-updating of references if you refactor, etc.
Here is a simple example of an embedded element that refers to two other elements, one in a subsite. The site XML can look like this:
<?xml version="1.0" encoding="UTF-8">
<!DOCTYPE site SYSTEM "/dtd/site.dtd">
<site>
<element id="ProductListEntry" implementation="com.foo.ProductListEntry" />
<element id="ShowProduct" implementation="com.foo.ShowProduct" url="show" />
<element id="AuthAdmin" extends="rife/authenticated/database.xml">
... template_name, etc. -- see the user's manual ...
</element>
<subsite id="Admin" file="admin.xml" urlprefix="admin" />
</site>
We declare the URL in the XML configuration because it is generally a piece of information that's not used by the actual element implementations, and so that the XML configuration can be quickly examined to look up which element handles a given URL.
The embedded element looks like this (not a complete implementation):
package com.foo;
@Elem(
url = ""
)
public class ProductListEntry extends Element {
@FlowlinkExitField(destClass = EditProduct.class,
destClassIdPrefix = "^Admin",
datalinks = {
@Datalink(srcOutput="productId", destInput="productId")
})
public static final String EXIT_EDIT_PRODUCT = "editProduct";
@FlowlinkExitField(destClass = ShowProduct.class,
datalinks = {
@Datalink(srcOutput="productId", destInput="productId")
})
public static final String EXIT_SHOW_PRODUCT = "showProduct";
@OutputProperty
public int getProductId() {
if (_product != null)
return _product.getId();
return 0;
}
public void processElement() {
...
exit(EXIT_SHOW_PRODUCT);
}
Templates
Authentication
Database
Adding Constraints
Make sure when you add a constraint that it exactly matches the field name of the Bean. Example:
Simple.java
package org.rifers.beans;
public class Simple{
private String someValue = 0;
public Simple(){}
...
}
SimpleMetaData.java
package org.rifers.beans;
public class SimpleMetaData extends MetaData {
public void activateMetaData() {
addConstraint(new ConstrainedProperty("someValue").identifier(true).unique(true));
}
}
Schema Creation
Most RIFE developers let RIFE handle their schema generation through the use of an InstallParticipant, MetaData merging and a series of Manager classes. The InstallParticipant creates the DB schema on the first run of the RIFE project, the MetaData classes add constraints to the POJO Beans, like unique(), identifier(), maxLength(), etc. and the Manager classes build your DAO layer. Most project will have a MetaData and Manager class associated with each Bean.
Simple.java
package org.rifers.beans;
public class Simple{
private int id = -1;
private String value = 0;
public Simple(){}
...
}
SimpleMetaData.java
package org.rifers.beans;
public class SimpleMetaData extends MetaData {
public void activateMetaData() {
addConstraint(new ConstrainedProperty("id").identifier(true).unique(true));
addConstraint(new ConstrainedProperty("value").notNull(true).maxLength(256));
}
}
SimpleManager.java
package org.rifers.beans.dao;
public class SimpleManager {
private static final Class BEAN_CLASS = Simple.class;
public void install(Datasource datasource)
{
GenericQueryManager manager =
GenericQueryManagerFactory.getInstance(datasource, BEAN_CLASS);
manager.install();
}
}
InstallParticipant.java
package org.rifers.participants;
public class InstallParticipant extends BlockingParticipant {
protected void initialize() {
Datasource datasource = Datasources.getRepInstance()
.getDatasource(Config.getRepInstance().getString("datasource_name"));
try{
SimpleManager simpleBeanMananger = new SimpleManager();
simpleBeanManager.install(datasource);
}catch(Throwable e){
Logger.getLogger(ProjectSettingsHelper.getProjectName())
.warning("The database structure couldn't be installed, it probably already exists.");
Logger.getLogger(ProjectSettingsHelper.getProjectName())
.warning(ExceptionUtils.getExceptionStackTraceMessages(e));
}
}
}
Config-Base.xml
<config>
<param name="datasource_name">postgresql</param>
<param name="database_name">DB_NAME_HERE</param>
<param name="database_user">USER_NAME_HERE</param>
<param name="database_password">PASSWORD_NAME_HERE</param>
</config>
Participants.xml
Add this line:
<participant>org.rifers.participants.InstallParticipant</participant>
When you run your RIFE setup the InstallParticipant gets called, then creates a Datasource connection, passes that off to the SimpleBeanManager and tells it to install the Simple Bean to your database. The MetaData class is "merged" with your Simple Bean to instruct the Generic Query Manager (GQM) on which constraints to add to the database. Finally, the GQM uses reflection on the Simple Bean and determines which fields to create inside the DB and which constraints to add. The code above would generate a table "simple" in the database, with all of the proper constraints including an auto-incrementing.
Content Management System (CMS)