<$BlogRSDURL$>
  Monday, March 20, 2006

Drools on AppFuse

Recently got a chance to adopt rule engine into project to allow abstraction of business rules from the service layer, that way, business rules can be maintained in it's own space. I pick Drools as the underlying rule engine because it is intuitive, simple, and robust JSR94 compliant rule engine, plus it's open source. Below are steps to integrate this powerful rule engine into AppFuse. I use the Spring Modules distribution to make it easier for the integration since all the plumbing is done nicely with Spring.

1. Add required libraries:
    a. springmodules-jsr94-0.2.jar
    b. /jsr94
    c. /drools
    d. /janino

Refer to How to add a Library into AppFuse for information on how to add new library into AppFuse.

2. Drop the Spring ApplicationContext for Drools configuration, applicationContext-rules.xml, into [appfuse]\web\WEB-INF\:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="ruleServiceProvider"
class="org.springmodules.jsr94.factory.DefaultRuleServiceProviderFactoryBean">
<property name="provider">http://drools.org/</value></property>
<property name="providerClass"><value>org.drools.jsr94.rules.RuleServiceProviderImpl</value>
</property>
</bean>
<bean id="ruleRuntime"
class="org.springmodules.jsr94.factory.RuleRuntimeFactoryBean">
<property name="serviceProvider"><ref local="ruleServiceProvider"/></property>
</bean>
<bean id="ruleAdministrator"
class="org.springmodules.jsr94.factory.RuleAdministratorFactoryBean">
<ref local="ruleServiceProvider"/></property>
</bean>
<bean id="ruleSource" class="org.springmodules.jsr94.rulesource.DefaultRuleSource">
<property name="ruleRuntime"><ref local="ruleRuntime"/></property>
<property name="ruleAdministrator"><ref local="ruleAdministrator"/></property>
<property name="source"><value>/WEB-INF/authorizedUsers.drl</value></property>
<property name="bindUri"><value>authorizedUsers</value></property>
</bean>
<bean id="rulesService" class="org.appfuse.service.impl.RulesServiceDrools">
<property name="ruleSource"><ref local="ruleSource"/></property>
</bean>
</beans>

The file, authorizedUsers.drl, is the ruleset, and RulesServiceDrools is the Drools rule implementation we have in this sample setup.

3. The signature for the ruleset, authorizedUsers.drl, in [appfuse]\web\WEB-INF\:

<?xml version="1.0" encoding="UTF-8"?>
<rule-set
name="Get authorized users"
description="Rules to retrieve authorized users"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://drools.org/rules rules.xsd
http://drools.org/semantics/java java.xsd">
<rule name="Enabled users">
<parameter identifier="user">
<class>org.appfuse.model.User</class>
</parameter>
<java:condition> user.isEnabled() != true </java:condition>
<java:consequence> drools.retractObject(user); </java:consequence>
</rule>
<rule name="Valid credentials">
<parameter identifier="user">
<class>org.appfuse.model.User</class>
</parameter>
<java:condition>
user.isCredentialsExpired() == true || user.getRoles().size() == 0
</java:condition>
<java:consequence>
drools.retractObject(user);
</java:consequence>
</rule>
</rule-set>

Two rules are defined in the ruleset: Enabled users and Valid credentials. Both rules check for condition, if met, the user is removed from list. Basically, the two rules mean:

if (!user.isEnabled() && (user.isCredentialsExpired()
|| user.getRoles().size() == 0))
userList.remove(user);

4. The interface for the rules service, RulesService, is simple enough:

public interface RulesService {
public List getAuthorizedUsers(List users);
}

And the associated implementation, RulesServiceDrools:

public class RulesServiceDrools extends Jsr94RuleSupport implements RulesService {
public final static String ACTIVE_USERS_URI="authorizedUsers";
public List getAuthorizedUsers(List users) {
return executeStateless(ACTIVE_USERS_URI, users);
}
}

I couldn't get the Spring Jsr94Template to work properly. Ideally, the output list from Drools should be access via Spring template:

public List getAuthorizedUsers(List users) {
List outputList = getTemplate().executeStateless(ACTIVE_USERS_URI,null,
new StatelessRuleSessionCallback() {
public Object execute(StatelessRuleSession session)
throws InvalidRuleSessionException, RemoteException {
return session.executeRules(users);
}
}
);
return outputList;
}


5. The Tapestry action code to get the list of users:

public abstract class AuthorizedUsers extends BasePage implements PageRenderListener {
public abstract UserManager getUserManager();
public abstract void setUserManager(UserManager manager);
public abstract RulesService getRulesService();
public abstract void setRulesService(RulesService svr);

public void pageBeginRender(PageEvent event) {
List authorizedUsers = getRulesService().getAuthorizedUsers(getUserManager().getUsers(null));
...
}
}


That's pretty much it to setup Drools in AppFuse and starts ruling away.
 
  Friday, January 06, 2006

