Want to show your appreciation? Please to my charity.

Wednesday, June 28, 2006

Saturday, June 24, 2006

Submitted a enhancement request to Sun for optional type parameters

The RFE is as below:

Title

[Generics] Support optional type parameters

Introduction

In majority of use cases, the data type retrieved from a Map object is the same type of the data that was added to it. But there are exceptions, for example, the MultiMap and TransformedMap in the wildly adopted Jakarta Commons-Collections. With the current definition of the java.util.Map interface, it is impossible to parameterized the MulitMap while still maintain the backward compatibility with legacy code. Refining the java.util.Map as Map<K,V,I> can resolve the problem but then all existing parameterized subclass of Map must be changed.

Justification

It is a very common situation in the evolution of any application or code library that a new typed parameter needs to be added to an existing interface or class. This task becomes extermely difficult (if not at all impossible) with the limitation of current generics syntax. As we all familiar with adding overloaded methods to extend the functionality of an object, a similar mechanism in Generics is very much yearned. The description section above provided a example and there is a blog explains it in detail: http://kennethxu.blogspot.com/2006/06/is-definition-of-map-interface-overly.html

Expected Behavior

A default type can be assiged to a typed parameter. Examples:
class Foo<K,V=Object> {...} // means Object is used when V is omitted
Foo<String> foo; // equivalent to Foo<string,> foo;
interface Bar<K,V=K> {...} // means type K is used when V is ommited
Bar<String> bar; // equivalent to Bar<String,String> bar;

Test Code

A very simple test case:
public class GenericsEnhancement {
   class Foo<K,V=Object> {} // means Object is used when V is omitted
   class Bar<K,V=K> {} // means type K is used when V is ommited
   public void test() {
     Foo<String> foo1 = new Foo<String,Object>();
     Foo<String,Object> foo2 = new Foo<String>();
     Bar<String> bar1 = new Bar<String,String>();
     Bar<String,String> bar2 = new Bar<String>();
   }
}

Friday, June 23, 2006

Eclipse compiler bug: false ambiguous method error

This is an eclipse bug in the area of supporting JDK 1.5 Generics. The bug was discovered in 3.1.2. Not sure about 3.2 but I couldn't find similar bug report in bugzilla.

To reproduce the bug, use one of the class below. Both of them are compiled fine with javac 1.5.0_06.

Link to bugzilla: https://bugs.eclipse.org/bugs/show_bug.cgi?id=148504

List 1 - A theoretical example.

public class EclipseAmbiguousBug {
   interface IWorker<E> {
     void add(E e);
   }

   interface IPerson<E> {
     void add(E e);
   }

   interface IFather<E> extends IWorker<E>, IPerson<E>{   }

   interface IMother<E> extends IPerson<E> {
     void add(E e);
   }

   interface IChild<E> extends IFather<E>, IMother<E> {   }
   static class Child<E> implements IChild<E> {
     public void add(E e) {}
   }

   public static void main(String[] args) {
     IChild c = new Child();
     c.add(""); // correct warning
     IChild<String> cg = new Child<String>();
     cg.add(""); // false ambiguous error
   }
}

List 2 - A practical example.

import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

public class EclipseAmbiguousBug2 {
   interface Addable<E> {
     boolean add(E e);
   }
   interface XCollection<E> extends Collection<E>, Addable<E> {}
   interface XSet<E> extends XCollection<E>, Set<E> {}

   static class TestXSet<E> extends AbstractSet<E> implements XSet<E> {
     public boolean add(E e) {return false;}
     public Iterator<E> iterator() {return null;}
     public int size() {return 0;}
   }

   public static void main(String[] args) {
     XSet xs = new TestXSet();
     xs.add(""); // correctly generated warning
     XSet<String> xs2 = new TestXSet<String>();
     xs2.add(""); // false ambiguous error message
   }
}

Monday, June 19, 2006

Is the definition of Map interface overly restricted in Java 5

I spent entire yesterday evaluating the possibility of Generifying Commons-Collections and still keeps the backward compatibility. It turned out to be not as simple as I thought initially.

