Friday, January 26, 2007

What should replace EJB Home interface in EJB3?

In the new EJB3 standard, there is no notion of an EJB Home interface If you are making the switch from EJB2.x to EJB3, chances are you will actually come to miss this. At least, you will have to think about where to place the Home methods now.

In the old EJB2.x standard, it was actually part of the specification where to create, find and remove instances of entity beans, namely on the Home interface.

Some typical (simplified) entity code in EJB2.x would be something like:


FooHome home = jndi.lookup("...jndi path of foo bean");
Foo newInstance = home.create(42);
...
...
Foo foundInstance = home.findByPrimaryKey(....);


So, the Home interface was typically used for Creates, Reads and Deletes in the CRUD acronym. Updates where simply done by setting fields on instances.

Enforcing the developer to place create and read methods on a special EJB Home interface was actually a piece of architectural enforcement of the old specification. You had to do this, period. This is gone in EJB3. The funny thing is that the new blueprints for EJB3 does not mention this. Actually, I think the current blueprints must be a freaking joke. They seem so shallow.

I've been so fortunate to also be working a lot with spring and hibernate the last couple of years and from this, I have some advice on how to architect this in EJB3 and JPA, without the home.

In spring/hibernate the common solution at time of writing is to architect with "services beans" and "dao beans", where dao beans take the role of the old EJB Home interfaces. "Services" beans is what we commonly call "business logic". When services needs to do CRUD, they go through one or more DAOs. Taking this directly to an EJB3 example, it will go something like this:
  • For each (JPA) entity, say Foo, construct a stateless session bean FooDao
  • Annotate the SLSB with @PersistenceContext to access the data store where Foo is placed
  • Add methods for create, read and delete (the C, R and D of CRUD)
  • Create SLSBs to architect your business layer
  • Annotate the business layer with "@EJB FooDao" where data access for Foo is needed
Some example code looks like this:


public interface FooDao {
Foo create(...)
Foo findById(....);
void delete(Foo);
}

@Stateless
public class FooDaoBean implements FooDao {
@PersistenceContext
private EntityManager entityManager;

public Foo create(...) {
Foo foo = new Foo(...);
entityManager.persist(foo);
return foo;
}

public Foo findById(...) {
return entityManager.find(Foo.class, ...);
}

public void delete(Foo foo) {
entityManager.remove(foo);
}
}


And a "services" SLSB would simply do:


@Stateless
public class BarServicesBean implements BarServices {
@EJB
private FooDao fooDao;

public void doStuff(...) {
Foo foo = fooDao.findById(...);
....
}
}


Keeping strict to this strategy for all JPA mapped entities has the benefit of keeping all direct access to the EntityManager API inside DAO SLSBs while making it easy for developers to know where to look for doing CRUD on an entity. And why is it nice to keep access to the EntityManager API in one layer?

For once, it is often a good design principle to isolate access to specific APIs to certain modules or layers in your application. Another good thing about it is more directly targeted at specifics about the named queries of the JPA API contra type safety.

Looking again at our example above, the Foo entity could be defined like this:


@Entity
@NamedQuery(name="app.foo.byName", query="SELECT o FROM Foo o WHERE o.name = :name")
public class Foo {
@Id
private Long id;

@Basic(optional = false)
private String name;

....
}


To execute the named query defined above, you go through the JPA API like this:

entityManager.createNamedQuery("app.foo.byName").setParameter("name", "bar").getSingleResult()


Now what is wrong with this?
  • Setting "name" parameter is not type-safe (setParameter takes Object type as second parameter)
  • Ensuring that all parameters in the query has been set is determined at runtime
If we later on redefine the named query to, say, take an extra parameter, we would need to find all places it was used and add setting of the parameter. But we can do better - by encapsulating access to the finder in one method, placed in a dao bean, like this:


public Foo findByName(String nameToLookFor) {
return entityManager.createNamedQuery("app.foo.byName").setParameter("name", nameToLookFor).getSingleResult();
}


All services (or other daos) that need to access the finder shall go through this method. If we add a new parameter to the finder, we will also add a new parameter to the findByName method - a thing that is statically checked at compile time. Much better.

To sum up, what I'm saying is, when switching from EJB2.x to EJB3:
  • Think about the Home interface being gone
  • Go to lengths to avoid having @PersistenceContext shattered all over your business logic
  • One architectural advice is the dao stuff above which is proven to work well with spring and hibernate

Sunday, January 07, 2007

IP-based Authentication in WebLogic

Some time ago I was asked to make certain some usernames could only be authenticated if tried from specific known IP-addresses. The application is running on a WebLogic server, and we are already running our own custom AuthenticationProvider and AuthorizationProvider.

It turned out getting to know the IP address of the incoming client request to authenticate was quite difficult. Actually, the WebLogic SSPI APIs for the Authentication part did not allow us to get the IP address.

Okay, so I thought I could simply put the IP address on some thread local storage in a WebLogic ConnectionFilter implementation and then access it from the Authentication implementation. But, guess what, the ConnectionFilter implementation and the SSPI implementation for a user request does not execute on the same thread, rendering the thread local solution useless.

Hmm, what then. I - briefly - thought about creating a servlet filter that could throw off the sensitive users after authentication but before application logic. It would work, but had several drawbacks
  • I - and others - would have to remember to mount the filter, and do it correctly, before any actual application logic
  • The sensitive user would actually be allowed login from a non-allowed IP before being thrown off
Back to the drawing board. After some googling and talking to WebLogic support, I found a feature in the Authorization part of the code. When a resource is authorized in WebLogic, at some point WebLogic calls our AccessDecision.isAccessAllowed(...) and among the parameters is the ContextHandler.

Basically, a ContextHandler is a Map like object, which contains "context information" from the request, which can be used in the process of authorization of the given resource. When the resource is a URLResource, a HttpServletRequest can be found in the ContextHandler which makes it possible to obtain the IP address.

So, the final solution ended up checking IP address and username against known IP ranges, all at authorization time. Sadly, this is still after authentication, but we simply could find no other solution inside the WebLogic SSPI APIs.