Braille, braille

What is this?
braille

The color combo looks familiar isn't it? Yap, you guess it, it's the color combo for Google logo, and it's the current logo on Google US site, head over there and take a look. (Well, guess you can only see it on Google site next year on 2007/1/4.)

Not sure what that means really, I know Google always have seasonal logo's but this one got me. Try clicking on the logo and see what happens. It actually brings you to Google search result page with "louis braille birthdate" as key word. Louis braille who? Following the link identifies Louis Braille was the man who invented braille, the six raised-dot system that "has been adapted to almost every major national language and is the primary system of written communication for visually impaired persons around the world."

Not sure what that means? Try this:
1. Go back to the Google logo above. Get it? No? Ok, try the next one.
2. Here is Louis Braille name in braille.

Get it now? Good. The Google logo above is the name Google in braille.

Now, how about this?
braille_lvk

That's the name of yours truly in braille. How about finding your name in the braille alphabet? Read more on braille.

Yeh, you might think that I'm either crazy or too bored. I could very well be both any time.
 
  Tuesday, October 18, 2005

Preferred Locale on AppFuse

The solution provided here are based on locale issues on AppFuse JIRA, specifically, APF-141 which is further refined in APF-142.

Here are the steps to change the request-based locale to user-preferred locale, i.e., implement a session-based locale on AppFuse:
1. Add session-scope PREFERRED_LOCALE and optional request scope DEFAULT_LOCALE constants.
2. Use LoginServlet to set locale session. The parameter, locale, is passed from loginForm.jsp, a selected locale from a dropdown for example.

public final class LoginServlet extends HttpServlet {
...
public void execute(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
...
request.getSession().setAttribute(Constants.PREFERRED_LOCALE, request.getParameter("locale"));
...
}
}

3. Persist preferredLocale for user in ActionFilter:

public class ActionFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain)
throws IOException, ServletException {
...
if ((username != null) && (user == null)) {
...
String preferredLocale = session.getAttribute(Constants.PREFERRED_LOCALE).toString();
if (preferredLocale != null) {
if (preferredLocale "") {
if (user.getPreferredLocale() null) {
user.setPreferredLocale(Constants.DEFAULT_LOCALE);
}
} else {
user.setPreferredLocale(preferredLocale);
}
}
}
session.setAttribute(Constants.PREFERRED_LOCALE, user.getPreferredLocale());
}
}

4. Add locale filter, LocaleFilter:

public class LocaleFilter implements Filter {
private static final transient Log log = LogFactory.getLog(LocaleFilter.class);
public void init(FilterConfig filterConfig) {
}

public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpSession session = servletRequest.getSession(false);

if (log.isDebugEnabled()) {
log.debug(servletRequest.getRequestURL());
}
Locale preferredLocale = null;

if (session!=null){
if (session.getAttribute(Constants.PREFERRED_LOCALE) != null) {
String localeKey = session.getAttribute(Constants.PREFERRED_LOCALE).toString();
if (localeKey != null) {
if (localeKey.equals("")) {
String[] keys = Constants.DEFAULT_LOCALE.split("-");
preferredLocale = new Locale(keys[0],keys[1]);
} else {
String[] keys = localeKey.split("-");
preferredLocale = new Locale(keys[0],keys[1]);
}
}
}
}
if (null != preferredLocale
&& !(request instanceof LocaleResponseWrapper)) {
request = new LocaleResponseWrapper(servletRequest, preferredLocale);
}

chain.doFilter(request, response);
}
}

5. The associated LocaleResponseWrapper is equivalent to LocaleRequestWrapper:

public class LocaleResponseWrapper extends HttpServletRequestWrapper {
private final Locale preferredLocale;
private Enumeration locales;

public LocaleResponseWrapper(HttpServletRequest request, Locale sessionLocale) {
super(request);
preferredLocale = sessionLocale;
}

public Locale getLocale() {
if (null != preferredLocale) {
return preferredLocale;
} else {
return super.getLocale();
}
}
public Enumeration getLocales() {
if (null != preferredLocale) {
return setLocales();
} else {
return super.getLocales();
}
}
private Enumeration setLocales() {
if (null == locales) {
List l = Collections.list(super.getLocales());
l.add(0, preferredLocale);
locales = Collections.enumeration(l);
}
return locales;
}
}

6. Put the locale filter in web.xml or appfuse/metadata/web/filters.xml:

<filter>
<filter-name>localeFilter</filter-name>
<display-name>Locale Filter</display-name>
<filter-class>org.appfuse.webapp.filter.LocaleFilter</filter-class>
</filter>

7. Modify JSP’s: add page-scope locale setting:

<fmt:setLocale value="${preferredLocale}" />


Now AppFuse is powered by preferred locale!
 

告訴你為什麼程式不 work

為什麼程式不 work? 我不是跟你說了嗎?

