vendredi 12 juillet 2013

Java : Why you should not use getClass().getClassLoader()


ClassLoader is a useful class when dealing with dynamic resource loading.

Common use is to load at runtime some configuration files or others kind of ressource : .properties, .xml, .png, ....

It gives you a convenient access to the classpath of your application.

The classpath ? Is there only one classpath ?

Not so fast !

Classloading


Classloading is one of the most unknown aspect of Java features but it's one of the most important and most magic feature.

Indeed this process is able  to load your code dynamically even if this code is somewhere on the network.

Classloading stands on ClassLoader and there are lot of things done behind the curtain when you start a Java program.

Run a Java application, anything you want, but just add the  -verbose:class option to the Java command line.


Here is a sample output :


[Opened /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Cloneable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassLoader from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.System from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Throwable from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Error from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ThreadDeath from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Exception from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.RuntimeException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.AccessControlContext from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ReflectiveOperationException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassNotFoundException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.LinkageError from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.NoClassDefFoundError from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassCastException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ArrayStoreException from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.VirtualMachineError from /Library/Java/JavaVirtualMachines/jdk1.7.0_10.jdk/Contents/Home/jre/lib/rt.jar]
...


ClassLoader


It's very important to understand there are many ClassLoader used in a Java Application.

Each ClassLoader is responsible for loading a piece of the overall class path.

At runtime, ClassLoader are linked to each other : this is known as the class loader hierarchy.

Application server use their own hierarchy which can be quite complex.

The key point is : the class loader responsible for loading a class will associated to this class for life, even if this class object is used in a context with another class loader.


getClass().getClassLoader() returns always the same ClassLoader object.


Here is a sample code.

As you can see the parent class loader of my main class can not find a resource which is in my application class path.


Thread context


Each thread has a context class loader.

When  you need to get some resource use the context thread class loader because it has access to the most complete class path.

Indeed, if you use the class loader of any class object, you'll get the class loader which loads this class the very first time.

This class loader may access to your resource but you can not know for sure.

So : Thread.currentThread().getContextClassLoader() is the key !





  










lundi 8 juillet 2013

JQuery perfomance tip : visible and hidden selector



As client side components becomes smarter, Javascript performance becomes a real concern which can turn user experience to a dream or a nightmare.

JQuery is a mandatory tool you need for some use cases .

One of this use case is  about stripping table rows with even and odd colors.

You can do it using CSS 3 selector :

