Pages

Showing posts with label springframework. Show all posts
Showing posts with label springframework. Show all posts

Wednesday, January 23, 2013

Integration of Drools and spring to perform business logic validation in JSF

In a previous post I explained how to use AOP to perform custom validation in JSF. Here I will show how you can use the business logic framework Drools to perform validation based upon external (business) rules in conjunction with springframework.

In this example I will perform the bussiness rules validation inside an advice class, but you can implement the same mechanism to perform that validation inside a JSF backing bean or in a spring bean.

Inside your application-context.xml file add your advice in the following way:

<bean id="validatorAspect" 
   class="com.foo.aspects.BackingBeanValidatorAdvice" 
   factory-method="aspectOf">
<property name="resources">
<list>
<value>/com/foo/business/rules.drl</value>
</list>
</property>
</bean>

I'm assuming you have basic knowledge of Drool, I will not get into syntax details here, there is a lot of documentation in Drool's site; instead I will just present a simple validation rule file rules.drl:


package com.foo.business

import com.foo.model.ModelBean;
import com.foo.aspects.Error;

query "Error" ()
    error : Error()
end


rule "Rule 1"
when
   ModelBean( id == 32 && internalId not matches "(J|P|R) ([0-9]){6}")
then
   Error err = new Error();
   err.setSummary("Error in Model Bean. ");
   err.setDetail("Internal Id not matches format: (J, P o R) followed by whitespace and 6 digits");
   insert(err);
end

Error is just a simple Pojo containing two properties (summary and detail).

Then, you must fire the validation inside the advice class:
public aspect BackingBeanValidatorAdvice {

    private KnowledgeBase kbase;

    private StatefulKnowledgeSession ksession;
    
    private String[] resources;

    pointcut doSaveChanges() : execution(* com.foo.beans.BackingBean.saveChanges(..));

    void around() : doSaveChanges() {
        FacesContext context = FacesContext.getCurrentInstance();
        ModelBean mbean = context.getApplication().evaluateExpressionGet(context, "#{bBean.selected}", ModelBean.class); 

        this.init();
        this.getKsession().insert(mbean);
        this.getKsession().fireAllRules();

        QueryResults results = this.getKsession().getQueryResults("Error");
        if (results.size() > 0) {
            for (QueryResultsRow row : results) {
                Error err = (Error) row.get("error");
                context.addMessage("error", new FacesMessage(FacesMessage.SEVERITY_ERROR, err
                        .getSummary(), err.getDetail()));
            }
        }
        proceed();
    }

    public void init() {
        if (this.getKbase() == null) {
            KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
            for (int i = 0; i < this.getResources().length; i++) {
                String rule = this.getResources()[i];
                kbuilder.add(ResourceFactory.newInputStreamResource(this.getClass().getResourceAsStream(rule)),
                    ResourceType.DRL);
            }
    
            if (kbuilder.hasErrors()) {
                System.out.println(kbuilder.getErrors());
                return;
            }
            Collection < KnowledgePackage > kpkgs = kbuilder.getKnowledgePackages();
            KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
            kbase.addKnowledgePackages(kpkgs);
            this.setKbase(kbase);
        }
        this.setKsession(this.getKbase().newStatefulKnowledgeSession());       
    }
    
    public StatefulKnowledgeSession getKsession() {
        return ksession;
    }

    public void setKsession(StatefulKnowledgeSession ksessionToSet) {
        this.ksession = ksessionToSet;
    }

    public String[] getResources() {
        return resources;
    }

    public void setResources(String[] resourcesToSet) {
        this.resources = resourcesToSet;
    }

    public KnowledgeBase getKbase() {
        return kbase;
    }

    public void setKbase(KnowledgeBase kbase) {
        this.kbase = kbase;
    }    
}


That's it. In case the validation fails, a FacesMessage is added to the FacesContext and JSF is responsible for handling and showing the error in a jsf page, eg: <h:messages .../>