Java 5 type-safe Map and Collection strictly requires the data that you retrieve from it must be of the same type of the data you put into it. This becomes a major problem generifying a number of useful Map and Collection implementations in Commons-Collections. For example, MultiMap, TransformedMap. While my initial evaluation goal yet to be archived, what did come to my attention is a difficult question: is the definition of Map (similarly to Collection) interface overly restricted in Java 5?

Well, in the real life, I put an egg in incubator can get the chick back after sometime. So for what reason the get method must return the same type of data as the put(or add or set) method takes? Let's look at the refined Map definition:

public interface Map<K,V,I=V> {
     // Ok, the I=V is something that I made up here, but isn't it neat?
     // It means if I is not given, default to V, assuming our compiler
     // is smart enough to understand it.
     V get(Object o);
     I put(K key, I input);
}

This pseudo Map interface is less restricted so apparently to be much useful. Let's exam how MultiMap can be supported with the refined interface.

public abstract class AbstractHashMap<K,V,I=V> implements Map<K,V,I> {
     public V get(Object object) {
         return ...;
     }
     protected boolean putInternal(K key, V input) {
         // same logic was in put
     }
}
public class HashMap<K,V> extends AbstractHashMap<K,V> {
     public V get(Object object) {
         return ...;
      }
     public V put(K key, V input) {
         return putInternal(key, input)? input:null;
     }
}
public class MultiMap<K,V,I=V> implements Map<K,Collection<V>,I> {
     public Collection<V> get(Object o) {
         return ...;
     }
     public I put(K key, I input) {
     }
}
public abstract class AbstractMultiHashMap<K,V,I=V>
     extends AbstractHashMap<K, Collection<V>, I>
     implements MultiMap<K, V, I>
{
    protected boolean addInternal(K key, V value) {
         Collection<V> c = get(key);
         if(c==null) {
             c= new ArrayList<V>();
             putInternal(key, c);
         }
         c.add(value);
        return true;
     }
}
public class MultiHashMap<K,V> extends AbstractMultiHashMap<K,V> {
    public V put(K key, V input) {
         return addInternal(key, input)? input:null;
     }
}
Obviously, we need to handle setValue in Map.Entry too. This might make it a little complicated but clearly it's doable. EDIT: RFE is filed to Sun and accepted in it's bug databaes

Friday, May 12, 2006

Working with WebLogic Portal UUP

WebLogic Portal 8.1 allows you to extend it's User Profile implementation with Unified User Profile(UUP. BEA site have documentation (not so clear) for that in section Modifying the Existing ProfileManager Deployment Configuration.

I did this before with support for User profile only and never returns null property value. This time I need to support Group profile as well as the property value can be null. Here are something that is not documented well but you must do to get it to work.

  • In order to get the group profile working, you need to do the same in ejb-jar.xml and weblogic-ejb-jar.xml file for GroupProfileManager in addition to UserProfileManager.
  • If the property can return null, it insist to look for the same property in the default profile manager again. So if you use transaction in your ejb, make sure you either use all XA enabled resources or set the transaction-type to Requires New.

Saturday, April 29, 2006

How to recover WebLogic admin password from boot.properties file

A friend came to me with a kind of locked out WebLogic 8.1 domain. He could still start the server but he didn't know the password for admin console. The fact that he could still start the server was because the username and password are stored in the boot.properties under domain directory. But they are encrypted. I told my friend that the password in the boot.properties must be a reversible as WebLogic itself needs the password to start the server. We searched string "boot.properties" in all the class files in the weblogic.jar file to find the class that does the magic. It's weblogic.security.internal.BootProperties. After 2 hours of try, we end up with code below:

import weblogic.security.internal.BootProperties;

public class RecoverPassword { 

  public static void main(String[] args) { 
    BootProperties.load(null, false);  // tested with 8.1
    // BootProperties.load("fullPathToBootPropertiesFile", false); // tested with 10.3
    BootProperties bootp = BootProperties.getBootProperties(); 

    System.out.println( 
      "#####################[" + 
       bootp.getOneClient() + "/" + bootp.getTwoClient() + 
       "]###################"); 
  }

}

Update 6/20/2009: Since many people asked me about later version of WebLogic. I took a look at the latest version 10.3 as of today. It turned out that BEA changed it a little bit. My original code, which was written for 8.1, throws the NullPointerException. But the fix is also simple, just change the call to BootProperties.load by giving the path to the boot.properties file.

