This blog is mainly about Java...

Showing posts with label Seam. Show all posts
Showing posts with label Seam. Show all posts

Monday, March 7, 2011

Creating an efficient memory based cache

If you find your self in the situation where you are creating global maps as cache, then you have to stop and rethink.
Global maps are prone to memory leaks

You should instead consider using soft reference and WeakHashMap or what I prefer, the MapMaker of Google Guava.

In this blog post, I will describe an efficient way of creating a map based cache. This can can either be stored in the session, application, or your existing cache.

Weak References
- What are weak references?

Weak reference basically means that the garbage collector can come and remove it when it is no longer in use. You have no guarantee that whatever you put in the map, will actually be around when you try to get it.
The reason why that is so useful is if you don't want to (or cannot afford to) retain an object indefinitely in memory.


Consider the following use case: You need to associate information with classes. Now, since you are running in an environment, where classes might get reloaded (say, a Tomcat, or OSGi environment), you want the garbage collector to be able to reclaim old versions of a class as soon as it deems safe to do so.

An initial attempt to implement this, might look like something like this:
 class ClassAssociation {  
   private final IdentityHashMap<Class<?>,MyMetaData> cache = new ...;  
 }  

The problem here is; this would keep all classes in the cache member forever (or at least, unless they are manually removed), forcing the garbage collector to retain them indefinitely, including everything referenced from the class (static member values, class loader information, etc).

By using weak references, the garbage collector can reclaim old version of the class as soon as no other references to it (usually instances) exist.
On the other hand, as long as such references exist, the value is guaranteed to also be reachable from the weak reference object, and thus, is a valid key in the cache table.

MapMaker FTW!

The thing about MapMaker is that there are many options for the kind of map you build, which enables those maps to serve many different purposes.
With the MapMaker you can choose between weak keys or weak values.

  • Soft values are useful for caching, as you can cache values in the map without worrying about running out of memory since the system is free to evict entries from the cache if it needs memory.
  • You can choose to have entries expire after a certain amount of time. This is also useful for caching, since you may want certain data cached for a specific period of time before doing an expensive operation to update it.
  • One of my favorite things is making a computing map. A computing map uses a Function to automatically retrieve the value associated with a given key if it isn't already in the map. This combines well with soft values and/or expiration times. After an entry is evicted by the map (due to memory demand or expiration), the next time the value associated with that key is requested it will automatically be retrieved and cached in the map once more.
Consider this example:
You have an expensive computation or query which you want to cache for performance gains. You store the value in a map with an id as key which you will use to retrieve your values.
Normally you would store these values in a regular HashMap and store the hashmap in the cache, session or application. Now we have seen that this is generally not a good idea, since it will consume a lot of memory.
It is in these situations the MapMaker shines!
Lets say you have a list of Tasks for each User. 
You would normally query the tasks like this:
Map<User,List<Task>> cache = new HashMap<User,List<Task>>(); //the global cache defined somewhere
 if(cache.get(user) == null) {
   List<Task> userTasks = getTasksForUser(user); // perform an intensive computation/query which we want to cache
   cache.put(user, userTasks);
 }
 return cache.get(user);

If you want to rewrite this to use a Computing MapMaker you would write like this:
ConcurrentMap<String, List<Task>> cache = ...// Get the cache
    if(cache != null) {
      //If the tasks have been garbage collected, the function is applied, and you get the tasks 
      return cache.get(user);
    } else {
      ConcurrentMap<String, List<Task>> cache = new MapMaker().softValues().expireAfterWrite(2L, TimeUnit.HOURS)
        .makeComputingMap(new Function<User, List<Task>>() {
        @Override
        public List<Task> apply(User user) {
          return getTasksForUser(user); // perform an intensive computation/query which we want to cache 
        }
      });
      
      cache.put(user, getTasksForUser(user));
      return cache.get(user);
    }

Here we have created a ConcurrentMap with weak values, which will be garbage collected in two hours. If the tasks have been garbage collected and the user is retrieving the tasks, the function is applied, and you get the tasks automatically, and put it back in the cache for another two hours.

Simple and great!

Thursday, October 7, 2010

Why you should NOT use the Seam Application Framework

NOT TO BE CONFUSED WITH SEAM, WHICH IS AWESOME!

I am a very experienced Seam user. I have used Seam daily now for almost 3 years.

In this blog post, I will try to convince you to avoid the Seam Application Framework, and truly understanding when it should be used, and when it shouldn't.

What is Seam Application Framework?
 Seam in Action says: "Seam is an application framework for Java EE. It provides a container that manages components and leverage's those components to tie the layers of an enterprise Java application together. Nestled within the Seam code base lies a handful of classes that comprise the Seam Application Framework. This "framework within a framework" is a specialized collection of component templates that effortlessly blanket the programming requirements of garden-variety web applications. Such tasks include performing create,read,update and delete (CRUD) operations on entity instances;querying for data; and developing JSF page controllers. You may be hesitant to give this spattering of classes much notice, but I assure you that they'll carry you a long way."

According to Dan Allen, he is encouraging you to check this "framework within a framework" out. However, though he is right, that there are benefits with this framework, I will prove that there are far more disadvantages than advantages and that he should have rewritten the last line to include that the framework is only really useful for small CRUD applications, and not something to leverage and build upon.

Way too many people are using this framework
If you have a look at the Seamframework.org forums, you will see tons of questions from people having problems and issues with the Seam Application Framework. Either a lot of people are developing small CRUD based applications, or there is a growing misconception about Seam and Seam Application Framework.

There are many people that are confused about this notion. They think that Seam == Seam Application Framework, and that they have to use it. And who can blame them? The seam-gen utility is a fantastic way of getting you quickly started. However, I have always encouraged people to only use the create-project and generate-entities part of the utility, and not the generate-ui part of it, this because most people I have talked with, are not developing small CRUD based applications, but rather medium sized enterprise apps, but since you get the Seam Application Framework for free, it is quite easy to extend and use it.

PROS
I did mention that there are a few pro's about using this framework, but it depends on people truly understanding the inner workings of the framework.
  • Quick to get started
  • Excellent for small models,presentation and proof of concept
  • Great in-built pagination of lists
  • Can @Override the methods and inherit classes to extend/tweak upon the framework
If you want to visualize for your customer, boss or client, an application as a proof of concept, then it is very easy to do this using the framework. In this case you don't care so much about performance and inherited abstraction layers, but rather how quickly you can get something visual and up and running.

I also want to note, before I get spammed with comments about how wrong I am, that you can if you know what you are doing, speed things up. Details can be found here. However, this again requires some knowledge about Seam.

CONS
  • Its a bit slower
  • More abstraction which might not suit your requirements
  • More difficult to use than the plain components. You need to "learn a new framework as well as Seam"

Consider this very very simple example. We have a SystemLog, and we want to display the log.


