Saturday, April 16, 2011

Using Shiro for Authorization via CDI Interceptors then Easily Test with Arquillian

Did you know Apache Shiro is an easy-to-use security framework for authentication and authorization in Java applications?
Did you know CDI (Java Context and Dependency Injection) Interceptors (and Seam Weld) can make your programming life easier?
Did you know JBoss Arquillian makes testing @Inject-enhanced Java code in a CDI container very easy?
Making It Work Together
Okay, enough with the buzzwords. And sorry for the confusing article title, but really this is about an example of integrating 4 separate cool (and practical!) stuff:
  1. Shiro for security (authentication and authorization)
  2. Use of CDI interceptors so that you don't have to sprinkle your business logic code with orthogonal concerns
  3. Integration testing (actually it is unit testing inside an integration container) your application in Arquillian, demonstrating that it is just as easy as plain JUnit tests. "Look ma, no manual wiring!"(tm)
  4. How Maven project management build tool helps in doing the tasks above easier, no more searching for stuff and configuring classpaths
The Goal
The goal is making this very simple JavaBean class:

public class ContactManager {

String name = "Hendy Irawan";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

... "enterprise-ready".

How? By making sure it handles authentication and authorization of secured operations without changing a single character of the above code at all!

"You've got to be kidding me!" No, I'm not. The trick is that I will add the following exactly before that code:

@Secured @NamedResource("contact")

And the bean will get secured. The best part is you can do it with any bean you want, not just the above.

Introducing Shiro The Guardian

The awesome framework that will perform the job as our security guy is Apache Shiro.

Summon Shiro in the Maven project's pom.xml :

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.1.0</version>
</dependency>

Shiro is very configurable, but for this example I'll just use a basic INI style configuration with the following contents:

[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
hendy = hendy, user

[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
guest = view:*
user = view:*, edit:*

I think the above is pretty easy to understand. Create the Shiro SecurityManager that loads the above configuration with:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
...
final String iniFile = "classpath:shiro.ini"; logger.info("Initializing Shiro INI SecurityManager using " + iniFile); securityManager = new IniSecurityManagerFactory(iniFile).getInstance(); SecurityUtils.setSecurityManager(securityManager);

It's now possible to check the permission by doing:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
...
Subject subject = SecurityUtils.getSubject();
try {
  subject.checkPermission("edit:contact");
} catch (Exception e) {
  logger.error("Access denied - {}: {}", e.getClass().getName(), e.getMessage());
  throw e;
}

Wiring It All with CDI

I don't want to manually assign objects by doing setSomedependency(object) all over the place. I want to use CDI dependency injection:

@Inject Subject subject;

which is provided by:

@Produces
public Subject getSubject() {
return SecurityUtils.getSubject();
}

You may argue that it's just as concise as:

Subject subject = SecurityUtils.getSubject();

However that code is not flexible, because it is tied directly to Shiro API. The use of dependency injection makes it easier if you want to change the Subject implementation to another (for example, during testing, a mock object).

Grab the JARs

Add the JBoss repository (btw, it's recommended to add this as a profile in your ~/.m2/settings.xml instead of adding directly to the project POM):

<repositories>
<repository>
<id>jboss-public-repository-group</id>
<name>JBoss Public Maven Repository Group</name>
<layout>default</layout>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</repository>
</repositories>

Here's the Maven dependencies:

<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.0-SP4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.interceptor</groupId>
<artifactId>jboss-interceptors-api_1.1_spec</artifactId>
<version>1.0.0.Final</version>
<scope>provided</scope>
</dependency>

By the way, if you use M2Eclipse plugin in Eclipse IDE, you don't have to worry about memorizing those because you can just right-click Project > Maven > Add Dependency and merrily search away the Maven repositories.

Using CDI Interceptors to Add Security Layer

Instead of putting the security code in the business logic (or in this case, entity) beans themselves, I'd like to use declarative security. First create an annotation that will serve as interceptor binding:

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

import javax.interceptor.InterceptorBinding;

@Inherited
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface Secured { }

Use it to secure the bean that needs it:

@Named @Secured
public class ContactManager { ...

Now implement the interceptor itself:

import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Secured @Interceptor
public class SecurityInterceptor {

@Inject Subject subject;
@Inject SecurityManager securityManager;
Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);
@AroundInvoke
public Object interceptGet(InvocationContext ctx) throws Exception {
logger.info("Securing {} {}", new Object[] { ctx.getMethod(), ctx.getParameters() });
logger.debug("Principal is: {}", subject.getPrincipal());

final Class<? extends Object> runtimeClass = ctx.getTarget().getClass();
logger.debug("Runtime extended classes: {}", runtimeClass.getClasses());
logger.debug("Runtime implemented interfaces: {}", runtimeClass.getInterfaces());
logger.debug("Runtime annotations ({}): {}", runtimeClass.getAnnotations().length, runtimeClass.getAnnotations());
final Class<?> declaringClass = ctx.getMethod().getDeclaringClass();
logger.debug("Declaring class: {}", declaringClass);
logger.debug("Declaring extended classes: {}", declaringClass.getClasses());
logger.debug("Declaring annotations ({}): {}", declaringClass.getAnnotations().length, declaringClass.getAnnotations());
String entityName;
try {
NamedResource namedResource = runtimeClass.getAnnotation(NamedResource.class);
entityName = namedResource.value();
logger.debug("Got @NamedResource={}", entityName);
} catch (NullPointerException e) {
entityName = declaringClass.getSimpleName().toLowerCase(); // TODO: should be lowerFirst camelCase
logger.warn("@NamedResource not annotated, inferred from declaring classname: {}", entityName);
}
String action = "admin";
if (ctx.getMethod().getName().matches("get[A-Z].*"))
action = "view";
if (ctx.getMethod().getName().matches("set[A-Z].*"))
action = "edit";
String permission = String.format("%s:%s", action, entityName);
logger.info("Checking permission '{}' for user '{}'", permission, subject.getPrincipal());
try {
subject.checkPermission(permission);
} catch (Exception e) {
logger.error("Access denied - {}: {}", e.getClass().getName(), e.getMessage());
throw e;
}
return ctx.proceed();
}
}

At this point you may think: "OMG! so much code just to check authorization. Can't you shorten the code a little bit?"

And that is exactly why you don't want to litter your business code with security or other concerns. So you can write as complex or long code as you want in the interceptor and not worry about it when you write the business logic.

You may also find bugs in the implementation above or change the requirements (e.g. to use Spring Security instead of Apache Shiro). No sweat, just fix the interceptor (this is just one alternative, read below why), and all the beans that use it will have it right after save the interceptor .java source file (since Eclipse IDE will auto-build your project, right? ;-).

The last step is activating the interceptor itself in the META-INF/beans.xml file:

<?xml version="1.0" encoding="UTF-8"?>
xsi:schemaLocation="
<interceptors>
<class>id.co.bippo.security.SecurityInterceptor</class>
</interceptors>
</beans>

Interceptors will be ignored by CDI container unless it is listed in beans.xml. Now I said that to change the implementation of the interceptor is just one way you can change the behavior of an interceptor binding annotation. Another perfectly valid way is just to activate a different interceptor class that implements the same interceptor binding, for example:

<interceptors>
<class>id.co.bippo.security.SpringSecurityInterceptor</class>
</interceptors>

You may notice I defined another annotation that is @NamedResource :

@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedResource {
String value();
}

My security implementation will check the permission using the value set by @NamedResource annotation, with the lowercase-name of the class used as fallback. The following:

@Named @Secured @NamedResource("contact")
public class ContactManager { ...

Tells the security interceptor to check the permission using "contact" as the resource name, not "contactmanager" inflected from the class name ContactManager.

From Great Power Comes Great Responsibility

Traditionally, running CDI unit tests using JUnit, outside a Java EE container means you either have to:
  1. Instantiate and configure a CDI implementation; or...
  2. Do it the old school POJO way --> someObject.setDependency(toAnother); all over the unit test
Unfortunately option #2 breaks down when you use other features than dependency injection, like interceptors.
And option #1 is just boring for your highly valuable time.

Arquillian Tames The Game

Of course, the movie won't be exciting unless you have a cool protagonist as your buddy: JBoss Arquillian. Let it handle the boring responsibility of dealing with the container and JUnit/TestNG integration, while you keep the power of easy unit testing and full CDI (and more!) features.

Summon the mighty Arquillian inside your pom.xml :

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<arquillian.version>1.0.0.Alpha5</arquillian.version>
<junit.version>4.8.2</junit.version>
</properties>

<dependencies>
<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-junit</artifactId>
<version>${arquillian.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
    <id>weld-se-embedded-11</id>
    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-weld-se-embedded-1.1</artifactId>
            <version>${arquillian.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.weld</groupId>
            <artifactId>weld-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.weld</groupId>
            <artifactId>weld-api</artifactId>
        </dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
<scope>runtime</scope>
</dependency>
        <dependency> 
            <groupId>ch.qos.logback</groupId> 
            <artifactId>logback-classic</artifactId> 
            <version>0.9.28</version>
            <scope>runtime</scope> 
        </dependency> 
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.weld</groupId>
                <artifactId>weld-core-bom</artifactId>
                <version>1.1.0.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-ext</artifactId>
<version>1.6.1</version>
</dependency>
        </dependencies>
    </dependencyManagement>
</profile>
</profiles>

The above POM snippet will:
  • depend on JUnit 4.8.2
  • depend on Arquillian 1.0.0.Alpha5 (don't be deceived by the "alpha" qualifier. it's excellently usable already!)
  • declare a weld-se-embedded-11 Maven profile that runs tests under Weld SE Embedded 1.1.0.Final CDI container. Note that Arquillian supports a whole host of other container configurations including GlassFish, JBoss, OpenWebBeans, etc.
I also made sure to use SLF4J 1.6.1 and the powerful Logback, but this is optional and you can replace it with slf4j-simple or log4j if you want.

Coding The Test

Fortunately, with a minor change, the test code is just plain JUnit 4 test case conventions you all know and love. (OK, some of you may love TestNG better, thankfully Arquillian also supports it)

import javax.inject.Inject;

import junit.framework.Assert;

import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class ContactManagerTest {

@Inject ContactManager cm;
@Inject Subject subject;
/**
 * Since Arquillian actually creates JAR files under the covers, the @Deployment
 * is your way of controlling what is included in that Archive. Note, each
 * class utilized in your test case - whether directly or indirectly - must
 * be added to the deployment archive.
 */
@Deployment
public static JavaArchive createTestArchive()
{
return ShrinkWrap.create(JavaArchive.class, "test.jar")
.addPackage("id.co.bippo.security")
//.addClasses(new Class[] {ContactManager.class, SecurityInterceptor.class, SecurityFacade.class})
.addAsManifestResource("META-INF/beans.xml");
}

@Test
public void guestCanView() {
subject.login(new UsernamePasswordToken("guest", "guest"));
Assert.assertEquals("Hendy Irawan", cm.getName());
}
@Test(expected=AuthorizationException.class)
public void guestCannotEdit() {
subject.login(new UsernamePasswordToken("guest", "guest"));
cm.setName("Pak Boss");
Assert.assertEquals("Pak Boss", cm.getName());
}
@Test
public void userCanEdit() {
subject.login(new UsernamePasswordToken("hendy", "hendy"));
cm.setName("Pak Boss");
Assert.assertEquals("Pak Boss", cm.getName());
}
}

There are only two things that make this JUnit test different than vanilla JUnit tests:
  1. Annotate the test class with @RunWith(Arquillian.class)
  2. Implement the public static @Deployment method. Declare all the classes (or packages) and files/resources that you need in the to test what you will deploy. It may take some time to get used to but after that you'll realize that it's so powerful. For example you can test how the beans behave with different beans.xml configuration! (putting the "C"ontext back in CDI!)

Launch The Test Away

Testing the project with Arqullian is as simple as:

mvn test -Pweld-se-embedded-11

Notice the profile argument. It means it's very easy to test your application in different containers at will just by changing the profile! Neatness to the Extreme! I'd say it's the definition of neatness.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running id.co.bippo.security.ContactManagerTest
16 Apr 11 16:03:14 org.jboss.arquillian.impl.client.container.ContainerRegistryCreator getActivatedConfiguration
INFO: Could not read active container configuration: null
16:03:14 [main] INFO  org.jboss.weld.Version - WELD-000900 1.1.0 (Final)
16:03:14 [main] INFO  org.jboss.weld.Bootstrap - WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
16:03:15 [main] WARN  o.j.i.util.InterceptionTypeRegistry - Class 'javax.ejb.PostActivate' not found, interception based on it is not enabled
16:03:15 [main] WARN  o.j.i.util.InterceptionTypeRegistry - Class 'javax.ejb.PrePassivate' not found, interception based on it is not enabled
16:03:15 [main] INFO  id.co.bippo.security.SecurityFacade - Initializing Shiro INI SecurityManager using classpath:shiro.ini
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Securing public java.lang.String id.co.bippo.security.ContactManager.getName() []
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Principal is: null
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime implemented interfaces: interface java.io.Serializable
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime annotations (2): [@id.co.bippo.security.Secured(), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring class: class id.co.bippo.security.ContactManager
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring annotations (3): [@id.co.bippo.security.Secured(), @javax.inject.Named(value=), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Got @NamedResource=contact
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Checking permission 'view:contact' for user 'null'
16:03:15 [main] ERROR i.c.b.security.SecurityInterceptor - Access denied - org.apache.shiro.authz.UnauthenticatedException: This subject is anonymous - it does not have any identifying principals and authorization operations require an identity to check against.  A Subject instance will acquire these identifying principals automatically after a successful login is performed be executing org.apache.shiro.subject.Subject.login(AuthenticationToken) or when 'Remember Me' functionality is enabled by the SecurityManager.  This exception can also occur when a previously logged-in Subject has logged out which makes it anonymous again.  Because an identity is currently not known due to any of these conditions, authorization is denied.
16:03:15 [main] INFO  o.a.s.s.m.AbstractValidatingSessionManager - Enabling session validation scheduler...
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Securing public java.lang.String id.co.bippo.security.ContactManager.getName() []
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Principal is: guest
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime implemented interfaces: interface java.io.Serializable
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime annotations (2): [@id.co.bippo.security.Secured(), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring class: class id.co.bippo.security.ContactManager
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring annotations (3): [@id.co.bippo.security.Secured(), @javax.inject.Named(value=), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Got @NamedResource=contact
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Checking permission 'view:contact' for user 'guest'
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Securing public void id.co.bippo.security.ContactManager.setName(java.lang.String) [Pak Boss]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Principal is: guest
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime implemented interfaces: interface java.io.Serializable
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime annotations (2): [@id.co.bippo.security.Secured(), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring class: class id.co.bippo.security.ContactManager
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring annotations (3): [@id.co.bippo.security.Secured(), @javax.inject.Named(value=), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Got @NamedResource=contact
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Checking permission 'edit:contact' for user 'guest'
16:03:15 [main] ERROR i.c.b.security.SecurityInterceptor - Access denied - org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [edit:contact]
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Securing public java.lang.String id.co.bippo.security.ContactManager.getName() []
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Principal is: hendy
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime implemented interfaces: interface java.io.Serializable
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime annotations (2): [@id.co.bippo.security.Secured(), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring class: class id.co.bippo.security.ContactManager
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring annotations (3): [@id.co.bippo.security.Secured(), @javax.inject.Named(value=), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Got @NamedResource=contact
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Checking permission 'view:contact' for user 'hendy'
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Securing public void id.co.bippo.security.ContactManager.setName(java.lang.String) [Pak Boss]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Principal is: hendy
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime implemented interfaces: interface java.io.Serializable
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime annotations (2): [@id.co.bippo.security.Secured(), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring class: class id.co.bippo.security.ContactManager
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring annotations (3): [@id.co.bippo.security.Secured(), @javax.inject.Named(value=), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Got @NamedResource=contact
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Checking permission 'edit:contact' for user 'hendy'
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Securing public java.lang.String id.co.bippo.security.ContactManager.getName() []
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Principal is: hendy
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime implemented interfaces: interface java.io.Serializable
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Runtime annotations (2): [@id.co.bippo.security.Secured(), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring class: class id.co.bippo.security.ContactManager
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring extended classes: {}
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Declaring annotations (3): [@id.co.bippo.security.Secured(), @javax.inject.Named(value=), @id.co.bippo.security.NamedResource(value=contact)]
16:03:15 [main] DEBUG i.c.b.security.SecurityInterceptor - Got @NamedResource=contact
16:03:15 [main] INFO  i.c.b.security.SecurityInterceptor - Checking permission 'view:contact' for user 'hendy'
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.621 sec

Results :

Tests run: 5, Failures: 0, Errors: 0, Skipped: 0

The Example Project

Now I'd argue that wasn't hard... compared to all the manual work that Java developers had to go through if doing it conventionally.

To make it even easier for everybody to see it in action with their own eyes, this complete example code is freely available, runnable (and forkable!) as arquillian-shiro-example project on GitHub.

Simply:

cd arquillian-shiro-example
mvn test -Pweld-se-embedded-11

And watch the magic unfold.

Conclusion

Java EE and its supporting technologies (like CDI) and tools (like Arquillian and Maven, note they are not specific to Java EE at all) continue to help developers become more productive by reducing unnecessary technical tasks. 


To know more about Java EE, I highly recommend Java EE 7 Essentials: Enterprise Developer Handbook by Arun Gupta.


Do you think CDI / Arquillian / Shiro / Maven or this article helpful to you? Let me know what you have in mind. :-)

Wednesday, April 13, 2011

Fixing EJB 3.1 + CDI + JSF 2.0 problem: javax.el.ELException: ...xhtml: The class ...WeldProxy does not have the property ...

Yesterday I got hit by a very frustrating problem in developing a Java EE 6 web application under Oracle GlassFish 3.1 Application Server, using EJB 3.1, CDI, and JSF 2.0 EL expression.

The error message:

SEVERE: Error Rendering View[/pages/refills.xhtml]
javax.el.ELException: /pages/refills.xhtml: The class 'com.abispulsa.bisnis.service.org$jboss$weld$bean-com$abispulsa$bisnis$jsf-SessionBean-RefillManager_$$_WeldProxy' does not have the property 'summary'.
at com.sun.faces.facelets.compiler.TextInstruction.write(TextInstruction.java:88)

It happens with this very simple JSF 2.0 Facelets tag:

<p>Records: #{refillManager.summary}</p>

Where refillManager is a EJB 3.1 bean:

import javax.ejb.Stateless;
import javax.inject.Named;

import com.abispulsa.refill.PendingHandler;

@Named("refillManager")
@Stateless
public class RefillManager implements PendingHandler {

Google Searches exposed many different issues, but nothing useful for my problem.

What I know is that it's caused by @Stateless, when I remove it and simply use CDI with @ApplicationScoped, it works perfectly. Of course at that point I lose declarative JPA transactions.
(note: You can use declarative transactions without EJB using Seam Persistence CDI extensions).

To add to the frustration, I can't seem to reproduce the problem with another EJB + CDI bean. The others work fine!

I found the solution while reading the excellent Java EE 6 Book Enterprise JavaBeans 3.1 from O'Reilly. It is @javax.ejb.LocalBean !

Cause of the problem is @Stateless/@Stateful session bean that implements an interface, but not marked it as @javax.ejb.LocalBean.

@LocalBeans designates that a session bean exposes a no-interface view.

If a bean implements interface(s), it seems EJB only exposes those interfaces, meaning JSF 2.0 EL will not be able to access getter/setters that are not part of the declared interfaces (although they're accessible using the concrete class).

This may also happen to objects from "rich" languages like Groovy or Scala.

Solution:

Annotate the class with @javax.ejb.LocalBean.

Example:

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Named;

import com.abispulsa.refill.PendingHandler;

@Named("refillManager")
@Stateless
@LocalBean
public class RefillManager implements PendingHandler {

Alternatives:
  1. Use EJB + JSF ManagedBean in XML, not CDI.
  2. Use CDI + Seam Persistence / Solder, not EJB. (I guess prefer this one)
Also, when all else fails: Undeploy, restart GlassFish, redeploy.

If you want to develop enterprise web applications using the latest Java EE 6 and EJB 3.1 technology be sure to get Enterprise JavaBeans 3.1 book from O'Reilly.

Tuesday, April 12, 2011

JSF 2.0 Cookbook [Java EE 6 Book]



Looking for a JSF 2.0 book that covers Java EE 6 web development? JSF 2.0 Cookbook from Packt Publishing might be what you're looking for.

The JSF 2.0 Cookbook contains step-by-step instructions for JSF users to build desktop-style interfaces in their own web applications. The book is designed so that you can refer to it chapter by chapter, or you can look at the list of recipes and read them in no particular order. This book is for two types of audience: Newcomers who know the basics of JSF but are yet to develop real JSF applications. JSF developers who have previous experience but are lacking best practices and a standard way of implementing functionality.

Written by Anghel Leonard:

The author is a senior Java developer with more than 12 years of experience in Java SE, Java EE, and the related frameworks. He has written and published more than 20 articles about Java technologies and more than 100 tips and tricks. He has also written two books about XML and Java (one for beginners and one for advanced developers). During this time, he has developed web applications using the latest technologies on the market. In the past two years, he has focused on developing RIA projects for GIS fields. He is interested to bring on web as much desktop as possible, therefore GIS applications represents a real challenge for him.

Some reviews:

I give Anghel Leonard's JSF 2.0 Cookbook ([...]) a qualified recommendation, but with a couple of caveats. Certainly he succeeds in providing JSF recipes that, taken as a whole, are worthwhile for the typical developer to know.

I would also like to recommend this book to anybody who is interested in what can be achieved server side, very quickly and with little prior knowledge. JSF 2.0 cookbook explains well how JSF can be used in conjunction other technologies

This book covers the new features of JSF 2 in the form of about 100 hundred recipes. In my opinion these recipes give a good overview of the new capabilities of the latest version of JSF

Disclosure: I am compensated from products you buy on Amazon.com.

Friday, April 8, 2011

Using Jackrabbit JCR 2.2.5 with SLF4J 1.6.1 and Logback Classic

Recently I was hit by a bug where it is impossible to use Jackrabbit 2.2.5 with SLF4J 1.6.1 due to a conflicting dependency error.
For some reason, SLF4J 1.5.11 is still loaded.

It turned that this is because Jackrabbit 2.2.5 depends on Apache Tika 0.8 which in turn depends on edu.ucar:netcdf:4.2 which bundles SLF4J 1.5. This error has since been fixed for Tika 0.9, so this should not be a problem for future Jackrabbit 2.3 versions.

To fix it, you need to exclude edu.ucar:netcdf:4.2 and replace the dependency with edu.ucar:netcdf:4.2-min as follows:

<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers</artifactId>
    <version>0.8</version>
    <exclusions>
        <exclusion>
            <!-- NOTE: Version 4.2 has bundled slf4j -->
            <groupId>edu.ucar</groupId>
            <artifactId>netcdf</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <!-- Patched version 4.2-min does not bundle slf4j -->
    <groupId>edu.ucar</groupId>
    <artifactId>netcdf</artifactId>
    <version>4.2-min</version>
</dependency>

Here's a complete Maven dependencies in pom.xml for Jackrabbit 2.2.5 and using SLF4J 1.6.1 with Logback classic logging implementation:

<!-- The JCR API -->
<dependency>
    <groupId>javax.jcr</groupId>
    <artifactId>jcr</artifactId>
    <version>2.0</version>
</dependency>

<!-- Jackrabbit content repository -->
<dependency>
    <groupId>org.apache.jackrabbit</groupId>
    <artifactId>jackrabbit-core</artifactId>
    <version>2.2.5</version>
</dependency>

<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers</artifactId>
    <version>0.8</version>
    <exclusions>
        <exclusion>
            <!-- NOTE: Version 4.2 has bundled slf4j -->
            <groupId>edu.ucar</groupId>
            <artifactId>netcdf</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <!-- Patched version 4.2-min does not bundle slf4j -->
    <groupId>edu.ucar</groupId>
    <artifactId>netcdf</artifactId>
    <version>4.2-min</version>
</dependency>

<!-- Use Logback for logging -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.6.1</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.6.1</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>0.9.28</version>
    <scope>runtime</scope>
</dependency>

Tuesday, April 5, 2011

Transitive Dependency Management in Groovy Grape / @Grab via @GrabExclude

Sébastien Launay provided a better solution for Fixing Slow @Grab Dependencies Issue with Groovy/Grape Scripts.

I didn't know that Groovy's Grape / @Grab supports transitive dependency management features (that Apache Ivy underneath is actually very good at). (where are the @Grab/@GrabExclude docs???) So this is very new to me.

Below is his post copied verbatim:

. . .

Actually I think a more sexier way would be to provide explicitly the version of groovy or even exclude the dependency it as it is provided by the application classloader.

So the following examples work and do not need to create a file:

@Grab(group="org.codehaus.groovy.modules.http-builder",
  module="http-builder", version="0.5.1")
@Grab(group="org.codehaus.groovy", module="groovy", version="1.7.10")

or

@Grab(group="org.codehaus.groovy.modules.http-builder",
  module="http-builder", version="0.5.1")
@GrabExclude("org.codehaus.groovy:groovy")

All of these are workarounds, the problem IMHO still lies in the usage of a version range dependency.

I am not sure what happens when the application classloader has let's say Groovy 1.7.0 and the Grape classloader (asuming there is a nested classloader) provides Groovy 1.8.0, I would say that Grape comes from Groovy 1.7.0 but the target script is compiled and executed with Groovy 1.8.0, am I right?

Friday, April 1, 2011

Fixing Slow @Grab Dependencies Issue with Groovy/Grape Scripts

Groovy scripts are not only quick to write and powerful but also very flexible because it can download Maven dependencies/artifacts automatically using the @Grab "annotation".

However there is currently an issue being discussed:  @Grab is Unusably Slow in Groovy-User mailing list.

Fortunately there is a solution (actually, workaround) before the powers that be (either Grapes and/or the http-builder guys) implements a proper solution.

The following is a verbatim copy of Sébastien Launay's post:

.

I also got the same issue which is a pain because scripting Rest Web Service in Groovy is very powerful especially when it is easy to share with Grape / @Grab.

The dynamic (version range) dependency is actually defined by http-builder himself. IMHO using version range is often a bad practice, at least on production release, because for the same sources the build result can be different when launched again which renders difficult bug analysis and maintenance.

The other thing is that Apache Ivy configuration tries all remote repositories to check for a new version even if it was resolved before but this is appropriate as it is a dynamic dependency. This can be tuned in Ivy with the cache TTL feature.

Indeed, I had the following logs:

== resolving dependencies
org.codehaus.groovy.modules.

http-builder#http-builder;0.5.1->org.codehaus.groovy#groovy;[1.5,1.7.99]
...
downloadGrapes: Checking cache for: dependency:
org.codehaus.groovy#groovy;[1.5,1.7.99] ...
default-cache: cached resolved revision expired for
org.codehaus.groovy#groovy;[1.5,1.7.99]

The default TTL in Ivy 2.1.0 is 10 seconds, by changing it I got
faster startup, this can be done by creating the file ~/.groovy/grapeConfig.xml from the one found in svn and
editing the file to have:

<ivysettings>
  <property name="ivy.cache.ttl.default" value="15m"/>
...

JBoss Seam version 3.0.0 Final for Java EE 6 Released

The Seam development team is pleased to announce the final release of Seam 3.0. Seam is a modular collection of portable extensions and tooling for Java EE6.

This latest version represents a significant milestone in Seam's history, as it is the first release to be based on the standardized component model defined by the CDI (JSR-299 Java Contexts and Dependency Injection) specification.

Seam's goal is to enable developers to create rich, standards-based internet applications by solving many of the challenges encountered when developing software in today's connected world. It achieves this by providing an integrated set of feature-centric modules, each targetted at a single area of concern. This modular design also allows users the choice of using individual Seam modules based on the requirements of their project, without forcing them to adopt the overhead of a monolithic framework stack.

The following table contains a list of the modules included in the Seam 3.0 release, along with a brief description of their features.

Solder Provides a collection of useful features for portable extension developers, and generally useful annotations and more for CDI-based applications.
Catch Provides a simple infrastructure based on the CDI event bus to allow developers to establish a unified and robust exception handling process.
Config Provides the capability to configure beans via alternate bean metadata sources, such as an XML-based configuration file.
Faces Further unifies JSF and CDI by providing additional features not defined by the specification, and provides JSF integration with other Seam modules.
International Provides a set of language and locale-based features to allow complete internationalization of your internet application.
Persistence Enables transactions and persistence features for managed beans, and provides a simplified transaction API.
Remoting Allows web-based applications to interact with the server-side component model via AJAX.
REST Provides JAX-RS integration for your Seam-based application.
Security Authentication and authorization services for your Java EE6 application.
Servlet Unifies the Servlet and CDI programming models via an event bridge, and provides producers for implicit Servlet objects.
Validation Provides enhanced validation features based on Hibernate Validator.
Wicket Integrates the CDI programming model and other portable Seam enhancements with Apache Wicket.

Maven Users

The Seam BOM (Bill Of Materials) is a Maven POM artifact that is provided as a convenience for your Seam application. It declares the versions for all Seam modules and third party libraries that are used in the Seam stack. To use Seam in your Maven-based project, it is first recommended that you define the Seam version you wish to use by declaring the following property value:

<properties> <seam.version>3.0.0.Final</seam.version> </properties>

Next, add the following section to your project's POM file to import the Seam BOM:

<dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.seam</groupId> <artifactId>seam-bom</artifactId> <version>${seam.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

After this, it is a simple matter of declaring which Seam modules you wish to use in your project. For example, if you wish to use the Seam Catch module in your project, simply add the following dependency:

<dependency> <groupId>org.jboss.seam.catch</groupId> <artifactId>seam-catch</artifactId> </dependency>

Please note that the Seam artifacts are published to the JBoss Community Maven Repository. See this page for more details on configuring Maven to use this repository.

Glassfish Users

If you use Glassfish 3.1 (or earlier) you must update the version of Weld contained inside your Glassfish installation. Please refer to the instructions at http://www.seamframework.org/Seam3/Compatibility for further information.

Downloads

Seam is available for download as a bundled distribution that includes all the modules, or alternatively as individual downloads for each module.

Bundled distribution: http://sourceforge.net/projects/jboss/files/Seam/3/3.0.0.Final/

Individual module downloads: http://www.seamframework.org/Seam3/Downloads

Documentation

Reference Documentation

API Documentation

Documentation for individual modules can also be found on the Seam documentation page.

User Forums

The Seam 3 user forums can be found at http://www.seamframework.org/Community/Seam3Users.

IRC

The Seam community is very active on IRC. Join us on the #seam or #seam-dev channels on Freenode to chat with the Seam developers. Find out more on the Seam Chat page.

Tooling

The Seam distribution comes bundled with Seam Forge, which replaces the seam-gen tool that was part of earlier versions of Seam. Forge is a rapid application development tool for Java EE6, which provides many productivity-enhancing features.

JBoss Tools 3.2 also supports Seam 3 development with its excellent CDI tooling. Some of the new features of Seam 3 are not yet covered, but are on the roadmap for JBoss Tools 3.3. You can find an informative walkthrough of the CDI tooling features here.

GWT

GWT support for CDI is provided by Errai. Please refer to the Errai reference documentation for details on integrating your GWT-based application with a CDI-based backend.

Acknowledgements

The Seam development team consists of a small team of full-time developers, plus a rapidly growing number of community contributors! Many of the modules in Seam 3 are led by community members who love and believe in Open Source software. Their ambition, innovation and ideas have made this important milestone possible! The entire Seam community should recognize and appreciate them for their dedication to the project, which is very much a collective community effort. While there are too many names to mention individually in this post, the full list of Seam contributors can be found here.


The above article is copied verbatim from Seam 3.0.0 Final announcement.