Performing Java EE resource injections dynamically

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Performing Java EE resource injections dynamically

mitya
Hi all,

I'm working on a project that is meant to bring CDI injections to
server-side JavaScript. There are similarities with Undertow.js [1],
but the project I'm working on has more generic nature, wider scope and
vendor-neutral character. From the very inception it was clear that
TomEE should be among the supported platforms; I am myself a long-time
Tomcat/TomEE user and fan.

You can find the project's write-up here: https://gist.github.com/dtele
guin/c93fe4a4c666234729d8

The cornerstone of the project should be the mechanism that would allow
to resolve JavaEE resource injections (@PersistenceContext, @Resource,
@EJB etc.) dynamically at runtime.

The first attempt was to generate on-the-fly a class with annotated
fields and instantiate it via javax.enterprise.inject.spi.Unmanaged.
This worked perfectly in WildFly, but failed in TomEE and GlassFish.
(However, simple @Inject worked everywhere.) See this StackOverflow
thread [2] for details and code.

I've spent some time studying TomEE/OpenEJB internals just to learn
that probably it wouldn't be that easy to implement the same in TomEE.
Is it correct that TomEE scans web application on deployment, creates
JNDI entries for all the detected injection points, and "injecting" is
simply wiring up the fields to corresponding JNDI entries?

With the said, how can I achieve what I want in TomEE? Is it possible
to trigger an on-demand JNDI resource creation and wiring?

Most likely some TomEE-specific code will be needed, or a privileged
context with ContainerServlet, or a custom valve; I'm OK with all the
above (however, of course, the platform-independent code would be
preferred). In fact, even generating a class may be redundant. Let's
describe it this way, I have a set of injection definitions like this:

@Annotation(param = "value") @Qualifier1 @Qualifier2 ... Type name;

where Annotation is one of Inject, PersistenceContext, Resource, EJB
etc. These definitions become available only at runtime. Finally, for
each definition I want to obtain an instance that would have been
injected if this were happening inside a full-featured CDI managed
bean.

Thanks in advance!
And long live the "technology that kicks ass" (C) :)

Dimitri

[1] http://wildfly.org/news/2015/11/02/Undertow/
[2] http://stackoverflow.com/questions/36239250/injecting-java-ee-resou
rces-into-dynamically-loaded-classes
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya
Sorry for broken links. Shouldn't have used line wrapping in my mail client.

Project write-up:
https://gist.github.com/dteleguin/c93fe4a4c666234729d8

StackOverflow thread:
http://stackoverflow.com/questions/36239250/injecting-java-ee-resources-into-dynamically-loaded-classes
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

Romain Manni-Bucau
Hi Dimitri

CDI injections are created - more validated actually - at startup and dont
rely on JNDI at all. EE injections rely on JNDI and are linked to CDI
through a particular bean (Comp) representing the whole webapp JNDI tree.

In term of code an InjectionTarget should work but needs to be there at
startup.

That said: why not using es6 and or purescript/typescript to get real
imports and potentially annotations/decorators and just convert it to java
and then wire it through websocket? Sounds like a simple and safe impl to
me to do that.
Le 29 mars 2016 01:48, "Dimitri" <[hidden email]> a écrit :

> Sorry for broken links. Shouldn't have used line wrapping in my mail
> client.
>
> Project write-up:
> https://gist.github.com/dteleguin/c93fe4a4c666234729d8
>
> StackOverflow thread:
>
> http://stackoverflow.com/questions/36239250/injecting-java-ee-resources-into-dynamically-loaded-classes
>
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya
Hi Romain,

> CDI injections are created - more validated actually - at startup and dont
> rely on JNDI at all. EE injections rely on JNDI and are linked to CDI
> through a particular bean (Comp) representing the whole webapp JNDI tree.
>
> In term of code an InjectionTarget should work but needs to be there at
> startup.

Does it mean that if I somehow manage to (dynamically) plant the
necessary entries to a JNDI tree, everything would work like in
WildFly? (InjectionTarget is what's used under the hood in Unmanaged)

> That said: why not using es6 and or purescript/typescript to get real
> imports and potentially annotations/decorators and just convert it to java

Did you mean "to JavaScript"? That would involve heavy-weight machinery
like Babel/Traceur that requires Node.js, and running Node code in
Nashorn is a non-trivial task (though I know the guy who currently
works on that). It's an interesting challenge, but I think I'd better
start with a simpler (ad-hoc) prototype.

> and then wire it through websocket? Sounds like a simple and safe impl to
> me to do that.

Sorry, can't understand how it would solve a problem of dynamic EE
injections. Could you please elaborate?

Thx! Dimitri

> Le 29 mars 2016 01:48, "Dimitri" <[hidden email]> a écrit :
>
> >
> > Sorry for broken links. Shouldn't have used line wrapping in my mail
> > client.
> >
> > Project write-up:
> > https://gist.github.com/dteleguin/c93fe4a4c666234729d8
> >
> > StackOverflow thread:
> >
> > http://stackoverflow.com/questions/36239250/injecting-java-ee-resources-into-dynamically-loaded-classes
> >
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

Romain Manni-Bucau
2016-03-29 14:32 GMT+02:00 Dimitri <[hidden email]>:

> Hi Romain,
>
>> CDI injections are created - more validated actually - at startup and dont
>> rely on JNDI at all. EE injections rely on JNDI and are linked to CDI
>> through a particular bean (Comp) representing the whole webapp JNDI tree.
>>
>> In term of code an InjectionTarget should work but needs to be there at
>> startup.
>
> Does it mean that if I somehow manage to (dynamically) plant the
> necessary entries to a JNDI tree, everything would work like in
> WildFly? (InjectionTarget is what's used under the hood in Unmanaged)
>

if JNDI is the JNDI tree of Comp bean (a kinf of EJB with its own JNDI
tree which is a merge of all trees to make it simple) yes

>> That said: why not using es6 and or purescript/typescript to get real
>> imports and potentially annotations/decorators and just convert it to java
>
> Did you mean "to JavaScript"? That would involve heavy-weight machinery
> like Babel/Traceur that requires Node.js, and running Node code in
> Nashorn is a non-trivial task (though I know the guy who currently
> works on that). It's an interesting challenge, but I think I'd better
> start with a simpler (ad-hoc) prototype.
>

Well babel is trivial to run - at least in version 5:
https://github.com/rmannibucau/ohmyjs

Now I thought to that cause that's what give a language a java
developer will not reject immediately I think. es5 is still not
tempting for a java developer cause of the scope plus adding
injections in comments is another reason to not accept this I think.
That said it is your choice and I can be totally wrong on that, no
issue ;).

>> and then wire it through websocket? Sounds like a simple and safe impl to
>> me to do that.
>
> Sorry, can't understand how it would solve a problem of dynamic EE
> injections. Could you please elaborate?

Here is the (lazy?) workflow I would have implemented if I would have
had it to do:

1. convert the js to java - just in term of EE contract

ex:

/**
@import org.apache.commons.configuration.Configuration
@import java.util.logging.Level

@Startup(order = 1)
@Inject Configuration config
*/

$LOG.log(Level.INFO, "Starting MyApplication {0} b{1}", [
        config.getString("myapp.version"),
        config.getString("myapp.build")
    ]);

would be

public class MyJsEnv {
      @Inject Configuration config;
      // all injections even @Resource, @PersistenceContext etc
}

2. I would have impl-ed a websocket endpoint getting MyJsEnv injected
and mapping client operations to server operations

This is actually pretty close to angularbeans:
http://bessemhmidi.github.io/AngularBeans/ but without the need to
recreate a server side API


This is for remoting - maybe not what you want to achieve.

A common alternative for 100% server usage is to simply name beans and
use them as bindings in the script engine. With the BeanManager it is
pretty easy to implement a Binding to do so (something like
https://github.com/apache/tomee/blob/120a33c7b4de07ae01c17978ea37d88a911ea860/container/openejb-core/src/main/java/org/apache/openejb/util/OpenEJBScripter.java#L104
but more integrate to JSR 223)

>
> Thx! Dimitri
>
>> Le 29 mars 2016 01:48, "Dimitri" <[hidden email]> a écrit :
>>
>> >
>> > Sorry for broken links. Shouldn't have used line wrapping in my mail
>> > client.
>> >
>> > Project write-up:
>> > https://gist.github.com/dteleguin/c93fe4a4c666234729d8
>> >
>> > StackOverflow thread:
>> >
>> > http://stackoverflow.com/questions/36239250/injecting-java-ee-resources-into-dynamically-loaded-classes
>> >
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya
Hi,

> > Does it mean that if I somehow manage to (dynamically) plant the
> > necessary entries to a JNDI tree, everything would work like in
> > WildFly? (InjectionTarget is what's used under the hood in
> > Unmanaged)
> >
> if JNDI is the JNDI tree of Comp bean (a kinf of EJB with its own
> JNDI
> tree which is a merge of all trees to make it simple) yes

Sounds cool, but how do I obtain an instance to plant? For example,
after parsing a script (at runtime!) I know that it requires a
@PersistenceContext with particular name, as well as some @Resource's
(probably also named), some @EJB's and some @WebServiceRef's. Is there
a "magic" method in OpenEJB that I can call this way: "okay, my newly-
loaded component needs this EE stuff, please instantiate it all for me
(or return reference if already instantiated)"?

Of course I could write my own handlers for each annotation type like
this:
- in case of @Resource -> look in JNDI
- in case of @EJB -> the same
- in case of @PersistenceContext/@PersistenceUnit -> instantiate (how
exactly?)
- in case of @WebServiceRef -> ???

But I would like to avoid it, since all of it is already implemented in
OpenEJB either way. 

> >
> Well babel is trivial to run - at least in version 5:
> https://github.com/rmannibucau/ohmyjs

Looks really cool - didn't know running Babel on Nashorn would be that
easy! It opens new horizons for me :) In fact, I wouldn't say I'm 100%
happy with ES5. Nashorn was chosen as a first candidate because of its
maturity, performance & support from Oracle; but nothing would prevent
from using any JSR-223 compliant engine.

>
> Now I thought to that cause that's what give a language a java
> developer will not reject immediately I think. es5 is still not
> tempting for a java developer cause of the scope plus adding
> injections in comments is another reason to not accept this I think.

Neither am I 100% happy with injections-in-comments. I was also
thinking about external (XML/JSON) metadata that would come in pair
with a script. E.g., a script itself could come in foo.js, and its
metadata in foo.{xml|json|etc.} The latter would contain
summary/description, imports, dependencies etc. - all the same, but in
a structured format.

I was examining ES7 decorators, too. Yes, they could be mapped to Java-
style annotations; the problem is, decorators are applied to JS
classes/methods/properties only. This will force users to
unconditionally wrap their JavaScript code in a class, which seems
reduntant to me. After all, there are a lot of methods to supply script
metadata, with JSDoc-style annotations being just a first try.

> 1. convert the js to java - just in term of EE contract
>
> ex:
>
> /**
> @import org.apache.commons.configuration.Configuration
> @import java.util.logging.Level
>
> @Startup(order = 1)
> @Inject Configuration config
> */
>
> $LOG.log(Level.INFO, "Starting MyApplication {0} b{1}", [
>         config.getString("myapp.version"),
>         config.getString("myapp.build")
>     ]);
>
> would be
>
> public class MyJsEnv {
>       @Inject Configuration config;
>       // all injections even @Resource, @PersistenceContext etc
> }
>
> 2. I would have impl-ed a websocket endpoint getting MyJsEnv injected
> and mapping client operations to server operations

This is very similar to what I've tried in the very beginning. Problem
is, in TomEE this works only statically, meaning that MyJsEnv should be
available at application deployment (pre-generated, compiled and
bundled in a WAR). If I generate/load such a class in runtime, EE
injections won't be processed (contrary to WildFly, which in this case
correctly processes both @Inject and @Resource/@EJB/@Persistence* etc).

> A common alternative for 100% server usage is to simply name beans and
> use them as bindings in the script engine. With the BeanManager it is
> pretty easy to implement a Binding to do so (something like
> https://github.com/apache/tomee/blob/120a33c7b4de07ae01c17978ea37d88a911ea860/container/openejb-core/src/main/java/org/apache/openejb/util/OpenEJBScripter.java#L104
> but more integrate to JSR 223)

