Reputation: 13
I want to implement a painting service using Java EE and JBoss Application Server.
Imagine a method called via an URL
http://mypaint.com/apply?image=1&action=line&from=10,10&to=100,10
which applies an action to the image with the id 1. The action is “draw a line from point (10,10) to point (100,10)”.
The “apply” method looks like this:
@Inject
private ImageProcessorServer processors
@GET
@Path("/apply")
public Response apply(
@QueryParam(value = "image") int imageId,
@QueryParam(value = "action") String action,
//some parameters more...
) {
… //check if user is allowed to access the image
ImageProcessor processor = processors.get(imageId);
//get image processor by image id
processor.apply(action/*, from, to*/);
}
The “ImageProcessorServer” looks like this:
@Singleton
@Startup
public class ImageProcessorServer {
private Map<Integer, ImageProcessor> processors =
new HashMap<Integer, ImageProcessor>();
@Lock(LockType.WRITE)
public ImageProcessor get(int imageId) {
ImageProcessor processor = processors.get(imageId);
if(processor == null) {
processor = new ImageProcessor(imageId);
processors.put(imageId, processor);
}
return processor;
}
}
It’s a singleton to make sure, that only one ImageProcessor per Image is generated (generation under mutual exclusion → Write-Lock).
Now the Problem: How can I inject my Data Access Objects (for manipulating my database) in my ImageProcessor class? DAOs are simple stateless beans. My ImageProcessor should look like this:
public class ImageProcessor {
@Inject
private ActionDao actionDao;
private Image image;
public ImageProcessor(int imageId) { … }
public void apply(String action, ...) {
//change image
//actionDao.persist(actionObject)
}
}
But this does not work. The ActionDao is NULL.
My current solution is to pass the DAOs as parameter in every method that is using DAOs, like this:
public ImageProcessor(int imageId, ImageDao dao) { … }
public void apply(String action, …, ActionDao dao, ImageDao dao2, …) {
//change image
//dao.persist(actionObject)
}
It is important that the requests with the same image id share the same image processor. A client can have multiple requests with different image ids.
Under this link Using Dependency Injection in POJO's to inject EJB's is said that one can use a factory. But I don’t know how to work this out. Can someone provide code for this?
Does someone know an elegant way to solve my problem?
Upvotes: 0
Views: 1664
Reputation: 4406
I could not find a way to use producer methods because the EJB is singleton and all injections will happen once in EJB creation time.
First of all Add a qualifier annotation : @ImageProcessor
Then define a producer method:
@produces @ImageProcessor
public ImageProcessor getImageProcessor(){
long imageId = // Get your image id from request parameter map
ImageProcessor processor = new ImageProcessor(imageId);
return processor;
}
and then use if you use injection in your singleton it will be injected once for all images because your EJB is Singleton!
@Inject @ImageProcessor
private ImageProcessor ip;
So for your use case this is not a good approach unless you use manual look up in EJB again but another problem would be threading problem in producer method!
Upvotes: 0
Reputation: 4406
In your case you can manually lookup your bean. to do so you can add an static util method to get the bean manager :
public static BeanManager getBeanManager()
{
try{
InitialContext initialContext = new InitialContext();
return (BeanManager) initialContext.lookup("java:comp/BeanManager");
catch (NamingException e) {
log.error("Couldn't get BeanManager through JNDI");
return null;
}
}
and then you can manually lookup your bean in singleton EJB, then all injections will happen in your bean:
public ImageProcessor getFacade()
{
BeanManager bm = getBeanManager();
Bean<ImageProcessor> bean = (Bean<ImageProcessor>) bm.getBeans(ImageProcessor.class).iterator().next();
CreationalContext<ImageProcessor> ctx = bm.createCreationalContext(bean);
ImageProcessor ip= (ImageProcessor) bm.getReference(bean, ImageProcessor.class, ctx); // this could be inlined, but intentionally left this way
return ip;
}
after looking up the bean set the image id using a setter method and then put it in your map.
Try to make a util class for manually looking for beans, then you can reuse it in whole project.
Also Seam3, Myface CODI and DeltaSpike have some utilities to do these routine processes.
Fore more info take a look at this sample.
Upvotes: 1
Reputation: 2145
For a field to be injected, the bean must be created by the CDI framework.
I can find several way "CDI" way to create the ImageProcessor
but as there is presidency between the scope of the imageId and the processor, it won't look good. (FYI bean that need initialization should be created via @Producer
annotated method)
The simplest way to solve the issue is to inject the DAO in the singleton (as it is stateless is shouldn't be an issue) and provide it to the processor via the construcor (that will set the DAO field). This is I think the cleanses way to do it, you will keep the ImageProcessor
creation limited to the singleton)
Upvotes: 0