Jrules

JRules is a “Business Rule Management System” from ILOG. That means it is an environment for managing and deploying business rules. JRules supports the development of rule-based applications and allows business users to contribute their knowledge through rule management. For example, business policy can be separated from application logic and therefore updated independently, without the need to look deep into the application logic (Catalystsoft.com).

URL: http://www-01.ibm.com/software/integration/business-rule-management/jrules/

A Java Conference: JSF2 run in the cloud

Here is the Java conference about makes JSF2 run in the cloud, Google Appengine, held in Las Vegas and opened registration.

http://javasymposium.techtarget.com/html/cloud.html?Offer=JSemcl120710&asrc=EM_UTC_12985279&uid=9127465#ABoschJSF

Many talked JSF2 run in the cloud, very few really doing this. I have realized it in my final year project.

Although to deploy JSF2 in Google Appengine faced some problems, such as lack support of threading function, however it gives many benefits to me. First is the easier and comfort deployment of my JSF2 application into Google Appengine. Where it is free to use at the first time. It is easier to integrate with Google Appengine Datastore to make a complete application. Secondly, I found that I am able to find the source code of the JSF2 Jar implementation, I can change some source code based on my usage, and replace it in my Java package structure. I am not sure normal server setup is it able to change source code as what I do like this, because last time I had tested and it was fail.

This is my first-touched experience when doing JSF2, and deployed JSF2 in the Google Appengine. I have faced many problems that Google Appengine don’t support the original concepts of JSF2, but it was solved.

I will not attend the conference, but hoped that I can find this speaker notes after the conference.

Objectify App Engine Persistence Layer

Just found Objectify on http://code.google.com/p/objectify-appengine/

The Google App Engine/J low-level datastore API is simple and elegant, neatly reducing your data operations to four simple methods: get, put, delete, and query. However, it is not designed to be used by the average developer:

DatastoreService persists GAE-specific Entity objects rather than normal POJO classes.
DatastoreService Keys are untyped and error-prone.
DatastoreService has a machine-friendly but not human-friendly query interface.
DatastoreService has an unnecessarily complicated transaction API.
DatastoreService is rather poorly documented. You get to figure it all out from the Javadocs.

We don’t want Java controlled by Oracle

Oracle sue Google because suspected Google using Java in its Google Android Operating System Framework. Oracle said that Java is an operating system framework language, and Google Android publishes its framework had infringed its seven patents. Is Java an operating system framework?

As Java Developer, I want to say something. Java is an open source product, should be maintained by open source community, and free to use by any company, any people, but not fully controlled by one central company, such as Oracle.

This Oracle’s decision to take over Java from Sun, to have fully control of the Java, will be a wrong step for Oracle, or the future of Java. If not Google, IBM, these companies using Java, Java may be die soon.

Oracle, please listen what Java developers thought, freeing Java, please, otherwise Java will die soon. Please don’t play fire yourself, which will burn your hand.

Simple Java BBCode Implementation

I have made a simple Java BBCode Implementation, and it is open source and free to use. Feel free to take it into your project.

Below is the Java static method to convert the bbcode-based text into HTML form to be displayed in browser.