To help me understand better, could you please briefly explain what
OpenEJBScripter is? Are there any examples of scripts for it? Are they
able to access applications' PersistenceUnits/PersistenceContexts,
UserTransactions etc.?

Thx! Dimitri

Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

Romain Manni-Bucau
2016-03-29 20:53 GMT+02:00 Dimitri <[hidden email]>:

> Hi,
>
>> > Does it mean that if I somehow manage to (dynamically) plant the
>> > necessary entries to a JNDI tree, everything would work like in
>> > WildFly? (InjectionTarget is what's used under the hood in
>> > Unmanaged)
>> >
>> if JNDI is the JNDI tree of Comp bean (a kinf of EJB with its own
>> JNDI
>> tree which is a merge of all trees to make it simple) yes
>
> Sounds cool, but how do I obtain an instance to plant? For example,
> after parsing a script (at runtime!) I know that it requires a
> @PersistenceContext with particular name, as well as some @Resource's
> (probably also named), some @EJB's and some @WebServiceRef's. Is there
> a "magic" method in OpenEJB that I can call this way: "okay, my newly-
> loaded component needs this EE stuff, please instantiate it all for me
> (or return reference if already instantiated)"?
>

You can rely on deep internals but I wouldn't recommand it.

Side note: it breaks by design JavaEE since you have passed the
startup which validates as much as possible the application and any
runtime change later on can surprisingly break the application (that's
why hot reloading is a good bad idea and can break after a restart
even if "F5" tests were green ;)).

> Of course I could write my own handlers for each annotation type like
> this:
> - in case of @Resource -> look in JNDI
> - in case of @EJB -> the same
> - in case of @PersistenceContext/@PersistenceUnit -> instantiate (how
> exactly?)

there are in global JNDI tree under a name you can find browsing the
internal tree (but same note: this is not portable and we can break it
without any notice if we need).

> - in case of @WebServiceRef -> ???
>

This one is easier since it is a plain webservice client, only
container config can be missing since it is not on the annotation.

> But I would like to avoid it, since all of it is already implemented in
> OpenEJB either way.
>
>> >
>> Well babel is trivial to run - at least in version 5:
>> https://github.com/rmannibucau/ohmyjs
>
> Looks really cool - didn't know running Babel on Nashorn would be that
> easy! It opens new horizons for me :) In fact, I wouldn't say I'm 100%
> happy with ES5. Nashorn was chosen as a first candidate because of its
> maturity, performance & support from Oracle; but nothing would prevent
> from using any JSR-223 compliant engine.
>

Nashorn is not that fast to be honest - even if good enough generally.
It still interprets a lot of relies on thread locals which can easily
breaks any app, in particular starting to use reactive patterns of
just EJB @Asynchronous :s - check GlobalContext ;).

>>
>> Now I thought to that cause that's what give a language a java
>> developer will not reject immediately I think. es5 is still not
>> tempting for a java developer cause of the scope plus adding
>> injections in comments is another reason to not accept this I think.
>
> Neither am I 100% happy with injections-in-comments. I was also
> thinking about external (XML/JSON) metadata that would come in pair
> with a script. E.g., a script itself could come in foo.js, and its
> metadata in foo.{xml|json|etc.} The latter would contain
> summary/description, imports, dependencies etc. - all the same, but in
> a structured format.
>
> I was examining ES7 decorators, too. Yes, they could be mapped to Java-
> style annotations; the problem is, decorators are applied to JS
> classes/methods/properties only. This will force users to
> unconditionally wrap their JavaScript code in a class, which seems
> reduntant to me. After all, there are a lot of methods to supply script
> metadata, with JSDoc-style annotations being just a first try.
>
>> 1. convert the js to java - just in term of EE contract
>>
>> ex:
>>
>> /**
>> @import org.apache.commons.configuration.Configuration
>> @import java.util.logging.Level
>>
>> @Startup(order = 1)
>> @Inject Configuration config
>> */
>>
>> $LOG.log(Level.INFO, "Starting MyApplication {0} b{1}", [
>>         config.getString("myapp.version"),
>>         config.getString("myapp.build")
>>     ]);
>>
>> would be
>>
>> public class MyJsEnv {
>>       @Inject Configuration config;
>>       // all injections even @Resource, @PersistenceContext etc
>> }
>>
>> 2. I would have impl-ed a websocket endpoint getting MyJsEnv injected
>> and mapping client operations to server operations
>
> This is very similar to what I've tried in the very beginning. Problem
> is, in TomEE this works only statically, meaning that MyJsEnv should be
> available at application deployment (pre-generated, compiled and
> bundled in a WAR). If I generate/load such a class in runtime, EE
> injections won't be processed (contrary to WildFly, which in this case
> correctly processes both @Inject and @Resource/@EJB/@Persistence* etc).

