Anees has posted 4 posts at DZone. View Full User Profile

Rule Based Swing UI Generation

05.12.2011
| 2408 views |
  • submit to reddit
There was a requirement to generate user interfaces on fly for different use cases. It is also required to allow the end user to change the look and feel and layout
without changing the java code. I was working on prototype which involved apache velocity and jboss drools.

So, what I did, I created the velocity template which acted as a layout generator based on drools syntax. When the Usecase object was passed to the rule engine, the JPanel object was received. Here, I am going to present the simple test case to elaborate this concept.
I am sure it can play a fundamental role in creating and developing more complex scenarios. Rules.vm file is created in the templates folder as follows:

templates/Rules.vm
#package com.eagle.coders.drools.rules

import com.eagle.coders.drools.UseCaseObject;

import java.awt.FlowLayout;
import java.awt.GridLayout;>

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

#list any import classes here.

#declare any global variables here


rule "use case generation"
	no-loop true
	when
		$u : UseCaseObject( )
	then
		
		JLabel uLabel = new JLabel("userName :");
		JTextField uField = new JTextField(10);
		JPanel uPanel = new JPanel();
		uPanel.setLayout(new FlowLayout());
		uPanel.add(uLabel);
		uPanel.add(uField);
		
		JLabel uPassword = new JLabel("password :");
		JPasswordField pField = new JPasswordField(10);
		
		JPanel pPanel = new JPanel();
		pPanel.setLayout(new FlowLayout());
		pPanel.add(uPassword);
		pPanel.add(pField);
		
		JPanel mainPanel = new JPanel();
		mainPanel.add(uPanel);
		mainPanel.add(pPanel);
		retract($u);
		insert(mainPanel);
end

 


The UsecaseObject is a simple POJO
package com.eagle.coders.drools;

/**
 * @author Anees
 *
 */
public class UseCaseObject {

	private String usecase;

	/**
	 * @return the usecase
	 */
	public String getUsecase() {
		return usecase;
	}

	/**
	 * @param usecase the usecase to set
	 */
	public void setUsecase(String usecase) {
		this.usecase = usecase;
	}
}


Now our testing class is as follows
package com.eagle.coders.drools;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collection;

import javax.swing.JFrame;
import javax.swing.JPanel;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.definition.KnowledgePackage;
import org.drools.io.Resource;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;

/**
 * @author Anees
 * 
 */
public class TestingUsecase {

	public static void main(String[] args) {

		final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
				.newKnowledgeBuilder();

		VelocityContext context = new VelocityContext();

		Template template = Velocity.getTemplate("templates/Rules.vm");
		// System.out.println(" THE TEMPLATE IS :: "+template.getName());

		StringWriter w = new StringWriter();
		template.merge(context, w);
		// System.out.println(" RULES :: "+ w.toString());

		Resource stringRes = ResourceFactory
				.newReaderResource(new StringReader(w.toString()));
		
		kbuilder.add(stringRes, ResourceType.DRL);
		
		// Check the builder for errors
		if (kbuilder.hasErrors()) {
			System.out.println(kbuilder.getErrors().toString());
			throw new RuntimeException("Unable to compile \"Rules.vm\".");
		}
		
		// get the compiled packages (which are serializable)
		final Collection pkgs = kbuilder
				.getKnowledgePackages();
		
		// add the packages to a knowledgebase (deploy the knowledge packages).
		final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
		kbase.addKnowledgePackages(pkgs);

		final StatefulKnowledgeSession ksession = kbase
				.newStatefulKnowledgeSession();
		
		UseCaseObject usecaseObject = new UseCaseObject();
		ksession.insert(usecaseObject);

		ksession.fireAllRules();
		
		Collection objects = ksession.getObjects();
		Object[] objArray = objects.toArray();
		JPanel panel =(JPanel) objArray[0];
		
		ksession.dispose();
		
		JFrame frame = new JFrame("Testing Framework");
		 frame.add(panel);
		frame.setVisible(true);
		frame.setSize(300, 100);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}



Even though this is a very simple prototype example but it can open new horizons to generate complex user interfaces.
However, more sophisticated Layout Managers like FormLayout and MigLayout must be used in Velocity Templates.
This concept will definitely help in removing the tight coupling of the user interface generation from the application.
Hopefully, this idea will help the developers to create more loosely coupled frameworks.
Published at DZone with permission of its author, Anees Ur-rehman.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)