tr:nth-child(even) {background: #CCC}
tr:nth-child(odd) {background: #FFF} 

But sometimes you need to deal with more complex tables.

Imagine you have a table of data : some TRs are not data directly but some extra informations about data, let's say a detail view of data you want to hide and show when user click on some place on the data row.

So you will  get an HTML stream like this :


<tr data-isdata="true">
     <td>drtykRnZWhIFYkbTwuNkrBuZvBtJEwVDvLFC</td>
     <td>GGRpOQXnEUIUrLztepXPgkNQukEHOKNYoXmO</td>
     <td>fQzhJfjzVmkxGxQAKYVLbdjsLriosPWcNNhq</td>
     <td>pCeIsdthjvnfPtEVfuswGPZpCMbAneejMPbf</td>
     <td>iMzqEzOpecwHwNkoewhynidzUliwMhGEidOe</td>
     <td>XWteGfqPeAXHiNEGxTurkWuMagYeGXosiGdX</td>
</tr>
<tr style="display:none;">
     <td col="7">Some extra informations to hide or show.</td>
</tr>


Now, you are stucked with CSS 3 selectors because you can not select  odd or even nodes with metadata isdata :

tr[data-isdata]:nth-child(even) {background: #CCC}


Does not work since nth-child apply to the element tr and not to the whole tr[data-isdata].

You can actually check this with new HTML 5 API for querying the DOM :

document.querySelectorAll("tr[data-isdata^='true']:nth-child(odd)")

This returns all the nodes carrying data-isdata attribute and not the odd ones.

Plus, you need to strip only visible rows !

CSS 3 don't provide a convenient way to select the nodes we need here.

Visible


Now, you need JQuery : it's very easy to select the nodes we need but you can do it using different ways.

One of the obvious way is to use visible selector :

$("tbody > tr:visible:odd").addClass("odd");
$("tbody > tr:visible:even").addClass("even");

Quite easy isn't it ?

Yes, but quite slow too !

In certain cases, this selector can literally crush the browser, i've seen the browser for 30 seconds with some complex tables with 500 rows !

The visible selector is slow because it does not exist in CSS, it's computed using many other properties :


return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
                        (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");


This piece of code is the core of hidden method called by the visible one. The visible method apply a not operator after calling hidden method.

Metadata


The use of a dedicated metadata for visibility is much more faster than the visible or hidden selectors.


<tr data-isdata="true" data-isvisible="true">
     <td>drtykRnZWhIFYkbTwuNkrBuZvBtJEwVDvLFC</td>
     <td>GGRpOQXnEUIUrLztepXPgkNQukEHOKNYoXmO</td>
     <td>fQzhJfjzVmkxGxQAKYVLbdjsLriosPWcNNhq</td>
     <td>pCeIsdthjvnfPtEVfuswGPZpCMbAneejMPbf</td>
     <td>iMzqEzOpecwHwNkoewhynidzUliwMhGEidOe</td>
     <td>XWteGfqPeAXHiNEGxTurkWuMagYeGXosiGdX</td>
</tr>
<tr style="display:none;">
     <td col="7">Some extra informations to hide or show.</td>
</tr>

I've built a sample sample with the two ways : the metadata way is 4 times faster than the visible way.

Check the sample on Gist.

Here are the selectors :

var tbody = $("tbody");
tbody.find("tr[data-visible]:odd").addClass("odd");
tbody.find("tr[data-visible]:even").addClass("even");

With a profiler (use Google Chrome "Profile" for instance) you can see that the method strip2 based on metadata selector is many times faster than the visible one.

The main pitfall of the metadata approach is to put extra load on server side and network bandwidth, the metadata is a piece of data you have to generate and carry through the network.

As usual performance tuning is a matter of balance !







mercredi 3 juillet 2013

Mod_JK : session mixed between users



I worked a few weeks ago on a particularly vicious bug.

Some users of our JEE web applications experimented a change of identity when they were working quietly.

Better, they were in the session of another user, set in a context that was not theirs.

Our architecture is based on Apache, Mod_JK and Glassfish.

A track


After googling a few minutes I found a serious lead: a critical bug in Mod_JK still open, entitled Response mixed betweens users.

The bug is opened in 1.2.28 as 1.2.19 but we must never forget that the discovery of a bug in a version that does not mean the bug did not exist before.

Even if the form does not mention the bug Glassfish, I assume this is a Mod_JK  bug.

So I decided to set up a test environment to try to reproduce the bug at will.

For this, some system commands later, I find myself with a proper environment.

Session mixing: how is this possible?

How to verify that occurs ?

If the bug is rare  then there is little chance that manual testing can reproduce the problem.

We must first understand how it happens : when a user receives the response to a query it was not intended to !

Indeed, the response header Set-Cookie is returned for each response sent by the Glassfish server. Note that Tomcat does not work the same way by issuing the response header Set-Cookie only once, when creating the session.

Glassfish and is more vulnerable to this Mod_JK  bug as all responses from the server contain this header field when the JVM is configured with a JVM route.

If Mod_JK mixes responses then users will receive a session cookie that is not for them.

How to check this?

After reading the comments on the bug, I've realized that this bug was kind of stealth or very stealthy.

The probability to reproduce this bug by hand,  playing with multiple browsers is almost zero.

JMeter to the rescue


JMeter is in my opinion a first-rate tool, both simple and powerful for those who know to exploit it (it's quite convenient for small load because of its thread based model).

It will allow me to generate a significant number of user sessions on a single JEE web and check for each query if the associated response contains the same Set-Cookie header.

We need a BeanShell assertion to check the crucial point: is there sharing session?

Here is the script (this is not a Groovy file but a BSH one, Gist does not know BSH).

You can find the JMeter script and BSH script here in my github repo.

This script should be used for each query in a BeanShell assertion with JMeter 2.9.

You can either reference a file that contains the code or paste the code in the box provided in the statement.

With this script I could reproduce the problem : check JMeter log file to see the mixing.

The rate is about 0.8 mixing for 1000 requests.

This is low, but for a web application that knows a sustained traffic, it is enough to be a major security risk.

Is this really a bug Mod_JK ?

Using Mod_proxy removed the behavior.

This does not come from the application. This observation is consistent with some evidence related to the bug.

Lessons


This expertise has been a confirmation of felt about the evolution of our architecture for me.

Several people were mobilized for a month to try to isolate and reproduce the problem, without success.

Architectures are more and more complex: it is almost impossible to solve problems without method or extreme rigor.

However, some professionals continue to understand the technical issues such as "before" no methodology is well established, no reliable configuration management of the architecture components, no trace when trying to copy, modify several parameters of architecture without properly control the scope of these changes, ...

A few days ago Michael Shallop published an article on the 5 best rules for debugging and diagnostic.

I recommend this article.




Contrat Creative Commons
the jee architect cookbook by Olivier SCHMITT est mis à disposition selon les termes de la licence Creative Commons Paternité - Pas d'Utilisation Commerciale - Pas de Modification 3.0 Unported.

HTML 5 Offline : IndexedDB Cleaner



I've built a small but useful Chrome extension to purge IndexedDB.


So, here it is : IndexedDB Cleaner

This extension can be used in combination with Local Storage Cleaner and Appcache Cleaner, found on same page.

Enjoy !






Contrat Creative Commons
the jee architect cookbook by Olivier SCHMITT est mis à disposition selon les termes de la licence Creative Commons Paternité - Pas d'Utilisation Commerciale - Pas de Modification 3.0 Unported.