Hmm, maybe not. JSP support injections and are generated at runtime so
it needs some glue code but reusing the same principle would work and
this API even if internal is more stable (see InstanceManager of
tomcat).

>
>> A common alternative for 100% server usage is to simply name beans and
>> use them as bindings in the script engine. With the BeanManager it is
>> pretty easy to implement a Binding to do so (something like
>> https://github.com/apache/tomee/blob/120a33c7b4de07ae01c17978ea37d88a911ea860/container/openejb-core/src/main/java/org/apache/openejb/util/OpenEJBScripter.java#L104
>> but more integrate to JSR 223)
>
> To help me understand better, could you please briefly explain what
> OpenEJBScripter is? Are there any examples of scripts for it? Are they
> able to access applications' PersistenceUnits/PersistenceContexts,
> UserTransactions etc.?

Was just a helper class there to allow to access CDI beans from JSR
223 scripts. Next step is to implements Bindings#get and avoid the
instantiate() method invocation to get a bean. It is not as good as
your impl but allows to wire CDI-script in a smooth fashion since you
provide any named CDI beans (very close to JSF xhtml/CDI link).

>
> Thx! Dimitri
>
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya
> (that's
> why hot reloading is a good bad idea and can break after a restart
> even if "F5" tests were green ;)).

Oh yeah, I've got obscure errors after hot reloading hundreds of times.
Most of them were JNDI-related, by the way ;)

> Hmm, maybe not. JSP support injections and are generated at runtime
> so
> it needs some glue code but reusing the same principle would work and
> this API even if internal is more stable (see InstanceManager of
> tomcat).

That's it - JSPs came to my mind even before the idea of generating
annotated classes. Needless to say I did some testing...

=============================== 8< ===================================
<%@page import="javax.enterprise.inject.spi.BeanManager"%>
<%@page import="javax.inject.Inject"%>
<%@page import="java.util.logging.Logger"%>
<%@page import="javax.transaction.UserTransaction"%>
<%@page import="javax.annotation.Resource"%>
<%@page import="javax.annotation.PostConstruct"%>

<%@page contentType="text/html" pageEncoding="UTF-8"%>

<%!
    @Resource
    private UserTransaction tx;
    
    @Inject
    private BeanManager bm;
    
    private final static Logger LOG = Logger.getLogger("foo.jsp");

    @PostConstruct
    public void post() {
        LOG.info("foo.jsp::post()");
        LOG.info("tx = " + tx);
        LOG.info("bm = " + bm);
    }
%>

<%
    request.setAttribute("tx", tx);
    request.setAttribute("bm", bm);
%>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        tx = ${requestScope.tx}<br>
        bm = ${requestScope.bm}<br>
    </body>
</html>
=============================== 8< ===================================

The results were, errr, a bit disappointing.

TomEE 7.0.0-M3
==============
tx = null
bm = org.apache.webbeans.container.InjectableBeanManager@b4ecb2
@PostConstruct: invoked

WildFly 10.0.0
==============
tx = null
bm = null
@PostConstruct: not invoked