public static String bbcode(String text) {

String temp = nl2br(text);

Map<string , String> bbMap = new HashMap</string><string , String>();</string>

bbMap.put(“\\[b\\](.+?)\\[/b\\]”, “<strong>$1</strong>”);

bbMap.put(“\\[i\\](.+?)\\[/i\\]”, “<span style=’font-style:italic;’>$1</span>”);

bbMap.put(“\\[u\\](.+?)\\[/u\\]”, “<span style=’text-decoration:underline;’>$1</span>”);

bbMap.put(“\\[h1\\](.+?)\\[/h1\\]”, “<h1>$1</h1>”);

bbMap.put(“\\[h2\\](.+?)\\[/h2\\]”, “<h2>$1</h2>”);

bbMap.put(“\\[h3\\](.+?)\\[/h3\\]”, “<h3>$1</h3>”);

bbMap.put(“\\[h4\\](.+?)\\[/h4\\]”, “<h4>$1</h4>”);

bbMap.put(“\\[h5\\](.+?)\\[/h5\\]”, “<h5>$1</h5>”);

bbMap.put(“\\[h6\\](.+?)\\[/h6\\]”, “<h6>$1</h6>”);

bbMap.put(“\\[quote\\](.+?)\\[/quote\\]”, “<blockquote>$1</blockquote>”);

bbMap.put(“\\[p\\](.+?)\\[/p\\]”, “<p>$1</p>”);

bbMap.put(“\\[p=(.+?),(.+?)\\](.+?)\\[/p\\]”, “<p style=’text-indent:$1px;line-height:$2%;’>$3</p>”);

bbMap.put(“\\[center\\](.+?)\\[/center\\]”, “<div align=’center’>$1”);

bbMap.put(“\\[align=(.+?)\\](.+?)\\[/align\\]”, “<div align=’$1′>$2”);

bbMap.put(“\\[color=(.+?)\\](.+?)\\[/color\\]”, “<span style=’color:$1;’>$2</span>”);

bbMap.put(“\\[size=(.+?)\\](.+?)\\[/size\\]”, “<span style=’font-size:$1;’>$2</span>”);

bbMap.put(“\\[img\\](.+?)\\[/img\\]”, “<img src=’$1′ />”);

bbMap.put(“\\[img=(.+?),(.+?)\\](.+?)\\[/img\\]”, “<img width=’$1′ height=’$2′ src=’$3′ />”);

bbMap.put(“\\[email\\](.+?)\\[/email\\]”, “<a href=’mailto:$1′>$1</a>”);

bbMap.put(“\\[email=(.+?)\\](.+?)\\[/email\\]”, “<a href=’mailto:$1′>$2</a>”);

bbMap.put(“\\[url\\](.+?)\\[/url\\]”, “<a href=’$1′>$1</a>”);

bbMap.put(“\\[url=(.+?)\\](.+?)\\[/url\\]”, “<a href=’$1′>$2</a>”);

bbMap.put(“\\[video\\](.+?)\\[/video\\]”, “<video src=’$1′ />”);

for (Map.Entry entry: bbMap.entrySet()) {

temp = temp.replaceAll(entry.getKey().toString(), entry.getValue().toString());

}

return temp;

}


6 June 2011
Refer to stackoverflow, there is one topic that is related to Java BBCode I discovered just now.

JSF 2 f:ajax why execute onevent three times?

Recently when I doing <f:ajax> inside JSF 2.0, I specify onevent javascript callback function, when f:ajax execute to the server and return to the client, it will call the javascript callback function. But I discovered that it will call the function three times. Afterwards when I checked the Javascript Console Log in Google, I discovered onevent will have three different status when it called the function.

The status are: begin, complete, success.

Therefore, the problem has known. We just have to define an onevent callback function as below.

function onevent(e) {
    if(e.status == ‘success’) {
        // TODO: your business
    }
}

Hello World to Direct Web Remoting

This is a simple article that how to hello world to DWR (Direct Web Remoting).

First, go http://directwebremoting.org/dwr/download.html download dwr.jar, for me I download 2.0.6 version in order it can be supported in Google Appengine. Put dwr.jar in WEB-INF/lib

Second, download commons-logging-1.1.1.jar, put this jar in WEB-INF/lib also.

Third, modify web.xml to add this content of

    <servlet>
  <servlet-name>dwr-invoker</servlet-name>
  <display-name>DWR Servlet</display-name>
  <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
  <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
  </init-param>
</servlet>

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

Fourth, create dwr.xml and with the content of

<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
    "
http://getahead.org/dwr/dwr20.dtd">

<dwr>
  <allow>
    <create creator="new" javascript="DWRStuff">
      <param name="class" value="com.fyhao.secs.DWRStuff"/>
    </create>
  </allow>
</dwr>

Then you have to create DWRStuff.java file with the content of

package com.fyhao.secs;

public class DWRStuff {

    public String shownum(int a) {
        return "" + (a * 3);
    }
}

Create a JSP file, called ajax.jsp with the content of

<%@ page contentType="text/html; charset=utf-8" language="java" import="java.sql.*" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script src=’dwr/interface/DWRStuff.js’></script>
<script src=’dwr/engine.js’></script>
<script type="text/javascript">
function update() {
    DWRStuff.shownum(5, function(data) {
                                 alert(data);
                                 });
}
</script>
</head>

