TLDR: CDI isn't behaving as I would expect after migrating a web profile sample to a full blown EE application
Let me start off by saying that I'm relatively new to CDI. I like to think that I understand the concept but the nuances of the implementation are yet to be conquered.
I'm trying to EJB-ify and subsequently wrap with a RESTful API Java Batch (JSR-352).
To start this off, I've taken the javaee7 sample for Payroll and pulled the logic and job specifications into a full blown EE7 project (ejb, web, ear modules).
When I call the RESTful service I initially run into this problem (shortened to save sanity):
WARNING: Caught exception executing step: java.lang.RuntimeException: com.ibm.jbatch.container.exception.BatchContainerRuntimeException: Tried but failed to load artifact with id: SimpleItemProcessor
Caused by: com.ibm.jbatch.container.exception.BatchContainerRuntimeException: Tried but failed to load artifact with id: SimpleItemProcessor
Caused by: java.lang.ClassNotFoundException: SimpleItemProcessor
Please bear in mind that this part of the application should have seen roughly no changes. SimpleItemProcessor definition is identical and shown below.
@Named("SimpleItemProcessor")
public class SimpleItemProcessor
implements ItemProcessor {
@Inject
private JobContext jobContext;
public Object processItem(Object obj) throws Exception {
Properties jobParameters = BatchRuntime.getJobOperator().getParameters(jobContext.getExecutionId());
PayrollInputRecord inputRecord = (PayrollInputRecord) obj;
PayrollRecord payrollRecord = new PayrollRecord();
payrollRecord.setMonthYear((String) jobParameters.get("monthYear"));
int base = inputRecord.getBaseSalary();
float tax = base * 27 / 100.0f;
float bonus = base * 15 / 100.0f;
payrollRecord.setEmpID(inputRecord.getId());
payrollRecord.setBase(base);
payrollRecord.setTax(tax);
payrollRecord.setBonus(bonus);
payrollRecord.setNet(base + bonus - tax);
return payrollRecord;
}
}
This SimpleItemProcessor is referred to by it's CDI Name inside the PayrollJob.xml definition (which the Batch environment is clearly finding). This specification is also a carbon copy of the working JavaEE7 example.
<job id="payroll" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="process">
<chunk item-count="3">
<reader ref="SimpleItemReader"></reader>
<processor ref="SimpleItemProcessor"></processor>
<writer ref="SimpleItemWriter"></writer>
</chunk>
</step>
If I use the fully qualified name of SimpleItemProcessor (com.mycompany.SimpleItemProcessor) it seems to work. The other two batch components (Reader and Writer) don't seem to need this.
My guess is that I'm missing something in CDI land.
This is all being run on Glassfish 4.
I have empty beans.xml's in both WEB-INF of my web module and META-INF of my EJB module.
Please give me any insight you might have on this. I've been churning for quite awhile.
Edit: Increased logging for the Batch component throwing the exception and got some interesting stuff pointing at something maybe CDI related.
FINER: ENTRY Loading batch artifact id = SimpleItemReader
FINER: Delegating to preferred artifact factorycom.ibm.jbatch.container.services.impl.CDIBatchArtifactFactoryImpl@4ac13bc2
FINER: ENTRY Loading batch artifact id = SimpleItemReader
FINER: RETURN For batch artifact id = SimpleItemReader, loaded artifact instance: com.mycompany.SimpleItemReader@4862cec7 of type: com.mycompany.SimpleItemReader
FINER: ENTRY Loading batch artifact id = SimpleItemProcessor
FINER: Delegating to preferred artifact factorycom.ibm.jbatch.container.services.impl.CDIBatchArtifactFactoryImpl@4ac13bc2
FINER: ENTRY Loading batch artifact id = SimpleItemProcessor
FINE: Tried but failed to load artifact with id: SimpleItemProcessor, Exception = java.util.NoSuchElementException
FINER: RETURN For batch artifact id = SimpleItemProcessor, FAILED to load artifact instance
FINER: Preferred artifact factory failed to load artifact SimpleItemProcessor. Defaulting to batch.xml.
FINE: TCCL = EarClassLoader :
CDI portion of the implementation that's failing (bm.getBeans(id) is coming back as empty):
51 private Object More ...getArtifactById(String id) {
52
53 Object artifactInstance = null;
54
55 try {
56 InitialContext initialContext = new InitialContext();
57 BeanManager bm = (BeanManager) initialContext.lookup("java:comp/BeanManager");
58 Bean bean = bm.getBeans(id).iterator().next();
59 Class clazz = bean.getBeanClass();
60 artifactInstance = bm.getReference(bean, clazz, bm.createCreationalContext(bean));
61 } catch (Exception e) {
62 // Don't throw an exception but simply return null;
63 logger.fine("Tried but failed to load artifact with id: " + id + ", Exception = " + e);
64 }
65
66 return artifactInstance;
67 }
Here is the same logging for the working web profile example:
FINER: ENTRY
FINER: No preferred job xml loader is detected in configuration
FINER: Preferred job xml loader failed to load PayrollJob. Defaulting to META-INF/batch-jobs/
FINER: Loaded job xml with PayrollJob from META-INF/batch-jobs/
FINER: ENTRY Loading batch artifact id = SimpleItemReader
FINER: Delegating to preferred artifact factorycom.ibm.jbatch.container.services.impl.CDIBatchArtifactFactoryImpl@4ac13bc2
FINER: ENTRY Loading batch artifact id = SimpleItemReader
FINER: RETURN For batch artifact id = SimpleItemReader, loaded artifact instance: com.oracle.javaee7.samples.batch.api.SimpleItemReader@753908c of type: com.oracle.javaee7.samples.batch.api.SimpleItemReader
FINER: ENTRY Loading batch artifact id = SimpleItemProcessor
FINER: Delegating to preferred artifact factorycom.ibm.jbatch.container.services.impl.CDIBatchArtifactFactoryImpl@4ac13bc2
FINER: ENTRY Loading batch artifact id = SimpleItemProcessor
FINER: RETURN For batch artifact id = SimpleItemProcessor, loaded artifact instance: com.oracle.javaee7.samples.batch.api.SimpleItemProcessor@5376b860 of type: com.oracle.javaee7.samples.batch.api.SimpleItemProcessor
FINER: ENTRY Loading batch artifact id = SimpleItemWriter
FINER: Delegating to preferred artifact factorycom.ibm.jbatch.container.services.impl.CDIBatchArtifactFactoryImpl@4ac13bc2
FINER: ENTRY Loading batch artifact id = SimpleItemWriter
FINER: RETURN For batch artifact id = SimpleItemWriter, loaded artifact instance: com.oracle.javaee7.samples.batch.api.SimpleItemWriter@243e016c of type: com.oracle.javaee7.samples.batch.api.SimpleItemWriter
A recent search directed me here so let me give a partial answer in spite of the age of the question.
First, for background do a quick read on CDI's "bean discovery mode", e.g. see here.
Now, the presence of an empty beans.xml at the 1.0 XSD level will be treated by CDI as ALL mode, while the absence of beans.xml altogether will be treated as the default ANNOTATED mode.
The batch artifacts are not particularly special to CDI. So like any classes, they will only be treated as CDI managed beans and will only be accessible by bean name (the @Named
value) when either:
@Dependent
is indeed a bean-defining annotation).My guess is that somehow in your repackaging, the beans.xml was no longer detected by CDI, so you effectively moved from ALL to ANNOTATED mode, requiring the @Dependent
bean-defining annotation to load the batch artifact by bean name.
(Why would this be? The OP didn't go into that much detail and has well moved on by now I'm sure...so doesn't matter, the rest of the answer I think is still valuable).
Hopefully that helps someone still !