I wanted to introduce CDI (Weld) to our project and now having some trouble with objects that are manually constructed.
So we have some classes implementing the IReport
interface, which have a field that should be injected. This is null at runtime because all of those classes are being generated by the ReportFactory
in a class ReportController
.
private Map<String,Object> generateReport(ReportInfo ri, ...) {
// some input validation
IReport report = ReportControllerFactory.getReportInstance( ri.getClassName() );
// ...
}
I am aware that I can use the @Produces
annotation together with another custom annotation in the ReportControllerFactory
, but how do I use the @Inject
for a variable that can only be created, after some validation was done, inside a method? And how would I submit the parameter ri.getClassName()
? The object ri
is not known when the ReportController
is constructed.
Thank you very much!
Kind regards, Sebastian
Edit at Jul 08, 2011 (10:00):
ReportFactory class:
public static IReport getReportInstance( String className ) throws ReportException {
IReport report = null;
try {
Class<?> clazz = Class.forName( className );
report = (IReport) clazz.newInstance();
}
catch ( Exception e ) { … }
return report;
}
Edit 2 (Selection of the right report implementation)
The report instance is selected by some paths that go from the JSF frontend to the ReportController. The ManagedBean calls a session bean, which has several methods, depending on which button was pressed where. All those methods set the report name and call the more generic method sendOrGetReport
. This method selects the unique key for the specified report from the database and decides whether to send an e-mail or immediately deliver the report. Let's assume it should be delivered.
Then the ReportController
comes into play. He fetches a ReportInfo
object based upon the unique key and other information provided by the methods above and calls the ReportFactory
to create a report of type ri.getClassName()
.
Fancy, huh? I think this whole section might need some refactoring. If you don't see any easy spot, I skip the @Inject
in the report implementation and do a JDNI lookup for that resource.
The idea behind CDI (and other DI-frameworks) in order to manage dependencies is to take over control of the managed beans lifecycle. This implies - among other things - that you cannot interfere with the creation of managed beans if you want it to be managed by the container.
Certainly this does not mean that your scenario is unsolvable, you just have to change your perspective a bit ;-)
The idea is to work with managed beans (obviously), but let your own logic decide which instance of all available instances is the correct.
...
@Inject Instance<IReport> availableReports;
...
@Produces
public IReport createReport() {
IReport result;
for (IReport report: availableReports) {
// choose correct instance, you might want to query the injection
// point or any attached qualifier a bit more in order to
// determine which is the correct instance
if ...
result = report;
...
}
return result;
}
...
With as many beans of beantype IReport as you like
public class AReport implements IReport {
...
@Inject
...
}
public class BReport implements IReport {
...
@Inject
...
}
And an automagical usage like this
public class MyStuff {
...
@Inject
IReport myReport;
...
}
See here and here for more information.
If I did not misunderstand your problem, this should bring you forward - feel free to post further questions / comments.
UPDATE:
Everything might be just dead simple if something like this fits your requirements:
@AReport
public class AReport implements IReport {
...
@Inject
...
}
@BReport
public class BReport implements IReport {
...
@Inject
...
}
With the usage like this
public class MyStuff {
...
@Inject
@AReport
IReport myAReport;
...
@Inject
@BReport
IReport myBReport;
...
}