GlassFish/Payara 4.1.1
======================
bm = (had to remove BeanManager since JSP didn't compile)
tx = null
@PostConstruct: not invoked

Is it another blind-spot in the spec? or is it a bug? or am I doing it
totally wrong?

Dimitri
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

Romain Manni-Bucau
jspInit() sounds better than PostConstruct. UserTransaction can use @Inject
by spec if Im not mistaken so issues are for entity manager and resources.
Here no portable solution but easy to generate at build tume producers for
them. A simpe Mojo/Ant task/gradle plugin would enable it in few lines of
code and in a portable manner.
Le 30 mars 2016 00:02, "Dimitri" <[hidden email]> a écrit :

> > (that's
> > why hot reloading is a good bad idea and can break after a restart
> > even if "F5" tests were green ;)).
>
> Oh yeah, I've got obscure errors after hot reloading hundreds of times.
> Most of them were JNDI-related, by the way ;)
>
> > Hmm, maybe not. JSP support injections and are generated at runtime
> > so
> > it needs some glue code but reusing the same principle would work and
> > this API even if internal is more stable (see InstanceManager of
> > tomcat).
>
> That's it - JSPs came to my mind even before the idea of generating
> annotated classes. Needless to say I did some testing...
>
> =============================== 8< ===================================
> <%@page import="javax.enterprise.inject.spi.BeanManager"%>
> <%@page import="javax.inject.Inject"%>
> <%@page import="java.util.logging.Logger"%>
> <%@page import="javax.transaction.UserTransaction"%>
> <%@page import="javax.annotation.Resource"%>
> <%@page import="javax.annotation.PostConstruct"%>
>
> <%@page contentType="text/html" pageEncoding="UTF-8"%>
>
> <%!
>     @Resource
>     private UserTransaction tx;
>
>     @Inject
>     private BeanManager bm;
>
>     private final static Logger LOG = Logger.getLogger("foo.jsp");
>
>     @PostConstruct
>     public void post() {
>         LOG.info("foo.jsp::post()");
>         LOG.info("tx = " + tx);
>         LOG.info("bm = " + bm);
>     }
> %>
>
> <%
>     request.setAttribute("tx", tx);
>     request.setAttribute("bm", bm);
> %>
>
> <!DOCTYPE html>
> <html>
>     <head>
>         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
>         <title>JSP Page</title>
>     </head>
>     <body>
>         tx = ${requestScope.tx}<br>
>         bm = ${requestScope.bm}<br>
>     </body>
> </html>
> =============================== 8< ===================================
>
> The results were, errr, a bit disappointing.
>
> TomEE 7.0.0-M3
> ==============
> tx = null
> bm = org.apache.webbeans.container.InjectableBeanManager@b4ecb2
> @PostConstruct: invoked
>
> WildFly 10.0.0
> ==============
> tx = null
> bm = null
> @PostConstruct: not invoked
>
> GlassFish/Payara 4.1.1
> ======================
> bm = (had to remove BeanManager since JSP didn't compile)
> tx = null
> @PostConstruct: not invoked
>
> Is it another blind-spot in the spec? or is it a bug? or am I doing it
> totally wrong?
>
> Dimitri
>
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya
In reply to this post by mitya
Quick followup. I tried similar JSP in Tomcat 8.0.27, and wow! @Resource injection into JSP worked like a charm!

context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/foo" privileged="true">
    <Environment name="foo" value="42" type="java.lang.Integer" override="false"/>
</Context>

JSP:

<%!
    @Resource(name = "foo")
    private Integer foo;
%>
<html>
 <body>
  foo = <%= foo %>
 </body>
</html>

This successfully rendered foo = 42.

Things were much more complicated in TomEE. I couldn't find a way to make @Resource work in JSP at all.
Whether a resource was defined in WEB-INF/resources.xml, or was it something like ManagedExecutorService - @Resource didn't work either.
In the case of ManagedExecutorService, I was able to lookup it manually in JNDI (as java:comp/DefaultManagedExecutorService).
In the case of resources.xml-defined entry, I didn't manage to resolve it even through JNDI manually.

In Tomcat, on the contrary, user-defined resources are resolvable in JSP both via @Resource injection and manual JNDI lookup.

I'm using TomeEE 7.0.0-M3. Didn't try 1.7.4 though.

Dimitri

> >
> > (that's
> > why hot reloading is a good bad idea and can break after a restart
> > even if "F5" tests were green ;)).
> Oh yeah, I've got obscure errors after hot reloading hundreds of
> times.
> Most of them were JNDI-related, by the way ;)
>
> >
> > Hmm, maybe not. JSP support injections and are generated at runtime
> > so
> > it needs some glue code but reusing the same principle would work
> > and
> > this API even if internal is more stable (see InstanceManager of
> > tomcat).
> That's it - JSPs came to my mind even before the idea of generating
> annotated classes. Needless to say I did some testing...
>
> =============================== 8<
> ===================================
> <%@page import="javax.enterprise.inject.spi.BeanManager"%>
> <%@page import="javax.inject.Inject"%>
> <%@page import="java.util.logging.Logger"%>
> <%@page import="javax.transaction.UserTransaction"%>
> <%@page import="javax.annotation.Resource"%>
> <%@page import="javax.annotation.PostConstruct"%>
>
> <%@page contentType="text/html" pageEncoding="UTF-8"%>
>
> <%!
>     @Resource
>     private UserTransaction tx;
>     
>     @Inject
>     private BeanManager bm;
>     
>     private final static Logger LOG = Logger.getLogger("foo.jsp");
>
>     @PostConstruct
>     public void post() {
>         LOG.info("foo.jsp::post()");
>         LOG.info("tx = " + tx);
>         LOG.info("bm = " + bm);
>     }
> %>
>
> <%
>     request.setAttribute("tx", tx);
>     request.setAttribute("bm", bm);
> %>
>
> <!DOCTYPE html>
> <html>
>     <head>
>         <meta http-equiv="Content-Type" content="text/html;
> charset=UTF-8">
>         <title>JSP Page</title>
>     </head>
>     <body>
>         tx = ${requestScope.tx}<br>
>         bm = ${requestScope.bm}<br>
>     </body>
> </html>
> =============================== 8<
> ===================================
>
> The results were, errr, a bit disappointing.
>
> TomEE 7.0.0-M3
> ==============
> tx = null
> bm = org.apache.webbeans.container.InjectableBeanManager@b4ecb2
> @PostConstruct: invoked
>
> WildFly 10.0.0
> ==============
> tx = null
> bm = null
> @PostConstruct: not invoked
>
> GlassFish/Payara 4.1.1
> ======================
> bm = (had to remove BeanManager since JSP didn't compile)
> tx = null
> @PostConstruct: not invoked
>
> Is it another blind-spot in the spec? or is it a bug? or am I doing
> it
> totally wrong?
>
> Dimitri
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya

> In the case of resources.xml-defined entry, I didn't manage to
> resolve it even through JNDI manually.

Sorry, I was wrong, it is resolvable as "openejb:Resource/XXX" in JNDI. But still no way to use @Resource annotation.

Dimitri

Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

Romain Manni-Bucau
True, we are based on a static analysis at startup so we likely miss
JSP - means if you use it somewhere else it should work. Fixed in
https://issues.apache.org/jira/browse/TOMEE-1764

Romain Manni-Bucau
@rmannibucau |  Blog | Github | LinkedIn | Tomitriber


2016-03-30 1:52 GMT+02:00 Dimitri <[hidden email]>:
>
>> In the case of resources.xml-defined entry, I didn't manage to
>> resolve it even through JNDI manually.
>
> Sorry, I was wrong, it is resolvable as "openejb:Resource/XXX" in JNDI. But still no way to use @Resource annotation.
>
> Dimitri
>
Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

mitya
Hi,

In Tomee 7.0.0-M3, I've tried to use JavaeeInstanceManager and
DefaultInstanceManager to instantiate dynamically loaded class. (To
obtain the first, I used InstanceManagerFactory; for the second I've
copied the code from your recent commit and used ContainerServlet to
obtain parent StandardContext.)

Excerpt from servlet:

    StandardContext webapp = (StandardContext) wrapper.getParent();
    DefaultInstanceManager defaultIM = new DefaultInstanceManager(
            webapp.getNamingContextListener().getEnvContext(),
            TomcatInjections.buildInjectionMap(webapp.getNamingResources()), webapp,
            ParentClassLoaderFinder.Helper.get());

    InstanceManager javaeeIM = InstanceManagerFactory.getInstanceManager(getServletConfig());

    Object foo = null, bar = null;

    try {

        URLClassLoader ucl = new URLClassLoader(
                new URL[]{new URL("file:///home/mitya/.../classes/")}, // here resides external class "my.Foo"
                this.getClass().getClassLoader() // otherwise ClassNotFoundException on UserTransaction, EntityManager etc.
        );

        foo = javaeeIM.newInstance("my.Foo", ucl);
        bar = defaultIM.newInstance("my.Foo", ucl);

    } catch (IllegalAccessException | InvocationTargetException | NamingException | InstantiationException | ClassNotFoundException ex) {
        LOG.log(Level.SEVERE, null, ex);
    }

Foo.java:

public class Foo {
       
    private static final Logger LOG = Logger.getLogger(Foo.class.getName());

    @Resource(name = "foo")
    private Integer foo;

    @Resource
    private UserTransaction tx;

    @PersistenceContext
    private EntityManager em;

    @PostConstruct
    public void post() {
        LOG.info("Foo::post");
        LOG.info("foo = " + foo);
        LOG.info("em  = " + em);
        LOG.info("tx  = " + tx);
    }
    
}

With JavaeeInstanceManager, everything works perfect, but *only* if the
my.Foo class is present in classpath at application startup. If loaded
from external location, injections are not processed (nulls
everywhere).

With DefaultInstanceManager, I get the following for every injected
field:

javax.naming.NameNotFoundException: Name [my.Foo/tx] is not bound in this Context. Unable to find [my.Foo].

Does this all work as intended? Can I do anything about it?

Regards,
Dimitri

Reply | Threaded
Open this post in threaded view
|

Re: Performing Java EE resource injections dynamically

Romain Manni-Bucau
Do you use a custome classloader? If so try to override equals/hascode to
simulate webapp one
Le 31 mars 2016 06:43, "Dimitri" <[hidden email]> a écrit :

> Hi,
>
> In Tomee 7.0.0-M3, I've tried to use JavaeeInstanceManager and
> DefaultInstanceManager to instantiate dynamically loaded class. (To
> obtain the first, I used InstanceManagerFactory; for the second I've
> copied the code from your recent commit and used ContainerServlet to
> obtain parent StandardContext.)
>
> Excerpt from servlet:
>
>     StandardContext webapp = (StandardContext) wrapper.getParent();
>     DefaultInstanceManager defaultIM = new DefaultInstanceManager(
>             webapp.getNamingContextListener().getEnvContext(),
>             TomcatInjections.buildInjectionMap(webapp.getNamingResources()),
> webapp,
>             ParentClassLoaderFinder.Helper.get());
>
>     InstanceManager javaeeIM =
> InstanceManagerFactory.getInstanceManager(getServletConfig());
>
>     Object foo = null, bar = null;
>
>     try {
>
>         URLClassLoader ucl = new URLClassLoader(
>                 new URL[]{new URL("file:///home/mitya/.../classes/")}, //
> here resides external class "my.Foo"
>                 this.getClass().getClassLoader() // otherwise
> ClassNotFoundException on UserTransaction, EntityManager etc.
>         );
>
>         foo = javaeeIM.newInstance("my.Foo", ucl);
>         bar = defaultIM.newInstance("my.Foo", ucl);
>
>     } catch (IllegalAccessException | InvocationTargetException |
> NamingException | InstantiationException | ClassNotFoundException ex) {
>         LOG.log(Level.SEVERE, null, ex);
>     }
>
> Foo.java:
>
> public class Foo {
>
>     private static final Logger LOG =
> Logger.getLogger(Foo.class.getName());
>
>     @Resource(name = "foo")
>     private Integer foo;
>
>     @Resource
>     private UserTransaction tx;
>
>     @PersistenceContext
>     private EntityManager em;
>
>     @PostConstruct
>     public void post() {
>         LOG.info("Foo::post");
>         LOG.info("foo = " + foo);
>         LOG.info("em  = " + em);
>         LOG.info("tx  = " + tx);
>     }
>
> }
>
> With JavaeeInstanceManager, everything works perfect, but *only* if the
> my.Foo class is present in classpath at application startup. If loaded
> from external location, injections are not processed (nulls
> everywhere).
>
> With DefaultInstanceManager, I get the following for every injected
> field:
>
> javax.naming.NameNotFoundException: Name [my.Foo/tx] is not bound in this
> Context. Unable to find [my.Foo].
>
> Does this all work as intended? Can I do anything about it?
>
> Regards,
> Dimitri
>
>