In my JSF application I'm using a @ViewScoped
bean Publication
to show/edit data coming from my database. In that bean there is a field for a subtype-specific data object, i.e. containing a different object depending on whether the publication is, say, a book or an article.
@ViewScoped
@Named
public class Publication implements Serializable {
@Inject
DatabaseStorage storage;
...
String id;
String type;
PublicationType typedStuff;
@PostConstruct
public void init() {
// Get an URL parameter from the request,
// look up row in database accordingly, initialize String "type".
switch (type) {
case "ARTICLE":
typedStuff = new Article(id);
break;
case "BOOK":
typedStuff = new Book(id);
break;
default:
break;
}
}
}
...with classes Article
and Book
that implement / extend PublicationType
.
So far, so good, but I would like for typedStuff
to be a CDI bean, so that I can inject useful resources there.
I've read this and this page on producer methods, as well as this tutorial and this very related SO question, but none of them answer precisely my question: Can I inject based on a field that the injecting bean itself only knows at runtime?
I've gotten the producer method to work as such, but I can't parametrize it, so I can't get that switch
to work.
If I put the producer method in a separate class (or bean) then I don't have access to the type
field.
If I inject the injecting bean into the producer class, or move the producer method into the injecting class, I get a circular injection.
If I put the producer method statically into the injecting class, I also don't have access, because type
cannot be static. (Although, since it's only used momentarily...?)
Also (and that is probably the answer right there), the producer method is executed before my injecting bean's init method, so type
wouldn't even have been set yet.
Does anybody have a better idea?
No you cannot, but you can select a bean based on the field value. Say:
public interface PublicationType {}
@PType("ARTICLE")
public class Article implements PublicationType{}
@PType("BOOK")
public class Book implements PublicationType {}
And define a qualifier:
public @interface PType {
String value();
}
And define an AnnotationLiteral
:
public class PTypeLiteral extends AnnotationLiteral<PType> implements PType {}
Then you can use:
public class Publication {
@Any
@Inject
private Instance<PublicationType> publicationTypes;
public void doSomething() {
PType ptype = new PTypeLiteral(type);
// Of course you will have to handle all the kind of exceptions here.
PublicationType publicationType = publicationTypes.select(ptype).get();
}
}