We placed the above RecoverPassword.java file in c:\recover directory and compiled it. Then made a copy of startWebLogic.cmd to C:\recover\recoverPassword.cmd and added a few lines to it, nearly the end of file(first and last are existing lines).

%JAVA_HOME%\bin\java %JAVA_VM% -version
SET CLASSPATH=C:\recover;%CLASSPATH% echo %CLASSPATH%
SET SERVER_CLASS=RecoverPassword
SET doExitFlag=false
if "%WLS_REDIRECT_LOG%"=="" (

Now cd to the domain home and execute c:\recover\recoverPassword, we have the password back :-)

Friday, April 28, 2006

WebLogic 8.1.4 is leaking memory (session object).

Recently we have observed memory leakage in our production box. It turned out that it was WebLogic 8.1.4 that is leaking the session object. Some long expired sessions didn't get invalidated.

The story of how we would have suspected this in the first place would be too long to discuss it here. But here I'll present the code that we used to prove the WebLogic session leakage. The concept is simple, we used a session listener to track all the session with the help of the WeakReference. And a jsp page to display all the sessions.

The session listener first, add code below to WEB-INF/src/webpkg/SessionExplorerListener.java.

package webpkg;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionEvent;

public class SessionExplorerListener implements HttpSessionListener {
   private static final Map sessionMap = new HashMap();

  public void sessionCreated(HttpSessionEvent event) {
    HttpSession session = event.getSession();
    put(session.getId(), new WeakReference(session));
  }

  public void sessionDestroyed(HttpSessionEvent event) {
    remove(event.getSession().getId());
  }   

  public static synchronized Set getIds() {
    return new HashSet(sessionMap.keySet());
  }   

  public static synchronized HttpSession get(String id) {
    WeakReference ref = (WeakReference)sessionMap.get(id);
    return ref==null ? null : (HttpSession) ref.get();
  }

  private static synchronized void remove(String id) {
    sessionMap.remove(id);
  }   

  private static synchronized void put(String id, WeakReference ref) {
    sessionMap.put(id, ref);
  } 
}

Then let's add our listener to web.xml:

<listener>
   <listener-class>webpkg.SessionExplorerListener</listener-class>
</listener>

Finally, a jsp page that list all the active sessions, listSessions.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8"
     pageEncoding="ISO-8859-1"%>
<%@ page import="java.util.*, webpkg.SessionExplorerListener" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Session List</title>
  </head>
  <body>
    <% Set sessionIdSet = SessionExplorerListener.getIds(); %>
    Total Sessions: <%=sessionIdSet.size()%>
    <table border="1">
       <tr>
        <th>Session ID</th>
         <th>Create Time</th>
         <th>Last Access</th>
        <th>Timeout in (s)</th>
       </tr>
      <%
        Iterator itr = sessionIdSet.iterator();
        while(itr.hasNext()) {
          String sessionId = (String)itr.next();
          HttpSession ses = SessionExplorerListener.get(sessionId);
      %>
      <tr>
         <td>
         <% if (sessionId.equals(session.getId())) { %>
         <b><%=sessionId%></b>
         <% } else { %>
         <%=sessionId%>
         <% } %>
         </td>
         <td><%=new Date(ses.getCreationTime())%></td>
         <td><%=new Date(ses.getLastAccessedTime()) %></td>
         <td>
          <%=ses.getMaxInactiveInterval()-(System.currentTimeMillis()
             -ses.getLastAccessedTime())/1000 %>
          /
          <%=ses.getMaxInactiveInterval()%>
         </td>
      </tr>
      <%
        }
      %>
</body>
</html>

This session leaking must have been there for long, we only found this out recently because we have released a new application that puts too much in user's session. Our memory usage is kept growing with those big sessions staying there forever. The problem is rather intermittent. Most of sessions do get cleaned up. We are still unclear of what caused the WebLogic to fail.

Wednesday, April 05, 2006

A Java based TCP/IP port forwarding utility