10. 'That's weird...'
9. 'It's never done that before.'
8. 'It worked yesterday.'
7. 'You must have the wrong version.'
6. 'It works, but it hasn't been tested.'
5. 'Somebody must have changed my code.'
4. 'Did you check for a virus?'
3. 'Where were you when the program blew up?'
2. 'Why do you want to do it that way?'

and finally ...

1. 'I thought I fixed that.'

source , link

慚愧,這些藉口我都用盡了,不曉得有否其他可以借用?
 
  Thursday, October 13, 2005

AJAX on AppFuse

Steps in setting up DWR on AppFuse:

1. Add DWR jar in /lib/dwr-1.0
2. Add to /lib/lib.properties:

#
# DWR - https://dwr.dev.java.net/ (Direct Web Remoting)
#
dwr.version=1.0
dwr.dir=${lib.dir}/dwr-${dwr.version}
dwr.jar=${dwr.dir}/dwr.jar

3. Add to /properties.xml:

<!-- Web -->
<path id="web.compile.classpath">
...
<pathelement location="${dwr.jar}"/>
</path>

4. Add to /build.xml:

<target name="package-web" depends="compile-web,jsp-2" description="Package WAR">
...
<war destfile="${webapp.dist}/${webapp.war}"
webxml="${webapp.target}/WEB-INF/web.xml" compress="true">
...
<lib file="${dwr.jar}"/>
</war>
</target>

5. Add DWR servlet in /metadata/web/servlets.xml:

<!-- dwr servlet -->
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

6. Add DWR servlet mapping in /metadata/web/servlet-mappings.xml:

<!-- dwr mapping -->
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

7. Exclude DWR from sitemesh in /web/WEB-INF/classes/decorators.xml:

<excludes>
<pattern>/dwr/*</pattern>
</excludes>

8. Add DWR configuration, /WEB-INF/dwr.xml:

<dwr>
<allow>
<convert converter="bean" match="com.octasoft.fp.*"/>
<create creator="spring" javascript="userManager">
<param name="beanName" value="userManager"/>
<include method="getUser"/>
<include method="getUsers"/>
</create>
</allow>
</dwr>

Here we use DWR provided Spring creator, and utilizes userManager for remoting. Also, we use default-deny policy for the service.

To browse and test DWR-enabled Spring beans: http://server:port/fpweb/dwr/index.html.

Note:
It might be required to delete the existing web.xml in \build\fpweb\WEB-INF\ in order to successfully add in DWR servlet into web.xml.

Use case
Verifying user by username before authentication on loginForm.jsp; on verification, user’s nickname is shown:

<script src="/appfuse/dwr/engine.js" type="text/javascript"></script>
<script src="/appfuse/dwr/util.js" type="text/javascript"></script>
<script src="/appfuse/dwr/interface/userManager.js" type="text/javascript"></script>
<script>
DWREngine.setErrorHandler(doNothing);
var nickname = "";
function doNothing() {
return false;
}
function getNickname() {
if (nickname == "") {
userManager.getUser(popUser,document.loginForm.j_username.value);
document.loginForm.j_password.focus();
return false;
} else {
return validateForm( document.loginForm, false, true, true, false, 1)
}
}

var popUser = function(user) {
if (user == undefined) {
return false;
} else {
DWRUtil.setValue("nick", "Hello, " + user.nickName);
nickname = user.nickName;
}
}
</script>
<form method="post" name="loginForm" id="loginForm"
action="<c:url value="/authorize"/>" onsubmit="return getNickname();">
<div id="nick"></div>
<input name="j_username" type="text" id="j_username"/>
<input name="j_password" type="password" id="j_password"/>
</form>
 
Software Culture
  03/01/2004 - 04/01/2004
  04/01/2004 - 05/01/2004
  05/01/2004 - 06/01/2004
  06/01/2004 - 07/01/2004
  08/01/2004 - 09/01/2004
  11/01/2004 - 12/01/2004
  07/01/2005 - 08/01/2005
  09/01/2005 - 10/01/2005
  10/01/2005 - 11/01/2005
  01/01/2006 - 02/01/2006
  03/01/2006 - 04/01/2006
ColdFusion
日常毒藥與養料
  Smart talk always right?
  Drools on AppFuse
  Braille, braille
  可愛提示
  The Dynamic SRC of IMG
  Preferred Locale on AppFuse
  告訴你為什麼程式不 work
  絲綢之路 2000:致命病毒
  AJAX on AppFuse
  1918
  Meet Mr. Writely
  網際網路的最後一頁
  小螞蟻最短篇
  健檢, e檢
  Open source ColdFusion
  八月半個
  自行其是
  Rich DHTML client
  cfspring, seriously
  三百萬民主補給站
  敏督利小插曲
  迷上喬治亞
  說故事
  Where are they?
  宿夢
  An Architect's View
  Martin Fowler
  Loud Thinking
  Raible Designs   fullasagoog
Home


Powered by Blogger