Quantcast
Viewing latest article 4
Browse Latest Browse All 4

Howto: JBoss Seam Performance Problems and Optimizations

Image may be NSFW.
Clik here to view.
Are you a java web application developer and you are working with JBoss Seam? Ok, then you might be interested in the following JBoss Seam performance problems (and their solutions). If not you should seriously consider leaving this post! It is rather long and dry and might make you never consider programing with Seam EVER (which can also be a good thing, but I will leave this discussion for another post). In any case: You have been warned.

Soo… I have recently gained some experience developing a medium/large web application with Seam and I also discovered a couple of pitfalls you run into when using this framework, especially in terms of performance optimization. At some point, I had pages of just average complexity, loading in more than 17 seconds! In this post, I will go over each of the steps that i did to speed up those pages to 3 – 6 seconds. During my code analysis, I searched through a couple of Seam performance tuning pages out there, but for some reason they did not really provide me the tweaks or straight solutions I was hoping to find. Many of the tips I found sounded very reasonable, but when I applied them to my project, there was barely any change in Seam’s performance! That’s why I publish my own list of performance optimizations, which definitely had a visible impact at least on my code and hope I can help those among you, who find themselves in a similar position like me. And even if my performance tuning tips are not able to help in your particular case, at least you will learn how to identify performance problems in your Seam project with this post.

First off, I give you the configuration of our target system, where the application was supposed to be running on (if you are using a slightly different version of Seam or JBoss should not really matter):

  • Seam 2.1.2 (+ Richfaces, JPA/Hibernate)
  • JBoss 4.2.3
  • Java 1.5 (32bit)
  • SQL Server 2008
  • IDE: Eclipse Indigo

Before we start, I would like to make clear, that even though I would like to teach anyone who is about to start development with Seam about the pitfalls to avoid right from the beginning, I do not have the time to go into all the details on what is going on when explaining the tweaks. I am no Seam guru myself. In fact, many things Seam does feel like magic (and this might also be a reason why it was sometimes that slow), but I assume that you have at least an understanding of its principal concepts like contexts, bijection, components (for example Home), permissions etc.

As always, you are very welcome to post your questions, explain your own tweaks and point out flaws in my findings in the comment section below.

So let’s dive right into things here.

How to run Performance Tests

Up front, I suggest you install TPTP (Test & Performance Tools Platform) to actually trace which methods are called how often and how long each call takes. At least that’s what I did. It is not available for Eclipse Indigo, so you should get Eclipse Helios with TPTP for your test runs. You can download the All-in-one solution for Windows (32bit) from here. You might need to install JBoss Tools for Helios too. The easiest way would be using the Eclipse update functionality (Start Eclipse, then select Help -> Install New Software) and use this nightly build url.

Profiling out of the box will most likely bring your system to its knees, because the profiler will observer all the packages in the VM and the Seam Framework is quite a complex beast. So we need to set a filter before to make it focus only on our own classes instead.

  • Go to the server Tab
  • Click the Start in Profiling Mode icon
  • A popup appears. Select the Execution Time Analysis and double-click on Java Profiling
  • Add an Exclude Filter Rule on * and make an Include Filter on the packages of your project (for example de.auralfloat.*)
  • Start the profiling and switch to Eclipse Profiling Logging Perspective
  • Open the Execution Statistics View and start analyzing (you might need to refresh this page once in a while)

This way, I found after one or two page requests already several methods that by itself were not taking that much time to compute, but have been called like 19.000 times. So it is either those long running methods or those who get called a lot of times you want to focus on. I will get back on how I fixed some of them later.

One thing that is not visible on the profiler however are the Injections/Outjections (you know, the @In and @Out annotations) from the Context (remember, we limited the profiling to our project package and those Bijections are handled by Seam and Reflection). However, I found a nice way to create a little custom annotation that you can put on top of your component classes and that is logging the time whenever a method on this component is called (including injection, method runtime and outjection). This together with the profiler where all the tools I used to identify the source of the problems and to consequently apply the modifications below.

The @Timed Annotation:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jboss.seam.annotations.intercept.Interceptors;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Interceptors(TimingInterceptor.class)
public @interface Timed {
}

The actual TimingInterceptor:

import org.jboss.seam.annotations.intercept.AroundInvoke;
import org.jboss.seam.annotations.intercept.Interceptor;
import org.jboss.seam.core.BijectionInterceptor;
import org.jboss.seam.intercept.AbstractInterceptor;
import org.jboss.seam.intercept.InvocationContext;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;