@Entity
public class SystemLog {

    @Getter @Setter
    @Id
    @GeneratedValue
    private Long id;

    @Getter @Setter
    @NotEmpty
    private String description;

    @Getter @Setter
    @Column
    private String category;

    @Getter @Setter
    @Temporal(TemporalType.TIMESTAMP)
    private Date date;

    @Getter @Setter
    @Column
    private String username;

    @Getter @Setter
    @Column
    private String organization;

    @Getter @Setter
    @Column(length = 1024)
    @Lob
    private String details;

    @Getter @Setter
    @Column
    private String severity;

    @Getter @Setter
    @Column
    private String ipAddress;

}

We have an action class that used the EntityQuery of the Seam Application Framework.


@Name("systemLogQuery")
@MeasureCalls
public class SystemLogQuery extends EntityQuery<SystemLog> {
  @Logger Log log;
  
  @Override
  public String getEjbql() {
    return "select sys from SystemLog sys";
  }
  
  @Override
  public List<SystemLog> getResultList() {
    log.warn("Inside systemlog query getResultList");
    return super.getResultList();
  }

}

And this normal (non tweaked) seam component.


@Name("systemLogNormal")
@MeasureCalls
public class SystemLogNormal {

  @Logger Log log;
  
  @In EntityManager entityManager;
  
  List<SystemLog> result;
  
  public List<SystemLog> getResultList() {
    log.warn("Inside systemLog normal resultList");
    if(result == null)
      this.result = entityManager.createQuery("FROM SystemLog").getResultList();
     
    return result;
  }
}


And a simple data table iterating over the elements

<h:dataTable value="#{systemLogNormal.resultList}" var="sys">
    <h:column>
      <f:facet name="header">
        description
      </f:facet>
      #{sys.description}
    </h:column>
</h:dataTable>

Now at first glance, I think the normal seam component is much cleaner and easier to read. Remember, this is a very simple example. In a normal real life example you would have extended the component to be much more complicated, and if you want to do this using the existing framework, you will need to find out what the framework already is doing, and correctly override those methods. Otherwise, you risk of doing the same thing twice.

If you ask your self what the @MeasureCalls annotation is doing, then have a look here, and scroll down to the second example. But a short description is that it measures the time of execution for a method.

When you run this example using the SystemLogNormal and the Query component, they are pretty much equal. However, you will see that the Query component is also calling the validate() method, which takes a few milliseconds to execute.
It is correct that a few milliseconds is hardly anything noteworthy, however this is a very very simple example, with very little data, and in a more realistic scenario, the number could possibly be different. Either way, the overall time difference is that the EntityQuery object is a few milliseconds slower.

13:58:24,224 WARN  [SystemLogQuery] Inside systemlog query getResultList
13:58:24,228 INFO  [STDOUT] Hibernate: 
    select
        systemlog0_.id as id0_,
        systemlog0_.category as category0_,
        systemlog0_.date as date0_,
        systemlog0_.description as descript4_0_,
        systemlog0_.details as details0_,
        systemlog0_.ipAddress as ipAddress0_,
        systemlog0_.organization as organiza7_0_,
        systemlog0_.severity as severity0_,
        systemlog0_.username as username0_ 
    from
        SystemLog systemlog0_
13:58:24,239 WARN  [SystemLogQuery] Inside systemlog query getResultList
13:58:24,257 WARN  [SystemLogQuery] Inside systemlog query getResultList
13:58:24,334 INFO  [TimingFilter] 2.201607 ms   1   EntityQuery.validate()
13:58:24,334 INFO  [TimingFilter] 15.967185 ms   3   SystemLogQuery.getResultList()



13:58:31,647 WARN  [SystemLogNormal] Inside systemLog normal resultList
13:58:31,648 INFO  [STDOUT] Hibernate: 
    select
        systemlog0_.id as id0_,
        systemlog0_.category as category0_,
        systemlog0_.date as date0_,
        systemlog0_.description as descript4_0_,
        systemlog0_.details as details0_,
        systemlog0_.ipAddress as ipAddress0_,
        systemlog0_.organization as organiza7_0_,
        systemlog0_.severity as severity0_,
        systemlog0_.username as username0_ 
    from
        SystemLog systemlog0_
13:58:31,662 WARN  [SystemLogNormal] Inside systemLog normal resultList
13:58:31,678 WARN  [SystemLogNormal] Inside systemLog normal resultList
13:58:31,712 INFO  [TimingFilter] 15.740409 ms   3   SystemLogNormal.getResultList()


As you can see, we get three outputs of "Inside systemlog". Really there should only be one. So adding a @Factory annotation on top of it will do the trick for both versions.

Then there are the Home and Query objects, with methods like isIdDefined(), clearDirty(), isManaged(), getEjbql(), RESTRICTIONS and more confusing methods which you need to read the documentation to understand.

Conclusion

In my opinion, for first time users who are learning Seam, it is more than enough to understand and learn the framework, instead of in addition learning "a framework inside a framework". The seam-gen utility is sort of a devil in disguise. Its great for a shortcut to get started, but its evil in disguise for new comers.

Newbies think Seam Application Framework is Seam, and all components they create must extends the Home, List or Query objects. Its confusing, and in my opinion they should avoid using it unless they know all the ins and out of both Seam and the application framework.

So if you are new to Seam, start by looking at the Seam examples that comes with the bundle. They are far better to use, than generating the UI with seam-gen and going from there.

Saturday, February 13, 2010

Advanced Seam series part 1 of 3: Seam Interceptors

Seam Interceptors

EJB 3.0 introduced a standard interceptor model for session bean components. Interceptors are a great way of performing extra functionality. I will not go into detail of what Interceptors are. If you want to know more, google is your friend. 

Prerequisites you should know:
  • Seam has a few built in Interceptors, these include but are not limited to:
    • BijectionInterceptor
    • MethodContextInterceptor 
    • ConversationInterceptor
    • SynchronizationInterceptor
    • ConversationalInterceptor
    • RemoveInterceptor
    • SeamInterceptor
    • SecurityInterceptor
    • TransactionInterceptor
    • EventInterceptor
    • HibernateSessionProxyInterceptor   
  • @BypassInterceptors will skip Interceptors 
In this example I will show you two examples on how you can use your own interceptor as a way to enhance your security domain and measure how long each method takes to execute.

First example
Imagine this simple model. A user is connected to one organization, and only the user connected to that Organization is allowed to view information about that organization.



Let's say you print out the users organization, and you have a page with detailed information about that organization like this:

<s:link view="myOrganization.xhtml" value="#{organizationAction.choose}">
    <f:param name="id" value="#{organization.id}"/>
</s:link>

In the request, something like this will appear:
http://mydomain.com/myOrganization.xhtml?actionMethod=myOrganization.xhtml%3organizationAction%choose()&organizationId=3