<body>
<div id="num"></div>
<script>update();
</script>
</body>
</html>

Remember the DWRStuff.js shown in the script source there.

Now, you can try to see if it is success.

Appengine JPA persist data problem

There is sometime coding JPA persist data problem may occured.

For example, I have a code that is about:

public class MemberBean {

    public void registerMember(Members member) {
        EntityManager em = EMF.get().createEntityManager();
        try {
            em.persist(member);
            //em.refresh(member);
        } finally {
            em.close();
        }
    }

As you can see that, if you run this code, sometimes you will got:

Problem accessing /fyhaosecs. Reason:

    Illegal argument
Caused by:
javax.persistence.PersistenceException: Illegal argument
	at org.datanucleus.jpa.NucleusJPAHelper.getJPAExceptionForJDOException(NucleusJPAHelper.java:214)
	at org.datanucleus.jpa.EntityManagerImpl.close(EntityManagerImpl.java:157)
	at org.datanucleus.store.appengine.jpa.DatastoreEntityManager.close(DatastoreEntityManager.java:54)
	at com.fyhao.secs.MemberBean.registerMember(MemberBean.java:18)
	at com.fyhao.secs.FyhaosecsServlet.doGet(FyhaosecsServlet.java:24)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)

Solution:

The solution is: just add em.refresh(member); after you persist.

You can also refer to this link: http://www.datanucleus.org/products/accessplatform_1_1/jpa/em.html

Java Version of Discuz! authcode()

Discuz! is a forum software developed by Comsenz. authcode is a function that able to encrypt the string into human unreadable form and also can decrypt it. The beauty inside is how many times you make encryption, the encrypted string is not same at all, but when you make decryption it is able to change back to original string. Comsenz had made great contribution to the PHP world by authcode function, whereas nowadays many of the PHP developers are using authcode function in their project, even me.

Now, I would like to find some way using Java to manipulate authcode function. However my skill is not talentable from getting authcode function works. Therefore, I searched online and found that the post rain.xie posted on Javaeye Forum. I changed a little bit from there to be hosted on Google Appengine along with my project. Notice that the implementation of Base64 Decoder I am using Google Appengine Library API.

Here is the code:

AuthCode.java

package com.fyhao.gift.helper;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Random;
import com.google.appengine.repackaged.com.google.common.util.Base64;

public class AuthCode {

    public enum DiscuzAuthcodeMode {
        Encode, Decode
    };

    public static String CutString(String str, int startIndex, int length) {
        if (startIndex >= 0) {
            if (length < 0) {
                length = length * -1;
                if (startIndex – length < 0) {
                    length = startIndex;
                    startIndex = 0;
                } else {
                    startIndex = startIndex – length;
                }
            }

            if (startIndex > str.length()) {
                return "";
            }

        } else {
            if (length < 0) {
                return "";
            } else {
                if (length + startIndex > 0) {
                    length = length + startIndex;
                    startIndex = 0;
                } else {
                    return "";
                }
            }
        }

        if (str.length() – startIndex < length) {

            length = str.length() – startIndex;
        }

        return str.substring(startIndex, startIndex + length);
    }

    public static String CutString(String str, int startIndex) {
        return CutString(str, startIndex, str.length());
    }

    public static String MD5(String pass) {
        byte[] defaultBytes = pass.getBytes();
        try{
             MessageDigest algorithm = MessageDigest.getInstance("MD5");
             algorithm.reset();
             algorithm.update(defaultBytes);
             byte messageDigest[] = algorithm.digest();

             StringBuffer hexString = new StringBuffer();
             for (int i=0;i<messageDigest.length;i++) {
            String hex = Integer.toHexString(0xFF & messageDigest[i]);
            if(hex.length()==1)
            hexString.append(‘0’);

            hexString.append(hex);
             }
             return hexString.toString();
        }
        catch(NoSuchAlgorithmException nsae){
        }

        return "";
    }

    public static boolean StrIsNullOrEmpty(String str) {
        //#if NET1
        if (str == null || str.trim().equals("")) {
            return true;
        }

        return false;
    }