@Interceptor(around = BijectionInterceptor.class, stateless = true)
public class TimingInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = 1L;
    Log log = Logging.getLog(TimingInterceptor.class);

    @AroundInvoke
    public Object aroundInvoke(InvocationContext ic) throws Exception {
        String componentName = getComponent().getName();
        componentName += "." + ic.getMethod().getName();
        long milliStart = System.currentTimeMillis();
        Object o = null;
        try {
            o = ic.proceed();
        } finally {
            log.info("@TIMED: Execution of " + componentName + " took " + (System.currentTimeMillis() - milliStart) + " millis");
        }
        return o;
    }

    public boolean isInterceptorEnabled() {
        return true;
    }
}

Just put @Timed to the other annotations on top of your component and each time a method on the component is called, you will receive the debug output with the time.

Improvement #1: s:hasPermission() called too many times

Speed: +12%

Problem: If you use the permission system of Seam, you might call #{s:hasPermission(...)} a couple of times on your xhtml page – without realizing how often they are actually executed. For example, to decide whether to display a delete button you might have put rendered="#{s:hasPermission(person, 'delete')}" on the button. As you can confirm with your own profiling, the hasPermission() method (on Identity.java) is very fast, even when using a lot of drools rules and considering the profiler itself is slowing down all the stuff too. However, I had some pages with a lot of ui and a4j repeaters of lists and those led to hasPermission() being called nearly 20.000 times each page load with basically the same parameters each and every call. Even if the method requires just 0.1ms to be executed, the huge amount of calls will give you 20.000 x 0.1ms = 2 seconds of time spend inside the hasPermission() method.

Solution: Usually when I load a page, there is a Home Component behind it handling the instance of the entity in question. For example if you look at a page of a Person then there is likely a PersonHome handling the logic and the data. And most of the permission on the front-end depends on a single permission the user has on that particular object. So i put the following line into the setId(Long id) method of that Home component:

permissionPersonDelete = getIdentity().hasPermission(getInstance(), "delete");

and outject the permissionPersonDelete to the Conversation Context:

@Out(required = false)
public Boolean permissionPersonDelete;

The example of the delete button above would to be changed to the following instead: rendered="#{permissionPersonDelete}.

Considerations: I could have injected the ‘true’ and ‘false’ by using a c:set variable from JSTL but I had a lot of issues with those tags in conjunction with ajax4jsf. Many of the tags did not work properly so I tried to avoid them altogether. However, the best way to increase performance in terms of hasPermission() calls is and will always be to reduce the complexity and just not call it so many times – if possible.

Improvement #2: Huge components with @In chains

Speed: +12%

Problem: The next thing that became pretty obvious using the profiler, was a chain of component creations when doing an @In injection of a component that injects other components via @In itself (and those inject components and so on). The context injection itself is quite fast, but sometimes you call a component just because of a single method and its likely that all the injections taking place are not required for that. Keep in mind, that all the injections are handled via Reflection by Seam – and its speed depends on the size and the number of classes to reflect upon – so it actually can get surprisingly slow sometimes.

Solution: My general solution on this is to almost never use @In injections anymore, except for things that are not complex objects but maybe just a simple List or a string. So, instead of for example

@In
private IdentityManager identityManager;

I’d just remove the Annotation and leave the

private IdentityManager identityManager;

and whenever the identityManager is required in one of the methods i put just before that:

if (identityManager == null)
    identityManager = (IdentityManager) Component.getInstance(IdentityManager.class, CONVERSATION);

which does basically the same as the @In but it does it only when and where the injected component is actually needed.

What we get is kind of a lazy load procedure of injected objects.

Considerations: Iencountered some rare occasions, where the Component.getInstance() method was not working properly and I never really found out why. But, so I did with the @In annotations (especially when using it in web services / rest services). In those cases, just switch to the other way of fetching your components and you should be fine.

Improvement #3: Repeaters call Get-Methods several times

Speed: depends on list sizes

Problem: Let’s assume you want to display a list of addresses for one particular person. Whenever you use some a4j:repeat, ui:repeat or c:forEach you have to provide a list as a source for the iterations. In this case you might have a list directly on that person containing the addresses: #{person.addresses}. Now you want to display a member of the addresses in some output text for example. The thing to consider is, that whenever the iterated object is accessed, the list getter is called. Therefor, if you have any logic in the getter (person.addresses in our case), it will slow down rendering considerably. Fortunately, this is very easily visible when profiling.

Solution: Whenever a list accesses a getter method, you want to make sure there is the least amount of logic within that method. The way I usually do it is by caching the database fetch of the list within the method. Let’s assume the method would have looked like this before:

public List<resource> getFilteredResources() {
    List<Resource> resources = new ArrayList<Resource>();
    for (Resource r : getResources()) {
        if (r.getType.equals(Resource.TYPE_PERSON)) resources.add(r);
    }
    return resources;
}

and getResources() would contain an database query. I would have modified the method like so:

private List<Resource> resources = null
public List<Resource> getFilteredResources() {
    if (resources == null) {
        resources = new ArrayList<Resource>();
        for (Resource r : getResources()) {
            if (r.getType.equals(Resource.TYPE_PERSON)) resources.add(r);
        }
    }
    return resources;
}

This way, only the first access will trigger the list to be generated, while every other call on the method will just return the previously stored list. The EntityQuery of Seam (the one with the method ‘getResultList()’ does the caching already.

Improvement #4: Eager Load vs Lazy Load

Speed: +12% depending on model complexity

Problem: Our data model is quite complex, with a lot of inheritance and lists within lists. The Problem here is that fetching a list of objects via EntityQuery does not additionally join eager loaded lists into the context. If you have for example a Person class that contains an eager loaded list of addresses, a query on “from Person p” will not include the addresses, but instead, after fetching the list, run single select calls on each of the persons to get their lists of addresses into the context, no matter if they are accessed or not. If the model is sufficiently complex, this behavior can slow down list generation considerably.

Solution: Use lazy loading whenever possible! Although, I had some issues with this and working with drools, since the drools context seems to be unable to resolve hibernate proxies, so things have to be loaded into drools before any assertions can be made. I am doing the same with our EntityQueries now. If there is a list of Person objects and we know we are going to access their addresses anyway, I keep the address lazy loaded but join them in the query manually: select p from Person p join fetch p.addresses a.

As a side note: if the addresses are already eager loaded, the term ‘fetch’ seems to make sure to join them into the list without triggering another DB Query.

Considerations: I often encountered problems when saving a chain of objects, linked via lazy loaded lists. Especially in cases where the inheritance has been inverted. Usually it helps to eager load the lists instead, but first be sure you haven’t just forgotten to fetch the lists before working with them, because a non-fetched list of lazy loaded objects can not be edited (or better: persisted properly).

Improvement #5: Use Java 6 or higher

Speed: +20%

Problem: Even though with the changes above I already managed to decrease the loading time of large pages from around 17 seconds down to less than 10 seconds, this is not yet satisfying enough.

Solution: Nearly by accident, I found out that switching from Java 5 to Java 6 makes the whole thing faster immediately. I can’t explain why (although I can take a guess). So just try it yourself and see if it helps boost the speed of your project as well. I can imagine that switching to Java 7 increases the speed even more, though i haven’t tried it nor do I know how much increase of speed we are talking about here yet.

Improvement #6: Cache Page Fragments

Speed: >100% on second load

Problem: Many objects on my pages are not changing very often. However, there can be lists where new objects get added. Also the GUI changes considerable depending on whether you are an administrator or just a reader. All these things considered, it is quite impossible to cache the full pages as they are. However, Seam provides a means to cache fragments of your pages. This cache is pretty dumb and therefor requires a little bit of manual tweaking but the result is pure awesome.

Solution: You may surround parts of your xhtml markup with the <s:cache> tag, telling seam to put the result of the rendering of this section into the cache and load it from there next time it is called. You have to give it a unique key and like I said, the mechanism is dumb, so you have to take care of any changes within that area and drop the cache for that particular key in the case you need a fresh version of it.

I did it in a way that the key is constructed by the page name plus the ID of the object referred to in the fragment (if available) and a fragment name. So for example, the menu containing administrative functionality on a person might have the key “person#{person.id}.menu”. As you can see, the cache now holds a rendered version of this person’s (and only this person’s) administration menu.

I furthermore had to distinguish between at least four different permission types (none, reader, author, editor), which potentially get to see different interfaces on one and the same objects. So i make use of the region parameter of the cache, which works like a namespace to distinguish them. The full tag looks like this:

<s:cache key="person#{person.id}.menu" region="#{personHome.getPermission()}" >

My Home component provides the ability to return the maximum permission a user has on the particular instance as a string (none, reader, author, editor) by a getPermission() method I implemented there. This way i just check which namespace to use for the cache region.

To refresh the cache, i wrote a component I call PageCacheRefinery. I register every key I use on my xhtml pages together with a class name. This map provides the possibility, that if a class with this name has changed, the according cache fragment containing potentially new data is deleted and therefor refreshed on the next access.

@Name("pageCacheRefinery")
@Scope(ScopeType.APPLICATION)
public class PageCacheRefinery {

    @Logger
    public Log                         log;

    public final static String         REGION  = "org.jboss.seam.cache.DefaultCache";
    public final static List<string>   REGIONS = Arrays.asList("NONE", "READER", "AUTHOR", "EDITOR");

    private final static Map<string , List<String>> keyRegistry;
    static {
        Map<string , List<String>> tmp = new HashMap<string , List<String>>();
        tmp.put("Person", Arrays.asList("person%s.menu"));
        keyRegistry = Collections.unmodifiableMap(tmp);
    }

    private void purgeEntity(final DBEntity target) {
        CacheProvider cacheProvider = CacheProvider.instance();
        if (keyRegistry.containsKey(target.getClazzName())) {
            for (String key : keyRegistry.get(target.getClazzName())) {
                for (String region : REGIONS) {
                    cacheProvider.remove(region, String.format(key, target.getId()));
                    log.info("Purged cache for key "
                      + String.format(key, target.getId()) + " in region " + region);
                }
            }
        }
    }

    public void purgeEntireCache() {
        CacheProvider cacheProvider = CacheProvider.instance();
        cacheProvider.clear();
        log.info("Cache entirely cleared.");
    }

}

The only thing required to trigger this purging is to call the purgeEntity(getInstance()) from within your Home’s save() method.

This cache makes almost any page load within a few seconds on the second load. The advantage is also, that the cache is global. So, a user accessing a fragment triggers the generation of the cache element for any following user. Just make sure not to cache user specific parts of the UI. We have decided to use EHCache because of its independence on the application server. There is the possibility to configure the cache with a config file (lifetime, size etc.), but I still have to look into the details on that.

Considerations: Be very careful what and how you cache your page fragments. You will be surprised how often a cached fragment will actually disturb the logic of your page. For instance, if by accident you cache the content of a popup that should be refreshed or a fragments that is different for different access rights. We are constantly testing our pages after applying a cache to them to make sure that everything still works as expected.

Final Statement

Furthermore, I applied a couple of other minor tweaks here and there, but I am not so sure they are actually having such an impact on the performance. I decided to put some of them here anyway, because just logically they seem to make sense and even if they are not helping me that much, maybe they are helping you. One example is the possibility to always tell Seam in which context to look for a component (to avoid the ResolverChain to kick in and lookup the component in all the possible contexts, factories etc.). So instead of

Component.getInstance("personHome")

you would call

Component.getInstance("personHome", CONVERSATION)

which goes to pick up ‘personHome’ directly from the Conversation context.

I also found out that JSTL (c:if and c:forEach etc.) does not mix well with ajax4jsf components (like already mentioned above) and in general can be pretty slow anyway. So it is always wise to try and use ui:repeat whenever possible or at least stick only to a4j:repeat for compatibility. For example, ui:repeat does not work if you want to iterate over a list of objects within a rich:tabpanel to create a rich:tab per iterated object. In this case you should use a4j:repeat and not c:forEach – something that I have never found being mentioned in any of the sources online. On the other hand, there were many sources suggesting not to use rich:dataTables if possible and take h:dataTables instead. This however did not make any difference in our performance tests. What makes a difference however is the use of only one rich:tooltip and rich:modalpanel template to fill with the content on demand, for example via include – otherwise a lot of redundant javascript is being generated if you place a rich:tooltip on every row in your dataTable.

Besides all of this, you should always consider checking the load strategy of javascript (in your web.xml), enable gzip compression on your JBoss and maybe change the xml parser to NEKO (as mentioned in source no. 2) which unfortunately destroyed my layout and was not an option for me. But it seems to be very fast, so give it a shot!

Well, that’s it so far. As I said, I am not giving any guarantees to the effect of those tweaks and I wouldn’t even be surprised if some of you have some corrections on what I am saying or even their own personal tips to share. So please, write your thoughts into the comments section below, so we can try to make this list more complete and hopefully help each other out!

The sources of some of the ideas I implemented above and a few further tweaks (which in my case didn’t make such a difference though) you can find by following the links below:

  1. http://www.javadude.nl/2010/06/timing-seam-injection.html
  2. http://javaspecialist.wordpress.com/2010/05/30/performance-tuning-of-seam-jsf-richfaces-for-webapps
  3. http://www.jsfcentral.com/articles/speed_up_your_jsf_app_1.html

Additional sources added later on:

The post Howto: JBoss Seam Performance Problems and Optimizations appeared first on NeoZenCortexis.


Viewing latest article 4
Browse Latest Browse All 4

Trending Articles