And this is how your simple OrganizationAdmin.java component looks like:

@Name("organizationAdmin")
@Scope(ScopeType.CONVERSATION)
@Restrict("#{s:hasRole('user')}&;quot;)
public class OrganizationAction {

    @RequestParameter
    Long id;

    @In
    EntityManager entityManager;

    Organization myOrganization;
   
    @Begin
    public void choose() {
        if(id != null) {
            this.myOrganization = (Organization) entityManager.find(Organization.class, id);
        }
    }

    public Organization getMyOrganization() {
          return this.myOrganization;
    }
}

Now, imagine changing the organizationId in the request to show information about an organization you should initially not be allowed to see.

This is obviously very bad. We have restricted the component to role user, but another user can easily change the request to another id, and get hold of another organization.

To fix this, you can add this extra security check:

@Name("organizationAdmin")
@Scope(ScopeType.CONVERSATION)
@Restrict("#{s:hasRole('user')}")
public class OrganizationAction {

    @RequestParameter
    Long id;

    @In
    EntityManager entityManager;

    Organization myOrganization;
   
    @In(create = true)
    User currentUser; //The current user logged in
   
    @Begin
    public void choose() {
        if(id != null) {
            this.myOrganization = (Organization) entityManager.find(Organization.class, id);
            if(!currentUser.getOrganization().getId().equals(myOrganization.getId()))
                throw new SecurityException("You are not allowed to view this page");
        }
    }

    public Organization getMyOrganization() {
          return this.myOrganization;
    }
}

Voila! Now if an authorized user tries to "hack" the system, you will throw a SecurityException. This is fine and works just fine. However, it is cumbersome to do and you have to remember to do this everywhere where these exploits exists. 
Wouldn't it be nice to just do this:

@Name("organizationAdmin")
@Scope(ScopeType.CONVERSATION)
@Restrict("#{s:hasRole('user')}")
@MySecurityAuthorized
public class OrganizationAction {

    @RequestParameter
    Long id;

    @In
    EntityManager entityManager;

    Organization myOrganization;
 
    @Begin
    public void choose() {
        if(id != null) {
            this.myOrganization = (Organization) entityManager.find(Organization.class, id);
        }
    }
    @AuthorizeOrganization
    public Organization getMyOrganization() {
          return this.myOrganization;
    }
}

I for one think this is much easier to do. You can even reuse the @AuthorizeOrganization annotation to apply the same security other places. You only need to write the logic once.

Our first Interceptor
We can do this by using @Interceptor or @Interceptors. The latter is pre Java EE 5 compatible. I will cover both, but will show the latter in this example.

We start by creating our two annotations.
@AuthorizeOrganization
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthorizeOrganization {}
@MySecurityAuthorized
/**
 * This annotation can be added to any Seam component to hook in interception of our custom annotations.
 * Important: Unless a custom specific @AuthorizeX annotation (like @AuthorizeOrganization) is also present, this annotation won't do anything 
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@org.jboss.seam.annotations.intercept.Interceptors(MyInterceptor.class)
public @interface MySecurityAuthorized {}

Note the @Interceptors(MyInterceptor.class). It is here we register this annotation to the Interceptor MyInterceptor.
AnnotationHandler.java
Note that this is just an interface that all our custom annotations will inherit from


public interface AnnotationHandler {
    public void preProceed();
    public void postProceed(Object retObj);
    public boolean isCurrentlyUsed();
}

BasicHandler.java
An abstract class all handler will extend from. Read javadoc and comments in code to get real understanding of what's going on. Much of this code is really not necessary for this simple example. But this is a good portion of the one we are using, and it is quite flexible.

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.interceptor.InvocationContext;

import org.jboss.seam.Component;
import org.jboss.seam.core.Events;
import org.jboss.seam.log.Logging;

/**
 * Class that can be extended when creating new custom managed annotations.
 * Add extending classes to MyInterceptor.
 *
 * @param <T> The Annotation that should be looked for
 */
public abstract class BasicHandler<T extends Annotation, U> {

    private User user;
    private Boolean isAdmin;
    
    private boolean isMethodAnnotated;
    private List<U> allInstanceParams = new ArrayList<U>();
    private List<U> annotatedInstanceParams = new ArrayList<U>();
    private List<Long> annotatedIdParams = new ArrayList<Long>();
    
    private Class<U> authorizedClass;
    
    /**
     * This constructor parses the method signature for annotations,
     * validated annotated types and
     * if needed verifies the current User
     * @param ic, the current InvocationContext
     * @param annotationClazz, the class of the annotation to be scanned for
     * @param authorizedClass, the class of the object to authorize
     */
    @SuppressWarnings("unchecked")
    public BasicHandler(InvocationContext ic, Class<T> annotationClazz, Class<U> authorizedClass) {

        this.authorizedClass = authorizedClass;
        
        //method annotation
        Annotation annotation = ic.getMethod().getAnnotation(annotationClazz);
        if (annotation != null) {
            isMethodAnnotated = true;
        }
        
        //parameter annotations - why why why is there no Parameter class with methods for annotations, values, types?
        Annotation[][] aaa = ic.getMethod().getParameterAnnotations();
        for (int i = 0; i < aaa.length; i++) {
            Object obj = ic.getParameters()[i];    
            if (isMethodAnnotated) {
                allInstanceParams.add((U) obj);
            }
            Annotation[] aa = aaa[i];
            for (int j = 0; j < aa.length; j++) {
                Annotation a = aa[j];
                if (a.annotationType().equals(annotationClazz) && ic.getParameters()[i] != null) {
                    //So we've found an annotated not null param
                    if (authorizedClass.isInstance(obj)) {
                        //instance of class to be authorized
                        annotatedInstanceParams.add((U) obj);
                    } else if (obj instanceof Long) {
                        //id of instance of class to be authorized
                        annotatedIdParams.add((Long) obj);
                    } else {
                        //Annotation put on erronous type
                        illegalObjectAnnotated((U) obj, annotationClazz);
                    }
                    //params.add(ic.getParameters()[i]);
                }
            }    
        }

        //Verify user
        if (isCurrentlyUsed()) {
            getUser();
        }        
    }
    
//public methods
    
    public boolean isCurrentlyUsed() {
        return (isMethodAnnotated || !annotatedInstanceParams.isEmpty() || !annotatedIdParams.isEmpty() ? true : false);
    }
    
//protected methods
    
    @SuppressWarnings("unchecked")    
    protected List<U> getAllReturnInstances(Object retObj) {
        List<U> l = new ArrayList<U>();
        
        if (isMethodAnnotated()) {            
            if (authorizedClass.isInstance(retObj)) {
                //Simple object
                l.add((U) retObj);
            } else if (retObj instanceof Collection<?>) {
                //Collections
                for (Object obj : (Collection<?>) retObj) {
                    if (authorizedClass.isInstance(obj)) {
                        l.add((U) obj);
                    }                     
                }
            }
        }            
        return l;
    }

    /**
     * Authorizing Organization
     * Role "admin" may access everything
     * Other (like "user") must be within the organization to have privilege
     * @param organization
     */    
    protected boolean authorize(Organization organization) {
        if (organization != null) {
            return authorizeUser(organization.getId().toString());
        } else {
            //null object need no authorization                
            return true;            
        }        
    }
    
    /**
     * Getting current logged in user
     * @return Getting current logged in user
     */
    protected User getUser() {
        if (user == null) {
            user = (User) Component.getInstance("currentUser", true);
            if (user.getId() == null || user.getId() == 0) {
                //annotation is present but we don't have an user
                logAndThrow("User is null, must exsist to enable authentication");
            } 
        }
        return user;
    }
    
    /**
     * Is user admin?
     * @return
     */
    protected boolean isAdmin() {
        if (isAdmin == null) {
            return isRoleInRoles(user.getRoles(), "admin");
        }
        return isAdmin;
    }

    /**
     * Checks to see if the role is in the Collection<Role>
     * If one of the roles appears in the Collection it returns true
     * 
     * @param allroles - All the roles
     * @param roles - varargs containing the roles to check for
     * 
     * @return true if any of the role is found
     */
    private boolean isRoleInRoles(Collection<Role> allroles, String ...roles) {
        if (allroles == null || roles == null || roles.length == 0)
            return false;

        for (Role r : allroles)
            for(String role : roles)
                if (r.getName().equals(role))
                    return true;

        return false;
    }
    
    protected boolean logAndThrow(String s) {
        Logging.getLogProvider(this.getClass()).warn(s);
        throw new SecurityException(s);
    }

//private methods

    private boolean authorizeUser(String orgId) {
        User user = getUser();

        if (isAdmin()) {
            //Admin can do whatever            
            return true;
        } else if (user.getOrganization().getId().equals(orgId)) {
            //Correct organization, can also check role here if you want
            return true;                                
        } else {
            //wrong organization, fail authorization
            return logAndThrow("User '" + user.getUsername() + "' is not authorized to view this page");
        }                
    }
    
    private boolean isMethodAnnotated() {
        return isMethodAnnotated;
    }
    
    private void illegalObjectAnnotated(U obj, Class<? extends Annotation> annotationClass) {
        logAndThrow("Program error, annotation " + annotationClass.getSimpleName() + " cannot handle object of type " + obj.getClass().getSimpleName());
    }    
    
//Getters    

    public List<U> getAllInstanceParams() {
        return allInstanceParams;
    }    
    
    public List<U> getAnnotatedInstanceParams() {
        return annotatedInstanceParams;
    }    

    public List<Long> getAnnotatedIdParams() {
        return annotatedIdParams;
    }        
}

@AuthorizeOrganizationHandler.java

/**
 * This AnnotationHandler handles the @AuthorizeOrganization annotation.
 * It authorizes a User for an Organization
 *
 */
public class AuthorizeOrganizationHandler extends BasicHandler<AuthorizeOrganization, Organization> implements AnnotationHandler {
    
    /**
     * This constructor looks for @AuthorizeOrganization markers on method
     * If any exists, User is validated directly.
     * @param ic
     */
    public AuthorizeOrganizationHandler(InvocationContext ic) {
        super(ic, AuthorizeOrganization.class, Organization.class);
    }

//Public methods
    
    public void preProceed() {
        //Method level - input parameters
        for (Organization obj : getAllInstanceParams()) {
            authorize(obj);
        }
    }

    public void postProceed(Object retObj) {
        //return value(s)
        for (Organization obj : getAllReturnInstances(retObj)) {
            authorize(obj);
        }        
    }
        
}


Last but not least, our interceptor.
MyInterceptor.java

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

/**
 * This interceptor handles method/param level annotations
 * (Seam only provides Class (ElementType.TYPE) level annotation support)
 * Thus this interceptor must be added to those beans that wish to use these annotations
 * If we implement new annotations, their corresponding AnnotationHandler must be registered below
 *
 */
public class MyInterceptor {
    @AroundInvoke
    public Object aroundInvoke(InvocationContext ic) throws Exception {
        
        //gather custom annotation handlers
        List<AnnotationHandler> ahs = new ArrayList<AnnotationHandler>();
        addHandlerIfNeeded(new AuthorizeOrganizationHandler(ic), ahs);
        //add new annotation handlers here - TODO: Use more clever generics instead
        
        //handle pre proceed
        for (AnnotationHandler ah : ahs) {
            ah.preProceed();
        }

        //proceed
        Object obj = ic.proceed();
        
        //handle post proceed
        Collections.reverse(ahs); //Reversing list in order to get same behavior as interceptors/filters
        for (AnnotationHandler ah : ahs) {
            ah.postProceed(obj);
        }
                
        return obj;
    }

    private void addHandlerIfNeeded(AnnotationHandler handler, List<AnnotationHandler> ahs) {
        if (handler.isCurrentlyUsed()) {
            ahs.add(handler);
        }
    }
}

Thats it! Now you can easily add new annotation and handlers and just add login in BasicHandler. If you look closely you will see that we automatically give all privileges to admin, but only check user's organization if they are role user. You can modify and do whatever fits your need.

Second Example

Finally I want to also mention the other Seam interceptor.
This is taken directly from the Seam forum (Link here). Which will print something like this:
284.94 ms   1   FooBean.getRandomDroplets()
284.56 ms   1   GahBean.getRandomDroplets()
201.60 ms   2   SohBean.searchRatedDoodlesWithinHead()
185.94 ms   1   FroBean.doSearchPopular()
157.63 ms   1   FroBean.doSearchRecent()
 42.34 ms   1   FooBean.fetchMostRecentYodel()
 41.94 ms   1   GahBean.getMostRecentYodel()
 15.89 ms   1   FooBean.getNoOfYodels()
 15.00 ms   1   GahBean.getNoOfYodels()
  9.14 ms   1   SohBean.mainYodels()
  1.11 ms   2   SohBean.trackHoorayEvent()
  0.32 ms   1   FroBean.reset()
  0.22 ms  43   NohBean.thumbPicture()
  0.03 ms  18   FooBean.getMostRecentYodels()
  0.01 ms   1   NohBean.profilePicture()
  0.01 ms   1   FroBean.setToDefault()
  0.01 ms   1   FroBean.getRecentMarker() 



The first column contains the accumulated time spent in this method. The second column contains the number of times the method has been called, and the final column shows which method was called.

This Interceptor will measure how long each method will take to execute in milliseconds.
TimingInterceptor.java

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.jboss.seam.annotations.intercept.AroundInvoke;
import org.jboss.seam.annotations.intercept.Interceptor;
import org.jboss.seam.core.BijectionInterceptor;
import org.jboss.seam.core.ConversationInterceptor;
import org.jboss.seam.core.ConversationalInterceptor;
import org.jboss.seam.core.EventInterceptor;
import org.jboss.seam.core.MethodContextInterceptor;
import org.jboss.seam.core.SynchronizationInterceptor;
import org.jboss.seam.ejb.RemoveInterceptor;
import org.jboss.seam.ejb.SeamInterceptor;
import org.jboss.seam.intercept.InvocationContext;
import org.jboss.seam.persistence.HibernateSessionProxyInterceptor;
import org.jboss.seam.security.SecurityInterceptor;
import org.jboss.seam.transaction.TransactionInterceptor;

/**
 * This interceptor will calculate how long it takes to execute each method
 * 
 * Copied and modified from @link http://www.seamframework.org/Community/SeamPerformanceProblemRewardingWorkaround
 *
 */
@Interceptor(
    around = {
        BijectionInterceptor.class,
        MethodContextInterceptor.class,
        ConversationInterceptor.class,
        SynchronizationInterceptor.class,
        ConversationalInterceptor.class,
        RemoveInterceptor.class,
        SeamInterceptor.class,
        SecurityInterceptor.class,
        TransactionInterceptor.class,
        EventInterceptor.class,
        HibernateSessionProxyInterceptor.class
})
public class TimingInterceptor {

    public final static CallChain callChain = new CallChain();

    @AroundInvoke
    public Object timeCall(InvocationContext invocation) throws Exception {
        long t0 = System.nanoTime();
        try {
            return invocation.proceed();
        } finally {
            long dt = System.nanoTime() - t0;
            callChain.addInvocation(invocation, dt);
        }
    }

    // -----------------------------------------------------------------------------

    /**
     * A call chain is the set of invocations on methods (annotated 
     * with MeasureCalls) that a request issued on its way through 
     * the application stack.
     */
    public static class CallChain extends ThreadLocal<Map<Method, TimedInvocation>> {

        @Override
        protected Map<Method, TimedInvocation> initialValue() {
            return new HashMap<Method, TimedInvocation>();
        }

        public void addInvocation(InvocationContext invocation, long dt) {
            Map<Method, TimedInvocation> invocations = get();
            Method method = invocation.getMethod();
            if (!invocations.containsKey(method)) {
                invocations.put(method, new TimedInvocation(invocation.getMethod(), dt));
            } else {
                TimedInvocation timedInvocation = invocations.get(method);
                timedInvocation.anotherCall(dt);
            }
        }

        public int totalNumberOfInvocations() {
            Map<Method, TimedInvocation> invocations = get();
            Collection<TimedInvocation> timedInvocationCollection = invocations.values();
            int totCalls = 0;
            for (TimedInvocation invocation : timedInvocationCollection)
                totCalls += invocation.getCalls();
            return totCalls;
        }
    }
}

TimedInvocation.java

import java.lang.reflect.Method;

import org.apache.commons.lang.StringUtils;

/**
 * TimedInvocation is an invocation (i.e. a method call) which is being
 * counted and timed. 
 */
public class TimedInvocation implements Comparable<TimedInvocation> {

    private long dt;
    private int calls = 1;
    private Method method;

    public TimedInvocation(Method method, long dt) {
        this.method = method;
        this.dt = dt;
    }

    public long getDt() {
        return dt;
    }

    public Method getMethod() {
        return method;
    }

    /**
     * The first column contains the accumulated time spent in this method. 
     * The second column contains the number of times the method has been called.
     * The third column contains the method which was called
     */
    public String toString() {
        String className = method.getDeclaringClass().getName();
        String shortendName = className.substring(method.getDeclaringClass().getPackage().getName().length() + 1);
        String duration = StringUtils.leftPad(Double.valueOf(dt / 1e6) + " ms", 11);
        String nCallStr = StringUtils.leftPad(String.valueOf(calls), 4);
        return duration + nCallStr + "   " + shortendName + "." + method.getName() + "()";
    }

    public void anotherCall(long dt) {
        this.dt += dt;
        calls++;
    }

    public int compareTo(TimedInvocation o) {
        return -Long.valueOf(dt).compareTo(o.dt);
    }

    public int getCalls() {
        return calls;
    }
}

Lastly, we will create a simple filter that will print out the result. The filter is only installed in debug mode so that it doesn't run on production. Be aware that the interceptor will still run if you don't forget to uncomment the interceptor.
TimingFilter.java

/**
 * This filter is used in conjunction with the TimingInterceptor. * 
 * It is defaulted to only work in debug mode (debug = true)
 * To use this filter you need to set value to true
 * 
 */
@Startup
@Scope(ScopeType.APPLICATION)
@Name("timingFilter")
@BypassInterceptors
@Filter
@Install(debug = true)
public class TimingFilter extends AbstractFilter {
    
    private static final LogProvider log = Logging.getLogProvider(TimingFilter.class);
    
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        if (!(req instanceof HttpServletRequest)) {
            chain.doFilter(req, res);
            return;
        }
        
        TimingInterceptor.callChain.remove();
        chain.doFilter(req, res);
        
        Map<Method, TimedInvocation> invocations = TimingInterceptor.callChain.get();
        for(TimedInvocation timedInvocation : invocations.values()) {
            log.debug(timedInvocation.toString());
        }
    }
}
Finally create an annotation where you register the interceptor.