    static private byte[] GetKey(byte[] pass, int kLen) {
        byte[] mBox = new byte[kLen];

        for (int i = 0; i < kLen; i++) {
            mBox[i] = (byte) i;
        }

        int j = 0;
        for (int i = 0; i < kLen; i++) {

            j = (j + (int) ((mBox[i] + 256) % 256) + pass[i % pass.length])
                    % kLen;

            byte temp = mBox[i];
            mBox[i] = mBox[j];
            mBox[j] = temp;
        }

        return mBox;
    }

    public static String RandomString(int lens) {
        char[] CharArray = { ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘j’, ‘k’,
                ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’,
                ‘x’, ‘y’, ‘z’, ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ };
        int clens = CharArray.length;
        String sCode = "";
        Random random = new Random();
        for (int i = 0; i < lens; i++) {
            sCode += CharArray[Math.abs(random.nextInt(clens))];
        }
        return sCode;
    }

    public static String authcodeEncode(String source, String key, int expiry) {
        return authcode(source, key, DiscuzAuthcodeMode.Encode, expiry);

    }

    public static String authcodeEncode(String source, String key) {
        return authcode(source, key, DiscuzAuthcodeMode.Encode, 0);

    }

    public static String authcodeDecode(String source, String key) {
        return authcode(source, key, DiscuzAuthcodeMode.Decode, 0);

    }

    private static String authcode(String source, String key,
            DiscuzAuthcodeMode operation, int expiry) {
        try {
            if (source == null || key == null) {
                return "";
            }

            int ckey_length = 4;
            String keya, keyb, keyc, cryptkey, result;

            key = MD5(key);

            keya = MD5(CutString(key, 0, 16));

            keyb = MD5(CutString(key, 16, 16));

            keyc = ckey_length > 0 ? (operation == DiscuzAuthcodeMode.Decode ? CutString(
                    source, 0, ckey_length)
                    : RandomString(ckey_length))
                    : "";

            cryptkey = keya + MD5(keya + keyc);

            if (operation == DiscuzAuthcodeMode.Decode) {
                byte[] temp;
                temp = Base64.decode(CutString(source, ckey_length));
                result = new String(RC4(temp, cryptkey));
                if (CutString(result, 10, 16).equals(CutString(MD5(CutString(result, 26) + keyb), 0, 16))) {
                    return CutString(result, 26);
                } else {
                    temp = Base64.decode(CutString(source+"=", ckey_length));
                    result = new String(RC4(temp, cryptkey));
                    if (CutString(result, 10, 16).equals(CutString(MD5(CutString(result, 26) + keyb), 0, 16))) {
                        return CutString(result, 26);
                    } else {
                        temp = Base64.decode(CutString(source+"==", ckey_length));
                        result = new String(RC4(temp, cryptkey));
                        if (CutString(result, 10, 16).equals(CutString(MD5(CutString(result, 26) + keyb), 0, 16))) {
                            return CutString(result, 26);
                        }else{
                            return "2";
                        }
                    }
                }
            } else {
                source = "0000000000" + CutString(MD5(source + keyb), 0, 16)
                        + source;

                byte[] temp = RC4(source.getBytes("GBK"), cryptkey);

                return keyc + Base64.encode(temp);

            }
        } catch (Exception e) {
            return "";
        }

    }

    private static byte[] RC4(byte[] input, String pass) {
        if (input == null || pass == null)
            return null;

        byte[] output = new byte[input.length];
        byte[] mBox = GetKey(pass.getBytes(), 256);

        int i = 0;
        int j = 0;

        for (int offset = 0; offset < input.length; offset++) {
            i = (i + 1) % mBox.length;
            j = (j + (int) ((mBox[i] + 256) % 256)) % mBox.length;

            byte temp = mBox[i];
            mBox[i] = mBox[j];
            mBox[j] = temp;
            byte a = input[offset];

            byte b = mBox[(toInt(mBox[i]) + toInt(mBox[j])) % mBox.length];

            output[offset] = (byte) ((int) a ^ (int) toInt(b));
        }

        return output;
    }

    public static int toInt(byte b) {
        return (int) ((b + 256) % 256);
    }

    public long getUnixTimestamp() {
        Calendar cal = Calendar.getInstance();
        return cal.getTimeInMillis() / 1000;
    }

}