I was looking for a port forwarding utility that I can use on a Solaris box yesterday. By searching the Internet, I found the portfwd which is a C++ utility. I downloaded to find out it didn't like my 4.x GCC. Figuring it might cost me more time and effort to rebuild an old version of GCC (without root permission), I decide to write a Java version myself so I can reuse it in other OS platforms in future.

I'm done and it is working very well. It listens on a given port and forward all connection to a destination. The port number and destination are specified in command line. I believe this utility is very useful to setup port forwarding in ad hoc basis. I have it in my USB flash drive so I can start forwarding in anywhere that Java goes.

I have file up for download at Google Code. If you do download and find it is useful, please leave a comment here so that I know somebody is using it. To start forwarding, unzip the downloaded zip file to get two jar files and do:

C:\>java -classpath commons-logging.jar;portforward.jar org.enterprisepower.net.portforward.Forwarder 8080 search.yahoo.com:80
Sep 18, 2008 1:11:14 AM org.enterprisepower.net.portforward.Listener <init>
INFO: Ready to accept client connection on 0.0.0.0:8080

Now, launch your browser and point to http://localhost:8080/ you should get the yahoo search page.

Update (9/8/2013): Source code is now at https://github.com/kennethxu/portforward, happy cloning.

Update (4/19/2009): Since I got a lot of request asking for the source code, I decided to have the source available in Google Code. Feel free to get it from the Subversion repository.

Sunday, March 12, 2006

Managing Multiple Projects with Subversion

Planning to add a number of projects to my newly installed subversion. Found links below to be very useful:

Wednesday, March 08, 2006

Subversion with ApacheSSL on Win32

After getting Apache2 + SSL running sucessfully, I started to install subversion.

Install Subversion

Download 1.3.0 installer (the setup.exe version) from http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 and run the executable. The installer not only installed Subversion but also modified the Apache http.conf file by adding two modules and restarted the Apache server.

Create Subversion Repository

Launch a command prompt and run this: svnadmin create c:\svnrepo

Import Source into repository

svn import "C:\path\to\project" file:///svnrepo -m "initial import"

Link apache to repository

Add location directive to the https virtual host in the ssl.conf file.

<location>
  DAV svn
  SVNPath "C:/svnrepo"
</location>

Restart the Apache server and launch browser to https://www.my-site.com/svn/

Enforce security

Go to Apache's conf directory and run command below to create two user account. Note: the first command has -c option to create the password file.

..\bin\htpasswd -cm svn-auth-file user1
..\bin\htpasswd -m svn-auth-file user2

Edit the ssl.conf file to change the section we just added as below.

<location>
  DAV svn
  SVNPath "C:/svnrepo"
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile "conf/svn-auth-file"
  Require valid-user
</location>

Final Restart

Restart the Apache server and launch the browser. We did it!

Apache2 + SSL + Win32

I have been following the instructions posted here by Raible to get Apache2SSL running on Win32. Well, there were something extra that I had to do to get it working finally.
  1. Download precompiled Apache2 binary with SSL module from http://hunter.campbus.com/ and extract them to
    C:\Program Files\Apache2
    .
    You can place it anywhere in your drive.
  2. Find two ddl files ssleay32.dll and libeay32.dll in the installation directory and copy them to
    %SYSTEMROOT%/system32.
  3. Followed the instruction to create the test cert. I have cygwin so it was simple the only difference is that I didn't need to use -config option.
    • openssl req -new -out server.csr
    • openssl rsa -in privkey.pem -out server.key
    • openssl x509 -in server.csr -out server.crt -req -signkey server.key -days 3650
  4. Create a subdir called ssl.crt under conf and place the genereated file server.crt and server.key there.
  5. edit httpd.conf file (The instruction on Raible's site didn't work for me on this part):
    • Uncommented this line LoadModule ssl_module modules/mod_ssl.so
    • Replaced all c:/apache with the installation directory. (Note that I use forward slash)
    • Removed line <IfModule mod_ssl.c> and it's closing tag to force include ssl.conf.
  6. edit ssl.conf
    • Removed line <IfDefine SSL> and it's closing tag to force SSL
    • Replaced all c:/apache.
    • Changed ServerName to your site name. Changed ServerAdmin too but should have no impact.
Start Apache and test, it works now! My next task is to get Subverion working with ApacheSSL on Win32...