@MeasureCalls

/**
 * This is an annotation that is used with the Interceptor TimingInterceptor,
 * that will measure time it takes to execute each method in a seam component.
 * 
 * The filter that uses this interceptor only works in debug mode and you will need to set value to true 
 * on the TimingFilter. Otherwize you will not see anything when running this in production or test. 
 * However, the interceptor will still run, so be "careful" where you put this annotation.
 * 
 * Usage: 
 * @AutoCreate
 * @Name("theBeanILikeToInvestigate")
 * @MeasureCalls
 * public class TheBeanILikeToInvestigare
 * 
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Interceptors(TimingInterceptor.class)
public @interface MeasureCalls {}

Just put @MeasureCalls on your component and all methods on that component will be invoked on.

Tuesday, February 9, 2010

Advanced Seam series

In the upcoming weeks I will be posting some nice articles about some of the advanced features of Seam I know people have trouble understanding and incorporating. 

The articles will be a "three" (may be more) part series where I will cover the topics Interceptors, Filters and Asynchronous events.

I will show working real life examples of all three. You will learned:
  • What Interceptors are, and how you can use them and incorporate security techniques using interceptors.
  • Filters - Why they are useful to use. 
    • I will show how you can implement a filter that enables downloading of files without consumption of unnecessary memory.
  • Asynchronous - I will show how you can send a mail asynchronously

If there are any advanced areas you wish to know more about, let me know by writing in the comment section and I will consider adding them to the series.

Friday, November 20, 2009

Seam / Infinispan meetup in Devoxx

So I just returned from the Seam / Infinispan meetup here in Devoxx and there where a few GOTCHA that I will come back to.

Firstly, the meeting it self was pretty awesome. Although I feel that the Seam discussion wasn't that coordinated as the Infinispan talk. There were too much focus on Weld, but I guess thats not surprising since Weld was just recently released, and most Seam developers except poor Norman Richards, where working on Weld to get it finished before the deadline.
Don't take my words literally, Norman might have done some work towards Weld for all I know, but he was at least the only one working on Seam 2.

Anyways, the ones from the Seam team who where present (forgive me if I forget someone) where: Pete Muir, Dan Allen, Norman Richards, Shane Bryzak and Christian Bauer.
Dan held a quick intro on Seam, for the 3 people who didn't know what Seam was, and then Pete talked a bunch about Weld, and finally Dan closed with showing a very cool testing framework which I cannot remember the name of now which will be implemented for Seam 3, and also Rest on Seam. Of course with a lot of Q&A in between.

So finally I got a chance to speak with Dan, Norman and Pete. I really wanted to have a chat with Shane as well, but unfortunaltly by the time I went to get a beer and come back he was gone. :-(
First I got a chance to ask Dan about the strange behaviour I found regarding injection of a stateless EJB in Seam, discussed here , and he confirmed what I suspected all along, that it was infact a bug in Seam. Now, I he didn't actually say "That is a bug" per say, but he did say "If that doesn't work, it is a bug in Seam". But from my experience I can safely conclude that this is a bug in Seam.
Before you say "Hey, why don't you file a JIRA on it", I will tell you why I will not.
After talking with Pete, I discovered that infact there WILL BE NO MORE releases of Seam. Meaning, even if there are any bugs, or even if someone in the community upload a patch, or even some core devel0per fix the bug in the Seam 2.2 branch, there will be no official release of it. You can however checkout a version of that fix and build binaries for your self.
I know, pretty lame and to be honest a big surprise from my part. But Pete did explain why, and the reason is that before shipping an official release, it has to go to the QA team for testing, and probably some other branches and this takes up rescources that JBoss doesn't want to commit to. Instead they want to focus on the development and shipping Seam 3, which is planned to come around march 2010.
So you must ask your self "What about us who are using Seam 2.x and really need to fix this and that issue? Is all hope gone, other than fixing it our selves and running a build?" The answer is no. If you REALLY want something, basically all you have to do, is buy a support deal from JBoss or a certified JBoss partner (as we are), and ask them to fix it. JBoss will then put resources into fixing that and actually commiting the fix in the branch so that it is available for everyone to checkout and build, however, they still will not release a version of that fix. You have to build it your self.

Lastly, this blog post deserves to be mentioning what Seam 3 will look like. Though this is not official or final, it is what I know has been discussed and agreed upon.
Seam 3 will be what is missing from the Java EE 6 stack. Everything thats sort of extra that didn't get into the spec. It will obviously contain all the pdf, rss, excel goodies we are familiar to from Seam 2, including JBPM 4 and Drools 5 support. Not to forget all the goodies that comes with Java EE 6, Bean validation, Servlet 3.0, EJB 3.1, JPA 2 and so on. It will also be modular based, meaning, that if you only need certain parts of Seam, then it is no need to get anything else which is irrelevant to you. There is probably more that I have forgotten, but Pete will blog about this pretty soon.
Lastly, I want to mention that I did try to encourage the people to participate on the seam forums, and help people that are struggling with their Seam 2.x apps. Because face it; those people are most likely to move to Seam 3 and Weld, and if you want them to do that, you should try to make the learning curve of Seam 2.x as little as possible.
Pete did agree, however he did say that the reason why he couldn't reallly encourage the core developers to use more time on the forums is because of all the time they spent on getting Weld finished. 14 hour(!) days jikes. But hopefully in the future, they will have more time to help the users out there.

Good night from Devoxx.

Thursday, October 8, 2009

IE8 bug in Seam 2

There seem to be a problem with IE8 and Seam applications. We are running Seam 2.1, JSF 1.2 and Facelets, and I cannot say for sure where the bug is. For all I know its a bug with IE8.

There seem to be problems when creating long-running conversations. In link using IE8 the cid doesnt appear. Somehow it is stripped or doesn't render correctly, and it causes some strange behaviour.
When trying to create a long running conversation by pushing an edit link for instance, we get a "Your session has timeout" exception.

We fixed this IE8 problem by telling Internet Explorer to emulate IE7 in pages.xml
<page view-id="*">
<header name="X-UA-Compatible">IE=EmulateIE7</header>
</page>

I suggest you do the same until the bug is identified and fixed.

Tuesday, July 14, 2009

Injecting checking of roles in Seam

It is well known that you can perform
#{s:hasRole('admin')}
on the view.
You can easily check for the role of the logged in user, by injecting the same thing.

@In(value = "#{s:hasRole('admin')}")
private boolean isAdmin;
If the user has a role of admin the boolean is set to true. You can also use the static method,
Identity.instance().checkRole("admin");

But the former is nicer right?

Sunday, June 28, 2009

Iteration on password is added in JBoss Seam 2.1.2

I blogged about how you could hash your password using Seam 2.1.1 hash-user-password-in-seam-211-manually.

In the latest production release of JBoss Seam 2.1.2 and the @UserPassword annotation, an attribute of iteration is now added with a default of 1000.

We should then modify our GeneratePasswordHash method to the following:

/**
* This method will generate a hash password
*
* @param password
* - The password in cleartext
* @param salt
* - The username is used as salt
* @return - hash password based on password and username
*/
public static String generatePasswordHash(String password, String salt) throws GeneralSecurityException {
char[] passToChar;
byte[] saltToByte;
String thePassword;
try {
passToChar = password.toCharArray();
saltToByte = salt.getBytes();
AnnotatedBeanProperty<UserPassword> userPasswordProperty = new AnnotatedBeanProperty<UserPassword>(ProcessUser.class, UserPassword.class);
// Will get the hash value from annotation UserPassword in ProcessUser.class
PasswordHash.instance().setHashAlgorithm(userPasswordProperty.getAnnotation().hash().toUpperCase());
thePassword = PasswordHash.instance().createPasswordKey(passToChar, saltToByte, userPasswordProperty.getAnnotation().iterations());
return thePassword;
} finally {
// Ensure that the password is not in memory
password = null;
passToChar = null;
salt = null;
saltToByte = null;
thePassword = null;
}
}

