My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

luni, 10 august 2015

JSF and Factory pattern (CDI aspects) - part II

Read also: JSF and Factory pattern - part I

We can use CDI in JSF, so we can consider the below example the simplest factory method pattern implementation. Basically, the Java EE annotations and dependency injection helps us to simply the things pretty much:

public class Foo {

 @Produces
 public String sayHelloFoo() {
  return "Foo says hello !";
 }
}

@Named
@RequestScoped
public class MyBean {

 @Inject
 private String foo;  
   
 public void fooSays(){
  System.out.println(foo);
 }
}

The sayHelloFoo() is known as a producer method and in this case it produces a String. The "beneficiary" of this String is MyBean, which practically injects this type via @Inject as above. CDI knows where to inject the produced String because the produced type is the same as the injected type.

But, a producer method can produce anything else, like primitive data types or objects. For example, let's suppose that we have the Foo object:

public class Foo {

 public String sayHelloFoo() {
  return "Foo says hello !";
 }
}

Now, the producer will produce Foo instances instead of String:

public class FooProducer {

 @Produces
 public Foo fooFactory() {
  return new Foo();
 }    
}

And MyBean will inject Foo type (with other words, the container is injecting the beans created by the factory using the @Inject annotation):

@Named
@RequestScoped
public class MyBean {

 @Inject
 private Foo foo;  
   
 public void fooSays(){
  System.out.println(foo.sayHelloFoo());
 }
}

But, this example will produce an error of type: WELD-001409: Ambiguous dependencies for type Foo with qualifiers @Default. Remember that we said that CDI relies on types to determine where to inject the produced type, but in this case we have two instances of the Foo:

Possible dependencies:
 - Producer Method [Foo] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces public beans.FooBuzzProducer.fooFactory()],
 - Managed Bean [class beans.Foo] with qualifiers [@Any @Default],

So, the container doesn't know which instance to inject! This is causing an ambiguity!

The solution relies on CDI qualifiers. The qualifiers have the power to disambiguate the beans. For this, we use the @Qualifier and @interface annotations, as below (further reading about qualifiers: Create Qualifiers for CDI Beans and Using Qualifiers):

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD})
public @interface FooQualifier {
}

Further, the qualifier is used to annotate the producer method:

public class FooProducer {

 @Produces @FooQualifier
 public Foo fooFactory() {
  return new Foo();
 }    
}

And the injection points also:

@Named
@RequestScoped
public class MyBean {

 @Inject @FooQualifier
 private Foo foo;  
   
 public void fooSays(){
  System.out.println(foo.sayHelloFoo());
 }
}

Now, let's have an example with a producer for two types, Foo and Buzz.

public class Foo {

 public String sayHelloFoo() {
  return "Foo says hello !";
 }
}

public class Buzz {

 public String sayHelloBuzz() {
  return "Buzz says hello !";
 }
}

For each of them we need a qualifier:

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD})
public @interface FooQualifier {
}

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD})
public @interface BuzzQualifier {
}

Now, we have a producer for Foo and Buzz:

public class FooBuzzProducer {

 @Produces @FooQualifier
 public Foo fooFactory() {
  return new Foo();
 }
   
 @Produces @BuzzQualifier
 public Buzz buzzFactory() {
  return new Buzz();
 }
}

And, finally exploit the factory in MyBean:

@Named
@RequestScoped
public class MyBean {

 @Inject @FooQualifier
 private Foo foo;
   
 @Inject @BuzzQualifier
 private Buzz buzz;
   
 public void fooSays(){
  System.out.println(foo.sayHelloFoo());
 }
   
 public void buzzSays(){
  System.out.println(buzz.sayHelloBuzz());
 }
}

Use @Any and factory pattern

Further, let's complicate the things and let's suppose that we have the following interface:

public interface CharacterType {

 public String sayHello();
}

Think that you have several implementations of this interface (e.g. Foo, Buzz).  Based on the above examples, you can implement the factory pattern by writing a producer method for each implementation. This way you will obtain a pretty clumsy to maintain code because adding a new implementation will involve adding a new producer, removing an existing implementation will require to remove the producer and so on. Java EE comes to rescue this situation via @Any annotation, which allows an injection point to refer to all beans or all events of a certain bean type. With other words, it will allows us to instruct the container that all beans implementing the given interface (e.g. CharacterType) should be injected at that injection point. Further, you can use a simple trick to distinguish between the injected beans. This trick take usage of annotation literals and enum types. First, we define a qualifier that uses an enum of character types:

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface CharacterQualifier {

 Type value();
 enum Type {
  FOO, BUZZ
 }
}

Next, each CharacterType implementation is annotated with this qualifier and the corresponding type:

@CharacterQualifier(CharacterQualifier.Type.FOO)
@Dependent
public class Foo implements CharacterType {

 @Override
 public String sayHello() {
  return "Foo says hello !";
 }
}

@CharacterQualifier(CharacterQualifier.Type.BUZZ)
@Dependent
public class Buzz implements CharacterType {

 @Override
 public String sayHello() {
  return "Buzz says hello !";
 }
}

The next step consist in creating an AnnotationLiteral.  In order to select the dependency you need to compare it with the enum type (FOO, BUZZ) of the qualifier(CharacterQualifier)  of each implementation by creating an AnnotationLiteral of the corresponding type:

@SuppressWarnings("AnnotationAsSuperInterface")
public class CharacterLiteral extends AnnotationLiteral<CharacterQualifier> implements CharacterQualifier {

 private static final long serialVersionUID = 1L;
 private final Type type;

 public CharacterLiteral(Type type) {
  this.type = type;
 }

 @Override
 public Type value() {
  return type;
 }
}

Now the factory class is pretty simple:

@Dependent
public class CharactersFactory {

 @Inject @Any
 private Instance<CharacterType> characters;

 public CharacterType getCharacter(CharacterQualifier.Type type) {
  CharacterLiteral characterLiteral = new CharacterLiteral(type);
  Instance<CharacterType> characterType = characters.select(characterLiteral);
  return characterType.get();
 }
}

The client of our factory needs to inject this factory and invoke the getCharacter() method. Of course, the client needs to indicate the character type (e.g. BUZZ type):

@Named
@RequestScoped
public class MyBean {

 @Inject
 CharactersFactory factory;

 public void characterSays() {
  CharacterType buzz = factory.getCharacter(CharacterQualifier.Type.BUZZ);
  System.out.println(buzz.sayHello());
 }
}

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors