JBossAS + JBossWS + JDK 6 = problems
Years ago, a lot of applications did not support JDK 5, even with the incredible range of new features. Today, the history repeats itself with JDK 6. A lot of applications do not support it. For my surprise, I added a big one to the list: JBoss.
How did I found it? By publishing a webservice using JBossWS (included in JBossAS). Deployment works, but when I consume the WS, I got this exception:
java.lang.UnsupportedOperationException: setProperty must be overridden by all subclasses of SOAPMessage
After researching it on Google, I found this bug report. They tell JBoss is not supported on JDK 6, but, since I follow the "upgrade-or-bust" philosofy, I decided to put it to work.
It was pretty simple (on JBoss 4.2.2GA): grab a copy of "jboss-saaj.jar" from "server/default/lib" and copy it to "JBOSS_HOME/lib/endorsed". Works like a charm!
JAAS + Struts + Tomcat = Secure and pratical
Still working on the application I describe in my previous post, I started to integrate it to use JAAS. It is a Struts-based system running in a Tomcat server. The login was an Struts Action, and a RequestProcessor took care of redirecting to login page.
It was pretty easy the conversion to JAAS:
- I developed a new Realm for Tomcat. It was just a matter of extend the RealmBase class and override the authenticate method. This step could be skipped if my employer's LDAP server accepted anonymous search - JNDIRealm can do this job;
- With the Realm ready, add it to the application's context.xml (NetBeans users will find it inside META-INF folder of the project);
- Next step was to make a "public access" version of the application. Every code that does the authentication and authorization was removed, except the dynamic menu;
- The dynamic menu was changed to use the request.isUserInRole method. For some reason, this property can't be accessed by EL. I expected something like "request.userInRole['role']", but it didn't work;
- The logged user can be retrieved with either request.getUserPrincipal() or the EL "pageContext.request.userPrincipal";
- Now, it is just a matter of adding every Struts action to security constraints in web.xml. In NetBeans, it's an easy-to-use visual editor. Here is a snippet:
<security-constraint> <web-resource-collection> <web-resource-name>SecurePages</web-resource-name> <description>My secret pages</description> <url-pattern>/secure.do</url-pattern> <http-method>GET</http-method> </web-resource-collection> <auth-constraint> <description>Allowed roles</description> <role-name>power_user</role-name> </auth-constraint> </security-constraint>
- The login page, if using FORM-based authentication, is now something like:
<form action="j_security_check" method="post"> User: <input type="text" name="j_username"/> Password: <input type="password" name="j_password"/> <input type="submit" name="btLogin" value="login"/> </form>
- To activate the FORM-based login, this must be added to web.xml:
<login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.jsp</form-login-page> <form-error-page>/login.jsp</form-error-page> </form-login-config> </login-config>
- Did you notice the login error page is the same as the login page? To check if the user entered an invalid password, I use:
<c:if test="${param.btLogin == 'login' and param.j_username <> ''}"> <c:out value="Invalid username and/or password"/> </c:if>
It is a long boilerplate, but steps are the same for every application. Using JAAS instead of programatic authorization makes your system more secure and easier to maintain.
DisplayTag inside a loop
In my (brand) new job, I've developed a simple report that is a list of employers along with phone calls they made. The employer list is iterated using JSTL's "c:forEach" and the call list is rendered by DisplayTag.
Very simple, except because I got a little problem: DisplayTag requires an unique id (set by the "uid" attribute) and this uid is also the iteration variable - just like the "var" attribute in "c:forEach".
This means I need to create an unique id for every outer iteration. But this also means that I create a new "current inner item" variable for each outer item. If I use a non-unique id, the navigation of all tables will be the same: if I click "next page" on the first table, the second table will also go to the next page. If I define an uid based on outer item data, I cannot access it using a direct approach (like "c:out"), because the EL must change for every iteration.
This limitation was solved by predefining the uid (before the DisplayTag table):
<c:forEach items="${myList}" var="${myItem}"> <%-- This creates an unique id based on item's id --%> <c:set var="tableId" value="list-${myItem.id}"/> <display:table uid="${tableId}" name="${myItem.mySubList}"> <display:column property="mySimpleValue"/> <display:column> <%-- Now, a little EL magick --%> <my:tag value="${pageScope[tableId].myValue}"/> </display:column> </display:table> </c:forEach>
Notice that this approach isn't needed if you want to show simple values, like a String. Using this to pass the value to a
How-(not-)to use Hibernate
Digging into some legacy code, I found the worst way to initialize Hibernate. The first step should be something like:
Configuration cfg = new Configuration(); cfg.addClass(Bean1.class) .addClass(Bean2.class) .addClass(Bean3.class);
But I found this:
Configuration cfg = new Configuration(); Object[] objs = new Object[] { new Bean1(), new Bean2(), new Bean3() }; for (int i = 0; i < objs.length; i++) cfg.addClass(objs[i].getClass()); }
You only need to know the basics of Java to find out the waste of memory and CPU, instantiating a lot of classes (and an array) only to get the class instantiated. And, of course, the unnecessary loop to iterate over the array. It's like baking a cake to find out the brand of the flour.
As if it's not enough, this piece of code is replicated in about 7 spots. Yes, the right way to configure Hibernate is using the "HibernateUtils" pattern, as in documentation.
And, as if it's not enough yet, this piece of code creates a SessionFactory that is sent via constructor to an entire hierarchy of classes. Yes, it is the worst solution, since the Session creation could be on HibernateUtils (as in documentation, again).
HTTP POST download with Java’s URL class
This is easy, but I always forget the recipe. I need to download and process a file using a POST request. The code is:
URL url = new URL(FORM_URL); URLConnection conn = url.openConnection(); conn.setDoOutput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); out.write("field=value&field=value&..."); out.flush(); InputStreamReader isr = new InputStreamReader(conn.getInputStream()); BufferedReader in = new BufferedReader(isr); String line; while ((line = in.readLine()) != null) { // do something } out.close(); in.close();
The only catch is to use URLEncoder.encode to translate keys and values to the x-form-url-encoded style.
Transactions in EJB3
A little pause in the Quest for the Java Portal. I'm creating a JavaEE application, and I got a simple problem, but not well explained on the Internet. The scenario:
- EJB module has:
- Some CMP beans (with @ManyToOne and @OneToMany relationships);
- A Session Facade (stateless local bean).
- WEB module has:
- A jMaki-powered menu tree;
- A JSF-managed bean (that populates the jMaki tree with a call to Session Facade).
It is very simple, but I got a ServletException (caused by a LazyInitializationException) when I try to navigate the relationships on web container.
Googleing this problem, I found that the transaction of a stateless bean (using a "requires" or "requires new" attribute) is closed after the method returns, if the container opened it. Specifically, it happens when the EntityManager is destroyed. This is obvious, but not very intuitive. Solutions:
- Use a stateful bean. The Entity Manager will "survive" along with the session, but I think this will make the transaction survive that long, too. Not the best solution;
- Open the transaction in web container. This is strange, but logical: if I want to access the database (with lazy fetching), I must have an active transaction. EJB container is prepared to reuse the transaction by using the @TransactionAttribute annotation (defaults to "REQUIRED"). Using the brand-new resource injection, it is a matter of creating a "@Resource UserTransaction tx" attribute on my JSF managed bean. It is not nice to inject a transaction on every bean of web container, so, you only need to create a web filter that opens before chaining the operation.
As a conclusion, my contribution to JavaEE community. If you want to use Lazy Loading on web containers, you just need to create a Filter like this one:
public class TransactionFilter implements Filter { @Resource private UserTransaction tx; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { if (tx.getStatus() == STATUS_NO_TRANSACTION) { tx.begin(); } } catch (NotSupportedException ex) { throw new ServletException(ex); } catch (SystemException ex) { throw new ServletException(ex); } chain.doFilter(request, response); } public void destroy() { } public void init(FilterConfig filterConfig) { } }
The quest for the Java Portal – Profiling Liferay
It's time to run the Profiler. I start JBoss with "run-nbprofiler.bat" (created by NetBeans) and ask NB to attach profiler. I'm using "analyze performance", filtering Java core classes. JBoss runs very slow, but this is normal, since profiler is collecting execution of every method.
My first try showed that Lucene is called a lot of times, even when server is on "idle" state. Maybe it is a background job that should have a smaller frequency.
The second try filters out Lucene classes (using NB Profiler options). I found two bootlenecks: com.liferay.portal.lucene.IndexWriterFactory.decrement (takes 58% of startup time) and JBoss classloaders. And I found that Liferay auto deploy is running too often (~5 seconds of delay). Nice for develoment, awful for production use.
I had to remove JBoss classes from profiling, and I got amazed on things I discovered. I got a OutOfMemoryError (perm gen space) - solved by adding "-XX:MaxPermSize=256m" to run script - but I could find a lot of interesting things:
- getResourceAsStream was called 12068 times;
- Xerces' ChildNode was instantiated 853808 times (does anybody imagine why Java is "slow"?);
- Stacktrace got really deep. About 50 levels or more, not including JBoss, Lucene and JDK classes;
- A chain of 7 filters was called before hitting Liferay's MainServlet. If you consider a little forward made after MainServlet, we reach 14 filters before hitting a JSP file;
- JSP compilation took 19s on this environment - total execution was 180s;
- LR's VelocityTaglib has 8 iconXXX() methods that took 8 seconds each - detail: each one forwards another request;
- Everytime a "include" is made, the chain of 7 filter is called. And there's a LOT of includes.
Remember that this was only ONE hit and the request wasn't complete, because of the OOM error. After that, I'm going to have some fun in the "real world". I'll try to go deeper tomorrow. Maybe I can send a RFE to Liferay team after I organize the arguments.
The quest for the Java Portal – Running Liferay
Now, I have a profiled environment. But, when I tried to run JBoss (without profiling), I got an error in counter-ejb module. Its classpath is not correctand, so, I added this line to my build.[user|computer].properties:
classpath.manifest=[original line in build.properties]
lib/doubleclick-filter.jar
I reported this bug on Liferay JIRA [#LEP-2406]. After another 16 minutes of compilation, I forgot to initialize the database. I had two options: use a diferent connection pool or initialize it before deploying Liferay. I prefer the second, so, the easiest way was to create a MBean that depends on LiferayPool. Hypersonic is smart enough to allow multiple SQL commands in one Statement. I will upload the code later, but it is a matter of create a MBean that reads the script provided by Liferay and runs it on the poll.
This leads to an strange error about Spring transactions with EJB, Hibernate, JBoss and all. I don't remember the details, but the solution is to change the debug attribute of CachedConnectionManager. This is something I can't do in my shared JBoss server, so, I used an dirty trick:
- Start JBoss with no Liferay, but with the original data source (liferay-db.xml configured);
- JBoss translates your -ds file into and -service file and logs it with debug level;
- Grab the translated file in log, create the -service, delete the -ds and indent the file - this will help you understand its structure;
- Now, the funny part: copy CacheConnectionManager definition from jboss-jca.xml, paste into liferay-service.xml (inside the "CachedConnectionManager" optional attribute), and rename the MBean to an unique name - I put a ",name=xxx" suffix.
This will create a custom CCM to Liferay, without violating the original instance. That's what I love in Java (specially in JBoss): you can create a Lego-like software that is just a matter of do the bindings.
Liferay runs fine, but I found two bugs: a ClassNotFoundException about ical4j, and the contents of Guest community are blank. The first, I solve by adding lib/ical4j.jar to manifest classpath (as above). The second I don't care, since I will clean everything when I deploy the real application.
The quest for the Java Portal – Compiling Liferay
I got some weird exceptions with JBoss Portal, so, I decided to play around with Liferay. I have it running on my shared JBoss, and I have some ideas to their structured articles feature, so I will try harder on Liferay.
I've decided to check the bootlenecks on Liferay, because it is too slow. I'll use NetBeans Profiler. Using NB's ability to create projects using existing Ant scripts, I've done some setup:
- Create an Java Application project using an already existing Ant script;
- Add all "src" folder to the source folders list - about 20 of them (I guess this isn't necessary unless we want to change anything);
- Adjust compilation build to the "start" target and run to "deploy" (Liferay does not set "start" as dependency to "deploy");
- Create a "build.[user|computer].properties" and "app.server.[user|computer].properties" to customize some build parameters. I dislike Jikes, so, I'm using "javac.compiler=modern". The rest of properties are straighforward to customize, but folders on app.server must be correctly configured - JBoss predefined values does not work on an out-of-box installation;
- After building a lot of modules (about 10 minutes on an almost empty Windows box powered by an Athlon XP 3200+), more than 2000 classes - yes, two thousands - are compiled without errors. Running the "deploy" target will install Liferay on JBoss. It installs some JARs on server's lib folder - I will change this later, before uploading to the real server.
Now, you can run JBoss. But first, some one-time configuration:
- Add a datasource. To use profiling, I created a memory-only HSQLDB:
<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>jdbc/LiferayPool</jndi-name> <connection-url>jdbc:hsqldb:.</connection-url> <driver-class>org.hsqldb.jdbcDriver</driver-class> <user-name>sa</user-name> <password></password> </local-tx-datasource> </datasources>
- Use NetBeans Profiler (Profile | Attach Profiler) to prepare a special run script to active profiling on JBoss - this file will be called "run-nbprofiler".
Now, we have an working environment. Next step is test and profile.
The quest for the Java Portal – Testimonial
I was supposed to be an user of Java Portals, not a developer. Things like JBoss management portlet are annoying, but it is just a matter of user experience. At least JBoss Portal deploys with minimal changes. Actually, I only need to change deployment descriptors.
Portals like Gridsphere, Jetspeed and others are unusable to normal users, since they need to know Java very well. How can I replace Zope/Plone in my job if every environment admin will need to know how to use Maven/Ant/etc?
Is that hard to develop an JavaEE application that makes users' life easier?
If I need to do any developer task, I prefer to develop my own portal that conforms to JSR-168 without violating JavaEE specifications.
BTW, I'm talking as a Java Portal user (i.e. portal deployer/admin). I'm not talking as the user of the portal I will create with a Java Portal (i.e. my "costumers").
End note: I'll try JBoss Portal beta-1, since it was released this week, but I won't use it if I still needs that buggy management layout.