Wednesday, May 20, 2009

New Seam book "Seam 2.x Web Development"

I have been asked to review the new book Seam 2.x Web Development from Packt Publishing

You can buy the book here
and there is also a free chapter "Developing Seam Applications" in PDF here: http://www.packtpub.com/files/seam-2-x-web-development-sample-chapter-2-developing-seam-applications.pdf

I am quite curious as to how this book is compared to Seam in Action by Dan Allen, and I will post my review here as soon as I have read the book.

Friday, February 13, 2009

Hash user password in Seam 2.1.1 manually without using IdentityManager

In the Seam Documentation, they give a minimal schema example of how you can create Users and Roles here: http://docs.jboss.com/seam/2.1.1.GA/reference/en-US/html_single/#d0e9178

As it is explained in the documentation, you can annotate your password with

@UserPassword(hash = "md5")
public String getPasswordHash() { return passwordHash; }
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
You could also use
hash= "none"
which doesnt hash the password which is what I did at first. The reason why I didnt start using md5 at once is because you needed a default username and password to be able to log into your seam application. And since I was using an import.sql script that created a default user for me, I had no way of knowing how to hash the password.

So what I needed to do is manually somehow hash a password which I could put in my import.sql script, so that I could enable hashing and correctly log in to my seam application. However this was easier said then done.
I tried changing the @UserPassword(hash="md5") and try to log in, but for obvious reason the login failed, because it was expecting a hashed password.

