With the advent of Java EE 6 it has become easier to build extensible applications. In this first post we will briefly explore how core components of standard applications can be customized with CDI @Specializes/@Decorator and more interestingly packaging the customized applications.
Specializes
Specialization in Java EE allows for simple customization of existing beans via inheritance. If a @Specializes bean is found on the classpath it will be injected instead of the original.
The specializing bean has to extend the original bean, it inherits all qualifiers and its name, if defined with @Named.
[java]
@RequestScoped
public class CoreService {
public void doStuff() {
System.out.println("Default implementation is doing Stuff.");
}
}
@Specializes
public class MySpezializedService extends CoreService {
@Override
public void doStuff() {
System.out.println("Spezialized implementation is doing Stuff.");
}
}
[/java]
MySpezializedService will be injected instead of MyDefaultService in any place where MyDefaultService has been defined for injection.
This simple inheritance hierarchy, allows you to adapt the core applications functionality as and when required.
Depending on your application and customization strategy, hook methods can be defined in the standard implementation for enhanced customization purposes.
[java]
@RequestScoped
public class FooProcessor {
public void process(Foo foo) {
Bar bar = calculateBar(foo);
…
}
private void calculateStuff(Foo foo) {
foo = preCalculationHook(foo);
…
return postCalculationHook(bar);
}
protected Foo preCalculationHook(Foo foo) {
return foo;
}
protected Foo postCalculationHook(Bar bar) {
return bar;
}
}
@Specializes
public class SpecializedFooProcessor extends FooProcessor {
@Override
public Foo preCalculationHook(Foo foo) {
…
return foo;
}
@Override
public Bar postCalculationHook(Bar bar) {
…
return bar;
}
}
[/java]
Decorator
Decorators allow for simple customization of business logic.
[java]
@Decorator
public abstract class CoreControllorDecorator implements CoreController {
@Inject
@Delegate
private CoreController coreControllor;
@Override
public String execute() {
return "Decorator doing stuff before CoreControllor." + coreControllor.execute();
}
}
[/java]
Decorators may be abstract so that only methods that should be decorated have to be implemented.
Decorators may not extend the decorated managed bean (see CDI-224 for more details), therefor you need a common super type (abstract or interface).
[java]
public interface CoreController {
public String execute();
public String randomMethod();
}
[/java]
It is important to note that decorators are not active by default, just like interceptors they have to be defined in the beans.xml.
[xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans …>
…
<decorators>
<class>com.knitelius.customized.app.web.CoreControllorDecorator</class>
</decorators>
</beans>
[/xml]
Packaging
Sadly there is no “one size fits all” solution in terms of packaging the customization’s, depending on the targeted application server you are deploying to. As Mark Struberg points out in CDI in EARs, CDI bean discovery in EAR files in not specified and varies between application servers.
Ideally the customization’s should be packaged into a separate [simple_tooltip content=’Must include a beans.xml or include beans with bean defining annotations‘]bean-archive compliant*[/simple_tooltip] JAR.
This works fine when customizing EJB-JAR’s, since the customizing bean archive that has a dependency on the EJB-JAR can be packaged into the EAR lib. Avoiding maven cyclic dependency issues.
[text gutter=”false”]
ear
├── lib
| └── customizations.jar
└── core-ejb-jar
└── …
[/text]
However this does not work for WAR’s, in this case you will need to extract all customizable core components into a separate bean archive, to avoiding cyclic dependencies.
[text gutter=”false”]
war
├── …
└── WEB-INF
| └── …
| └── lib
| └── customizable-compontents.jar
| └── customizations.jar
└── …
[/text]
Alternatively Maven overlays offers a more versatile solution for customizing web applications.
In the simplest case it is sufficient to declare a runtime dependency on the core application WAR. Maven will overlay (overwrite or include) all resources in the core application with the once defined in the customized application.
[xml]
<?xml version="1.0" encoding="UTF-8"?>
…
<groupId>com.knitelius</groupId>
<artifactId>CUSTOMIZED-App-Web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>CUSTOMIZED-App-Web</name>
<dependencies>
<!– WAR to be overlayed. –>
<dependency>
<groupId>com.knitelius</groupId>
<artifactId>CORE-App-web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
…
</dependencies>
…
[/xml]
To gain access to the core applications classes for customization a [simple_tooltip content=”the core classes are included by maven overlay in WEB-INF\classes”]provided*[/simple_tooltip] dependency on the core applications classes has to be defined.
[xml]
<?xml version="1.0" encoding="UTF-8"?>
…
<!– Makes CORE classes available for customization. –>
<dependency>
<groupId>com.knitelius</groupId>
<artifactId>CORE-App-web</artifactId>
<classifier>classes</classifier>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
…
</dependencies>
…
[/xml]
In part 2 we will explore how a non-invasive extension mechanism can be implemented for Java EE applications.
Good article.
Question: In which beans.xml do you have to register the Decorator. Do I have to change the beans.xml in the core or can I do this in the customizing project.
Little typo in the Sepcializes section: In the code you use CoreService, in the text MyDefaultService.
Looking forward to part2.
Cheers
Jan
In the customizing project.
* In the case of Maven overlay the costume beans.xml will overwrite the core one.
* When packing the customization’s into a separate JAR, CDI will find any @Decorator or @Interceptors defined in the its beans.xml.