So then I got the idea to call a method that doesn't require login, which creates a user for us, that is called from a button in the login page. However when I persisted the user, the password was saved as clear text. This baffled me, because I thought that seam would handle that automatically. But if you actually look at the user entity and the setHashPassword method, it is not annotated, and it only sets a string. So it was obvious it only persisted the password as string.
In Seam you can also use IdentityManager to create a user for you. This will perform the hashing for you. So I tried to call
identityManager.createUser("username","password","firstname","lastname");

However, identityManager requires that you are correctly Authenticated, and it threw an exception
org.jboss.seam.security.AuthorizationException: Authorization check failed for permission seam.user,create

So apparently IdentityManager requires the correct permissions, which must be set in your drools setting in security.drl, but I didn't want to do that, since that is not how I wanted to configure my seam application.

So what I did, was look in the seam source and IdentityManager class and find the exact code that performs the hashing for us, and do that manually. And this worked great. Here is the source.

public String saveProcessUser() {
// Check if a new password has been entered
if (currentUser.getPasswordHash() != null && !"".equals(currentUser.getPasswordHash())) {
if (!currentUser.getPasswordHash().equals(passwordVerify)) {
StatusMessages.instance().addFromResourceBundle("admin.wrongPassword");
return "failure";
}
}

// If passwordVerify is not empty, it generally means its a new password
if (!passwordVerify.equals("")) {
String hashPass = generatePasswordHash(currentUser.getPasswordHash(), currentUser.getUsername());
log.debug("Setting new hash password: " + hashPass);
currentUser.setPasswordHash(hashPass);
}

if (getCurrentUser().getId() != null) {
entityManager.flush();
} else {
entityManager.persist(currentUser);
}

FacesMessages.instance().addToControlFromResourceBundle("successMessage", "admin.user.saved");
return "success";
}

/**
* This method will generate a hash password
*
* @param password - The password in cleartext
* @param salt - The username is used as salt
* @return - hash password based on password and username
*/
private String generatePasswordHash(String password, String salt) {
AnnotatedBeanProperty<UserPassword> userPasswordProperty = new AnnotatedBeanProperty<UserPassword>(ProcessUser.class, UserPassword.class);
// Will get the hash value from annotation UserPassword in ProcessUser.class
String algorithm = userPasswordProperty.getAnnotation().hash();
return PasswordHash.instance().generateSaltedHash(password, salt, algorithm);
}


Note that the method saveProcessUser() is called from the user administration form, and the currentUser object is my User entity. Seam will inject all the methods for us, so we dont have to do that. So we check if the verifyPassword is set and correct and then we hash the password. If the @UserPassword is set to something other than none, then it will correctly save a user and you can finally copy the hashed password in your import.sql script.

PS: You first should have the @UserPassword(hash="none") and then go to your user registration and then just print out what the password is, and then copy that to your import.sql script. When the authentication correctly works with either md5 or sha, then you can safely add the user to the database.

Wednesday, February 4, 2009

How to create and use a WebService with Axis 2 and Seam 2.x in JBoss 4.x

In this example, I will show how you can create a Webservice using Axis 2.
First of all, download the latest version of Axix 2 from http://ws.apache.org/axis2/

To create a WebService in Java EE 5 you can use the annotation @WebService.
We also annotate this class as a seam component so that we can incorporate it in our existing business logic.

This is our WebService:

package somepackage.webservice;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.persistence.EntityManager;

import org.jboss.seam.Component;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.security.Credentials;
import org.jboss.seam.security.Identity;

@Name("fooService")
@Stateless
@WebService(name = "FooService", serviceName = "FooService")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class FooService implements FooServiceLocal {

@In EntityManager entityManager;

@In Credentials credentials;

private boolean login(String username, String password) {
credentials.setUsername(username);
credentials.setPassword(password);
Identity.instance().login();
return Identity.instance().isLoggedIn();
}

private boolean logout() {
Identity.instance().logout();
return !Identity.instance().isLoggedIn();
}

@WebMethod
public List<FooCanonical> getFoo(@WebParam(name = "username")
String username, @WebParam(name = "password")
String password, @WebParam(name = "orgnumber")
String orgnumber) {
// orgnumber can be null!
if (username == null || password == null) {
return null;
}
//First thing we do is to login to ensure that the user has the correct username/password
//We are using basic seam login method
boolean isLoggedIn = login(username, password);

if (isLoggedIn) {

List<FooCanonical> returnList = new ArrayList<FooCanonical>();
//Do some stuff with the list
//Remember to log out
logout();
return returnList;
} else {
// Probably wrong username password
return null;
}
}

}


Next what we need to do, is create a way for this webservice to interact with JBoss through our SOAP definition. We do that by creating a xml file called
standard-jaxws-endpoint-config.xml

<jaxws-config xmlns="urn:jboss:jaxws-config:2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jaxws-config:2.0 jaxws-config_2.1.xsd">
<endpoint-config>
<config-name>Seam WebService Endpoint</config-name>
<pre-handler-chains>
<javaee:handler-chain>
<javaee:protocol-bindings>##SOAP11_HTTP</javaee:protocol-bindings>
<javaee:handler>
<javaee:handler-name>SOAP Request Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.seam.webservice.SOAPRequestHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</pre-handler-chains>
</endpoint-config>
</jaxws-config>

And place this file in the $JBOSS_HOME/resources/META-INF directory.
Now you are done! Deploy your application and look in
http://localhost:8080/jbossws/services 
and see if your WebService is correctly deployed and the wsdl available.
This should look something like this:

Endpoint Namejboss.ws:context=foo-foo,endpoint=FooService
Endpoint Addresshttp://localhost:8080/foo-foo/FooService?wsdl

Next, we will use the Axis2 framework to create client stubs by using axis2-1.4.1 and the script wsdl2java. Navigate to $AXIS_HOME/bin and type in the following command:
./wsdl2java.sh -uri http://127.0.0.1:8080/foo_foo/FooService?wsdl -o build/client

This command will create an ant script under the directly build/client.
Now go to build/client and type ant after setting $AXIS_HOME. This will generate FooService-test-client.jar which we now can use to retrieve data from the WebService in the client. I recommend changing the name to something more appropriate.

In your client, you can call the getFoo WebMethod like this:

FooServiceStub stub;
GetFoo getFoo;

stub = new FooServiceStub();
getFoo = new FooServiceStub.GetFoo();
getFoo.setUsername("username");
getFoo.setPassword("password");
getFoo.setOrgnumber("1234");

FooServiceStub.GetFooE fooImpl = new FooServiceStub.GetFooE();
fooImpl.setGetFoo(getFoo);

//Retrieve the List as an array
FooCanonical[] get_return = stub.getFoo(fooImpl).getGetFooResponse().get_return();
//Do what you want with the array

Note that even if you return a List from the WebService, you will get it as an array. But it is quite easy to put it in a List in the client afterwards. Also remember that the username and password is sendt in clear text, so you might want to send it through https, so it is encrypted.

Saturday, September 27, 2008

Seam, EJB and Hibernate useful tips

Here are some tips I have gathered during our Seam project.

@Create - Signals that this method should be called upon instantiation of the JavaBean.

@Stateless - Makes an EJB3 object

@Stateful - (SESSION) Caches in memory between server requests

@In - These variables will automatically be set by Seam through injection of the variables labeled by the annotation. i.e
String zipCode; (Behind the curtain: zipCode = request.getParameter("zipCode"); )

@Out - Same as above. This variable is also automatically set by Seam. Behind the curtain this variable is set in the HttpSession object.
( request.setAttribute("bestRestaurant", restaurant); )
What you also must remember is that if you want to use the outjected object, you only call it with its name. Not component name first.
i.e. If you stateful session bean is named @Name("someName") and you have a @Out String test, then when you use it in the view, you must write #{test} and not #{someName.test}
You can also say @Out(required = false) which means that the Object can be null. Otherwize it cannot.

@DataModel annotation exposes an attribute of type java.util.List to the JSF page as an instance of javax.faces.model.DataModel. This allows us to use the list in a JSF with clickable links for each row. The DataModel is made available in a session context variable named the variable name.

@PersistenceContext(type=EXTENDED)
EntityManager em;
This stateful bean has an EJB3 extended persistence context. The messages retrieved in the query remain in the managed state as long as the bean exists, so any subsequent method calls to the stateful bean can update them without needing to make any explicit call to the EntityManager.
Remember, its a good idea to clear the entitymanager once in a while if you do lots of searches

@Factory("messageList")
public void findMessages() {
messageList = em.createQuery("from Message msg order by msg.datetime desc").getResultList();
}
The first time we navigate to the JSP page, there will be no value in the messageList context variable. The @Factory annotation tells Seam to create an instance of MessageManagerBean and invoke the findMessages() method to initialize the value. We call findMessages() a factory method for messages.

@Remove
@Destroy
public void destroy() {}
All stateful session bean Seam components must have a method with no parameters marked @Remove that Seam uses to remove the stateful bean when the Seam context ends, and clean up any server-side state.

@NamedQuery(name="findProeveByAddress",
query = "SELECT p " +
"FROM Proeve p, PersonAdresseAdressetype paa "
+ "WHERE paa.person = p.person "
+ "AND (lower(p.person.fornavn) like :fornavn OR lower(p.person.mellomnavn) like :fornavn) "
+ "AND (lower(p.person.etternavn) like :etternavn) "
+ "AND (lower(p.person.foedselsnummer) like :foedselsnr) "
+ "AND (lower(paa.adresse.adressefelt1) like :adresse) "
+ "AND (lower(p.kommune.navn) like :kommunenavn) "
+ "order by p.person.etternavn, p.avlagt desc"
)

@Lob - Used for storing large string/byte objects. Becomes text or blob.

Join Fetch - Makes the fetch eager. Be careful using join fetch. What I mean with that is that you should only use fetch when you are going to call methods
on the object being fetched. Otherwize it will be a performance issue.

@Enumerated(EnumType.STRING) - Makes the enum as string in the database

@Observer("proeveSearchDirty")
public void clear() {
proeveSokList = null;
}
With the observer annotation you can call on methods by giving it a name from other seam components.
For instance you can raise event which will call the observer method after an persist is made

@RaiseEvent("proeveSearchDirty")
public String persist() {
return super.persist();
}

If you want to override the default "Successfully created/updated/removed" from Seam and your EntityHome objects, then you can either override the methods
in the Home class. i.e
@Override
public String getCreatedMessage() {
return "Saved";
}

@Override
public String getUpdatedMessage() {
return "Updated";
}

@Override
public String getDeletedMessage() {
return "Removed";
}
Or you can put it in the messages.properties file with the notation:
*EntityName*.created. For instance:
Adresse_created=Lagret
Adresse_updated=Oppdatert
Adresse_deleted=Slettet
The messages.properties will be the default even if you have overrided the methods in your Home objects.

Friday, August 22, 2008

Using Encryption (Jasypt) in Seam 2.0 and how to search on encrypted values

In our project we had to encrypt all fields in the person table that can identify a person.
I found a nice framework that makes encryption quite easy called Jasypt and more specifically, it had very nice and easy configuration for Seam 2, which can be found here: Jasypt with Seam 2.

The problem however is that all our searches that we had created for the person fields that are now encrypted fail. For obvious reasons, you cannot compare (run LIKE) on encrypted fields.
But the user demanded to still be able to filter the search based on the encrypted values.
There are two theories on how I could do this.

The first, was to encrypt the user input and then try to find a match against the encrypted fields. However since Jasypt uses SALT I cannot easily do this because SALT generates x amount of random bytes and makes two equal values different chipertext. So to solve that I would have to remove the SALT and do the comparing. However we have very little time to solve this, so I went with the second option which I really wanted to avoid.

What I do know is create a query based on the values that are not encrypted and return the List. However, it is not certain that the user will enter one of the values that is not encrypted, so the search will then retrieve all Person objects, and then I loop through the List, decrypt the values (which Jasypt automatically does) and compare against what the user has inputted in the search criteria and then return the correct list.
This is a very cumbersome method to retrieve the List. Having all the Person objects in memory is not feasible. I will have to add some sort of caching so that it at least will be better when it is in production, but still this is something I would like to avoid. (Yes I know I have all the objects decrypted in the memory/cache, but I don't see any other solution)

If anyone has other ideas on how could be solved, then please leave a comment or contact me.

Labels