[Juzu] ProviderFactory and ClassLoader

16 views
Skip to first unread message

Damien B

unread,
Oct 7, 2013, 3:38:00 AM10/7/13
to ju...@googlegroups.com
Hello,
here is my context :
Juzu 0.7
Portlet

I've got a provider juzu.inject.ProviderFactory local to the war
(because creating a jx.i.Provider interface by interface is too much
work :-))(by the way I have to put the
META-INF/juzu.inject.ProviderFactory in a jar in WEB-INF/lib, it's not
picked up if it's in
WEB-INF/classes/META-INF/juzu.inject.ProviderFactory), the
ProviderFactory is instantiated and called, so far so good. But in my
war I have several portlets, so in order to have only one initialization
sequence, I've put some initialization in a ServletContextListener
("back to the roots"). From my tests, the instance of my custom
ProviderFactory is in the Portal ClassLoader whereas the objects created
by the ServletContextListener are in the "war ClassLoader", therefore
they have to declare themselves to the custom ProviderFactory, which is
not so nice.

As usual, my first question is: is this analysis about the classloaders
correct? Oh, and there is no second question, it's more along the lines
of "shouldn't the thread context ClassLoader be set on the 'war
classloader' when the custom ProviderFactory is called" (lots of
potential side effects). And a last one : for the use case where I put
several portlets in a war, and where they share something costly to
initializa, let's say an EntityManagerFactory, do I keep the same
initialization style (ServletContextListener), or Juzu does provide
something (it doesn't seem so to me).

Damien

Julien Viet

unread,
Oct 7, 2013, 4:29:30 AM10/7/13
to ju...@googlegroups.com
Hi,


On Oct 7, 2013, at 9:38 AM, Damien B <night...@gmail.com> wrote:

> Hello,
> here is my context :
> Juzu 0.7
> Portlet
>
> I've got a provider juzu.inject.ProviderFactory local to the war (because creating a jx.i.Provider interface by interface is too much work :-))(by the way I have to put the META-INF/juzu.inject.ProviderFactory in a jar in WEB-INF/lib, it's not picked up if it's in WEB-INF/classes/META-INF/juzu.inject.ProviderFactory), the ProviderFactory is instantiated and called, so far so good. But in my war I have several portlets, so in order to have only one initialization sequence, I've put some initialization in a ServletContextListener ("back to the roots"). From my tests, the instance of my custom ProviderFactory is in the Portal ClassLoader whereas the objects created by the ServletContextListener are in the "war ClassLoader", therefore they have to declare themselves to the custom ProviderFactory, which is not so nice.
>
> As usual, my first question is: is this analysis about the classloaders correct?

it is correct.

normally the thread context loader should be the war file since Juzu is bootstrapped by the war file (using JuzuServlet or JuzuPortlet).


> Oh, and there is no second question, it's more along the lines of "shouldn't the thread context ClassLoader be set on the 'war classloader' when the custom ProviderFactory is called" (lots of potential side effects).

yes it should be, I don't understand why (cf point before).

can you give me a stack trace of the code executing in your ProviderFactory ? ( a mere "new Exception().printStackTrace()" in your provider factory should be enough)


> And a last one : for the use case where I put several portlets in a war, and where they share something costly to initializa, let's say an EntityManagerFactory, do I keep the same initialization style (ServletContextListener), or Juzu does provide something (it doesn't seem so to me).

I think ServletContextListener is a good place for doing this.

A way to achieve something similar would be to have a simple bean for this task with a @PostConstruct annotated. But this bean would not be shared among applications, unless we develop some kind of special scope for it.

>
> Damien
>
> --
> You received this message because you are subscribed to the Google Groups "Juzu" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to juzu+uns...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Damien B

unread,
Oct 7, 2013, 5:25:31 AM10/7/13
to ju...@googlegroups.com
Re and thanks for the answers,
here is a partial stacktrace, the remaining is 190 lines of Catalina +
GateIn 3.4:

=> System.out.println("Renvoie d'une impl�mentation pour " +
implementationType);
Renvoie d'une impl�mentation pour interface
test.portlet.portlet1.services.TestService
=> System.out.println("GuiceManager=" + GuiceManager.getInstance()); //
GuiceManager is a ServletContextListener, getInstance() returns an Injector
=> the same code works with Arquillian + Tomcat Embedded, i.e. only one
ClassLoader
=> the ServletContextListener is correctly initialized (checked with
traces, not shown here)
GuiceManager=null (ok, null and not ClassNotFoundException, so it's not
a trivial ClassLoader isolation problem)
java.lang.Exception
at
test.lifecycle.LocalGuiceProviderFactory$1.get(LocalGuiceProviderFactory:20)
at
juzu.impl.inject.spi.guice.BeanBinding$ToProviderInstance.get(BeanBinding.java:128)
at
com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40)
at
com.google.inject.internal.SingleFieldInjector.inject(SingleFieldInjector.java:53)
at
com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:110)
at
com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:94)
at
com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:254)
at
com.google.inject.internal.InjectorImpl$4$1.call(InjectorImpl.java:978)
at
com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024)
at
com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:974)
at
juzu.impl.inject.spi.guice.GuiceContext.createContext(GuiceContext.java:222)
at
juzu.impl.inject.spi.guice.GuiceContext.createContext(GuiceContext.java:53)
at
juzu.impl.inject.spi.InjectionContext$BeanLifeCycleImpl.get(InjectionContext.java:95)
at juzu.impl.request.Request.dispatch(Request.java:393)
at juzu.impl.request.Request.invoke(Request.java:252)
at
juzu.impl.plugin.controller.ControllerPlugin.invoke(ControllerPlugin.java:155)
at juzu.impl.request.Request.invoke(Request.java:243)
at juzu.impl.plugin.amd.AMDPlugin.invoke(AMDPlugin.java:228)
at juzu.impl.request.Request.invoke(Request.java:243)
at juzu.impl.plugin.asset.AssetPlugin.invoke(AssetPlugin.java:239)
at juzu.impl.request.Request.invoke(Request.java:243)
at
juzu.impl.plugin.controller.ControllerPlugin.invoke(ControllerPlugin.java:135)
at
juzu.impl.bridge.spi.portlet.PortletRequestBridge.invoke(PortletRequestBridge.java:287)
at
juzu.impl.bridge.spi.portlet.PortletRenderBridge.invoke(PortletRenderBridge.java:76)
at juzu.bridge.portlet.JuzuPortlet.render(JuzuPortlet.java:318)
at
org.gatein.pc.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:569)

Damien


Le 07/10/2013 10:29, Julien Viet a �crit :

Julien Viet

unread,
Oct 7, 2013, 5:39:13 AM10/7/13
to ju...@googlegroups.com
ok so this seems to be executed from your Provider implementation returned by the ProviderFactory (test.lifecycle.LocalGuiceProviderFactory$1)

can you do the same in the ProviderFactory#getProvider method ?

On Oct 7, 2013, at 11:25 AM, Damien B <night...@gmail.com> wrote:

> Re and thanks for the answers,
> here is a partial stacktrace, the remaining is 190 lines of Catalina + GateIn 3.4:
>
> => System.out.println("Renvoie d'une implémentation pour " + implementationType);
> Renvoie d'une implémentation pour interface test.portlet.portlet1.services.TestService

Damien B

unread,
Oct 7, 2013, 5:48:48 AM10/7/13
to ju...@googlegroups.com
Yes we can.

java.lang.Exception
at
test.lifecycle.LocalGuiceProviderFactory.getProvider(LocalGuiceProviderFactory:16)
at
juzu.impl.plugin.binding.BindingPlugin.init(BindingPlugin.java:79)
at
juzu.impl.plugin.application.Application.start(Application.java:119)
at
juzu.impl.runtime.ApplicationRuntime.start(ApplicationRuntime.java:154)
at
juzu.impl.runtime.ApplicationRuntime.refresh(ApplicationRuntime.java:129)
at
juzu.impl.bridge.module.ApplicationBridge.refresh(ApplicationBridge.java:106)
at juzu.impl.bridge.Bridge.refresh(Bridge.java:65)
at juzu.bridge.portlet.JuzuPortlet.render(JuzuPortlet.java:298)
at
org.gatein.pc.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:569)





Le 07/10/2013 11:39, Julien Viet a �crit :
> ok so this seems to be executed from your Provider implementation returned by the ProviderFactory (test.lifecycle.LocalGuiceProviderFactory$1)
>
> can you do the same in the ProviderFactory#getProvider method ?
>
> On Oct 7, 2013, at 11:25 AM, Damien B <night...@gmail.com> wrote:
>
>> Re and thanks for the answers,
>> here is a partial stacktrace, the remaining is 190 lines of Catalina + GateIn 3.4:
>>
>> => System.out.println("Renvoie d'une impl�mentation pour " + implementationType);
>> Renvoie d'une impl�mentation pour interface test.portlet.portlet1.services.TestService
>> Le 07/10/2013 10:29, Julien Viet a �crit :

Julien Viet

unread,
Oct 7, 2013, 9:47:34 AM10/7/13
to ju...@googlegroups.com
what puzzles me is that in both cases the thread context classloader should be correct as Juzu only change it one time (before dispatching to a controller) and that the runtime thread context classloader should be set by the environment (the servlet container) to be the one of the dispatched application.

would you mind to debug the Thread#setContextClassLoader and monitor (with a breakpoint in a debugger) its changes during a request to figure out why it is not correct ?

On Oct 7, 2013, at 11:48 AM, Damien B <night...@gmail.com> wrote:

> Yes we can.
>
> java.lang.Exception
> at test.lifecycle.LocalGuiceProviderFactory.getProvider(LocalGuiceProviderFactory:16)
> at juzu.impl.plugin.binding.BindingPlugin.init(BindingPlugin.java:79)
> at juzu.impl.plugin.application.Application.start(Application.java:119)
> at juzu.impl.runtime.ApplicationRuntime.start(ApplicationRuntime.java:154)
> at juzu.impl.runtime.ApplicationRuntime.refresh(ApplicationRuntime.java:129)
> at juzu.impl.bridge.module.ApplicationBridge.refresh(ApplicationBridge.java:106)
> at juzu.impl.bridge.Bridge.refresh(Bridge.java:65)
> at juzu.bridge.portlet.JuzuPortlet.render(JuzuPortlet.java:298)
> at org.gatein.pc.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:569)
>
>
>
>
>
> Le 07/10/2013 11:39, Julien Viet a écrit :
>> ok so this seems to be executed from your Provider implementation returned by the ProviderFactory (test.lifecycle.LocalGuiceProviderFactory$1)
>>
>> can you do the same in the ProviderFactory#getProvider method ?
>>
>> On Oct 7, 2013, at 11:25 AM, Damien B <night...@gmail.com> wrote:
>>
>>> Re and thanks for the answers,
>>> here is a partial stacktrace, the remaining is 190 lines of Catalina + GateIn 3.4:
>>>
>>> => System.out.println("Renvoie d'une implémentation pour " + implementationType);
>>> Renvoie d'une implémentation pour interface test.portlet.portlet1.services.TestService

Damien B

unread,
Oct 7, 2013, 9:55:35 AM10/7/13
to ju...@googlegroups.com
I'm progressing...
Here is a test where the ServletContextListener (GuiceManager) declares
itself on a static member of LocalGuiceProviderFactory.
I've added a static {} block inside the LocalGuiceProviderFactory to see
when the class was loaded and by which ClassLoader.

The ClassLoader is good, but the class is loaded twice :
I'm loaded (LGPF)
WebappClassLoader
context: /portletswar
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@c5a67c9

java.lang.Exception
at
test.lifecycle.LocalGuiceProviderFactory.<clinit>(LocalGuiceProviderFactory.java:19)
at test.lifecycle.GuiceManager.forceNew(GuiceManager.java:54)
at
test.lifecycle.GuiceManager.contextInitialized(GuiceManager.java:28)
at
org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4205)

I'm loaded (LGPF)
WebappClassLoader
context: /portletswar
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@c5a67c9

java.lang.Exception
at
test.lifecycle.LocalGuiceProviderFactory.<clinit>(LocalGuiceProviderFactory:19)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at
java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:345)
at java.util.ServiceLoader$1.next(ServiceLoader.java:421)
at juzu.impl.common.Tools.list(Tools.java:575)
at juzu.impl.common.Tools.list(Tools.java:569)
at
juzu.impl.plugin.binding.BindingPlugin.init(BindingPlugin.java:47)
at
juzu.impl.plugin.application.Application.start(Application.java:119)
at
juzu.impl.runtime.ApplicationRuntime.start(ApplicationRuntime.java:154)
at
juzu.impl.runtime.ApplicationRuntime.refresh(ApplicationRuntime.java:129)
at
juzu.impl.bridge.module.ApplicationBridge.refresh(ApplicationBridge.java:106)
at juzu.impl.bridge.Bridge.refresh(Bridge.java:65)
at juzu.bridge.portlet.JuzuPortlet.render(JuzuPortlet.java:298)
at
org.gatein.pc.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:569)

Since nothing points on the LocalGuiceProviderFactory, the class can be
unloaded and garbage collected, so it doesn't seem that abnormal.

So back to the previous attempt : LocalGuiceProviderFactory fetches it's
context from GuiceManager (a ServletContextListener) which has a static
member.

I'm loaded (GM)
WebappClassLoader
context: /portletswar
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@151ca803

java.lang.Exception
at test.lifecycle.GuiceManager.<clinit>(GuiceManager.java:22)
at
sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at
sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at java.lang.Class.newInstance0(Class.java:355)
at java.lang.Class.newInstance(Class.java:308)
at
org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4149)

I'm loaded (LGPF)
WebappClassLoader
context: /portletswar
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@151ca803

java.lang.Exception
at
test.lifecycle.LocalGuiceProviderFactory.<clinit>(LocalGuiceProviderFactory:19)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at
java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:345)
at java.util.ServiceLoader$1.next(ServiceLoader.java:421)
at juzu.impl.common.Tools.list(Tools.java:575)
at juzu.impl.common.Tools.list(Tools.java:569)
at
juzu.impl.plugin.binding.BindingPlugin.init(BindingPlugin.java:47)
at
juzu.impl.plugin.application.Application.start(Application.java:119)
at
juzu.impl.runtime.ApplicationRuntime.start(ApplicationRuntime.java:154)
at
juzu.impl.runtime.ApplicationRuntime.refresh(ApplicationRuntime.java:129)
at
juzu.impl.bridge.module.ApplicationBridge.refresh(ApplicationBridge.java:106)
at juzu.impl.bridge.Bridge.refresh(Bridge.java:65)
at juzu.bridge.portlet.JuzuPortlet.render(JuzuPortlet.java:298)
at
org.gatein.pc.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:569)

I'm loaded (GM)
WebappClassLoader
context: /portletswar
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@151ca803

java.lang.Exception
at test.lifecycle.GuiceManager.<clinit>(GuiceManager:22)
at
test.lifecycle.LocalGuiceProviderFactory.getProvider(LocalGuiceProviderFactory:27)
at
juzu.impl.plugin.binding.BindingPlugin.init(BindingPlugin.java:79)
at
juzu.impl.plugin.application.Application.start(Application.java:119)
at
juzu.impl.runtime.ApplicationRuntime.start(ApplicationRuntime.java:154)
at
juzu.impl.runtime.ApplicationRuntime.refresh(ApplicationRuntime.java:129)
at
juzu.impl.bridge.module.ApplicationBridge.refresh(ApplicationBridge.java:106)
at juzu.impl.bridge.Bridge.refresh(Bridge.java:65)
at juzu.bridge.portlet.JuzuPortlet.render(JuzuPortlet.java:298)
at
org.gatein.pc.portlet.impl.jsr168.PortletContainerImpl$Invoker.doFilter(PortletContainerImpl.java:569)

So here it's the GuiceManager class, which is the
ServletContextListener, which is loaded twice by the same classloader,
and thus loses its static members.
And that I can not explain ^^;
To be sure, I've run with -verbose:class, and...

[Loaded test.lifecycle.GuiceManager from
file:/C:/shared/tools/GateIn-3.4.0.Final-tomcat6/webapps/portletswar/WEB-INF/classes/test/lifecycle/GuiceManager.class]

Before the first loading, and then before the second loading:

[Loaded test.GuiceManager from juzu:/]

And for now, I've no analysis to give.

Damien





Le 07/10/2013 11:39, Julien Viet a �crit :
> ok so this seems to be executed from your Provider implementation returned by the ProviderFactory (test.lifecycle.LocalGuiceProviderFactory$1)
>
> can you do the same in the ProviderFactory#getProvider method ?
>
> On Oct 7, 2013, at 11:25 AM, Damien B <night...@gmail.com> wrote:
>
>> Re and thanks for the answers,
>> here is a partial stacktrace, the remaining is 190 lines of Catalina + GateIn 3.4:
>>
>> => System.out.println("Renvoie d'une impl�mentation pour " + implementationType);
>> Renvoie d'une impl�mentation pour interface test.portlet.portlet1.services.TestService
>> Le 07/10/2013 10:29, Julien Viet a �crit :

Damien B

unread,
Oct 7, 2013, 10:31:51 AM10/7/13
to ju...@googlegroups.com
Ok, found it (sort of), it was the live mode that considered that the
class was not known. In dev mode I've got the statics back.
Thanks for the time.

Damien

Le 07/10/2013 15:55, Damien B a �crit :

Julien Viet

unread,
Oct 7, 2013, 10:37:10 AM10/7/13
to ju...@googlegroups.com
is it the correct thread context classloader ?

On Oct 7, 2013, at 4:31 PM, Damien B <night...@gmail.com> wrote:

> Ok, found it (sort of), it was the live mode that considered that the class was not known. In dev mode I've got the statics back.
> Thanks for the time.
>
> Damien
>
>>>> => System.out.println("Renvoie d'une implémentation pour " + implementationType);
>>>> Renvoie d'une implémentation pour interface test.portlet.portlet1.services.TestService

Damien B

unread,
Oct 7, 2013, 10:41:01 AM10/7/13
to ju...@googlegroups.com
Yep, the context ClassLoader displayed is the right one, I don't how the
DevClassLoader is inserted in the chain, but somewhere it estimated that
it provided a better resource than the deployed .class file.

Le 07/10/2013 16:37, Julien Viet a �crit :
> is it the correct thread context classloader ?
>
> On Oct 7, 2013, at 4:31 PM, Damien B <night...@gmail.com> wrote:
>
>> Ok, found it (sort of), it was the live mode that considered that the class was not known. In dev mode I've got the statics back.
>> Thanks for the time.
>>
>> Damien
>>
>> Le 07/10/2013 15:55, Damien B a �crit :
>>> Le 07/10/2013 11:39, Julien Viet a �crit :
>>>> ok so this seems to be executed from your Provider implementation returned by the ProviderFactory (test.lifecycle.LocalGuiceProviderFactory$1)
>>>>
>>>> can you do the same in the ProviderFactory#getProvider method ?
>>>>
>>>> On Oct 7, 2013, at 11:25 AM, Damien B <night...@gmail.com> wrote:
>>>>
>>>>> Re and thanks for the answers,
>>>>> here is a partial stacktrace, the remaining is 190 lines of Catalina + GateIn 3.4:
>>>>>
>>>>> => System.out.println("Renvoie d'une impl�mentation pour " + implementationType);
>>>>> Renvoie d'une impl�mentation pour interface test.portlet.portlet1.services.TestService
>>>>> Le 07/10/2013 10:29, Julien Viet a �crit :

Julien Viet

unread,
Oct 7, 2013, 10:43:46 AM10/7/13
to ju...@googlegroups.com
that's the point of the DevClassLoader that replaces the classes in /WEB-INF/classes by the live mode compiled classes.

On Oct 7, 2013, at 4:41 PM, Damien B <night...@gmail.com> wrote:

> Yep, the context ClassLoader displayed is the right one, I don't how the DevClassLoader is inserted in the chain, but somewhere it estimated that it provided a better resource than the deployed .class file.
>
>>>>>> => System.out.println("Renvoie d'une implémentation pour " + implementationType);
>>>>>> Renvoie d'une implémentation pour interface test.portlet.portlet1.services.TestService

Damien B

unread,
Oct 7, 2013, 10:46:29 AM10/7/13
to ju...@googlegroups.com
Le 07/10/2013 16:43, Julien Viet a �crit :
> that's the point of the DevClassLoader that replaces the classes in /WEB-INF/classes by the live mode compiled classes.

Yep, but that one was not recompiled at all and furthermore is not bound
to the Juzu lifecycle :-)



>
> On Oct 7, 2013, at 4:41 PM, Damien B <night...@gmail.com> wrote:
>
>> Yep, the context ClassLoader displayed is the right one, I don't how the DevClassLoader is inserted in the chain, but somewhere it estimated that it provided a better resource than the deployed .class file.
>>
>> Le 07/10/2013 16:37, Julien Viet a �crit :
>>> is it the correct thread context classloader ?
>>>
>>> On Oct 7, 2013, at 4:31 PM, Damien B <night...@gmail.com> wrote:
>>>
>>>> Ok, found it (sort of), it was the live mode that considered that the class was not known. In dev mode I've got the statics back.
>>>> Thanks for the time.
>>>>
>>>> Damien
>>>>
>>>> Le 07/10/2013 15:55, Damien B a �crit :
>>>>> Le 07/10/2013 11:39, Julien Viet a �crit :
>>>>>> ok so this seems to be executed from your Provider implementation returned by the ProviderFactory (test.lifecycle.LocalGuiceProviderFactory$1)
>>>>>>
>>>>>> can you do the same in the ProviderFactory#getProvider method ?
>>>>>>
>>>>>> On Oct 7, 2013, at 11:25 AM, Damien B <night...@gmail.com> wrote:
>>>>>>
>>>>>>> Re and thanks for the answers,
>>>>>>> here is a partial stacktrace, the remaining is 190 lines of Catalina + GateIn 3.4:
>>>>>>>
>>>>>>> => System.out.println("Renvoie d'une impl�mentation pour " + implementationType);
>>>>>>> Renvoie d'une impl�mentation pour interface test.portlet.portlet1.services.TestService
>>>>>>> Le 07/10/2013 10:29, Julien Viet a �crit :

Julien Viet

unread,
Oct 7, 2013, 10:54:29 AM10/7/13
to ju...@googlegroups.com
indeed, juzu should be a bit more smart and avoid to double load such class when the original class was not modified.

would save some bits in heap.

I recorded it there https://github.com/juzu/juzu/issues/52 for the future work on live mode.

On Oct 7, 2013, at 4:46 PM, Damien B <night...@gmail.com> wrote:
>>>>>>>> => System.out.println("Renvoie d'une implémentation pour " + implementationType);
>>>>>>>> Renvoie d'une implémentation pour interface test.portlet.portlet1.services.TestService

Julien Viet

unread,
Oct 11, 2013, 2:26:19 AM10/11/13
to ju...@googlegroups.com
I gave more thoughts about the double load class in live mode, first here is a bit of how it works internally and how it could be modified to work.

At compilation time, the application is compiled from the sources (each time a source has been detected to be modified)

1/ At runtime, the application is started using the following classloader architecture:
- Web application classloader (provided by Tomcat) : load the application classes and lib (plus the parent of course)
/\
- DevClassLoader : load any class from the parent except the classes from WEB-INF/classes
/\
- URLClassLoader : load the compiled classes in phase 1

2/ this can be modified to using a ChildFirstClassLoader hierarchy
- Web application classloader
- ChildFirstClassLoader : load any class before the parent

The trick is that the ChildFirstClassLoader would not contain all application classes but only the ones that are:
- new
- don't have the same bytecode

The big differences between the two ways:
in 1/ the same class is loaded twice by the Web application classloader and the URLClassLoader
in 2/ the same class is loaded once unless it has been modified (and in that case it is not the same exact class)

And there is my conclusion and there are cons / pro

1/ uses more heap / static are reinitialized
2/ uses less heap / static are reset

So this behavior with static is the main functionnal differences between the two.

any additional thoughts ?

Julien Viet

unread,
Oct 11, 2013, 2:28:34 AM10/11/13
to ju...@googlegroups.com
actually there would be also classcast issues…. (how stupid I am :-) )

Damien B

unread,
Oct 11, 2013, 6:16:07 PM10/11/13
to ju...@googlegroups.com
More heap in live mode doesn't bother me much (especially if you're testing in eXo, you need a solir 2GiB of RAM for the heap, what 10 classes are going to add to that ? :-)).
Otherwise reversing the delegation model would be gould. There would be no classcast in my case (or I dont' see immediate one) if the only recompiled ones are those managed by Juzu (Controler and View). In my case Services are not managed by Juzu because I need to add an external Guice module in the injector, so it's a separately managed container (and Juzu should not try to recompile it... I should put those classes outside of the package hierarchy holding the @juzu.Application )

Julien Viet

unread,
Oct 12, 2013, 4:10:06 AM10/12/13
to ju...@googlegroups.com
I'm thinking about doing that actually but it requires some minor tricky stuff to do:

so we have classes provided by webapp classloader from WEB-INF/classes (let's call it A) and then the classes provided by the live compiled sources (let's call it B).

some classes can be removed in B, some modified in B, some new in B.

so I think to achieve that we must determine the set of classes using this way:

1/ find all new classes or modified classes in B let's call it C
2/ from C determine D which is its transitive closures
3/ from D we remove the set C and we get E
4/ we analyze E and we keep only the classes that references any class in C

so this set E that are classes in A and B that are not modified, references the set of classes in C and therefore must be loaded by the ChildFirstClassLoader.

and this way to do implies to analyse the bytecode of the produced classes to find the transitive closure.

So probably the rules before are not precise enough of sufficient, but you get the idea.

If you can think about something simpler, let me know :-)

Julien Viet

unread,
Oct 17, 2013, 11:10:17 AM10/17/13
to ju...@googlegroups.com
Hi Damien,

I pushed a reimplementation of the live mode in the current master that uses the new classloading mechanism.

For the records, now there is a LiveClassLoader that more or less do what i tried to describe before:
- a class is loaded from the live loader when it is loadable by the live loader (i.e it was compiled by the live mode) and it has been modified (so there are two .class objects which are different loaded from the parent loader and the live loader) or references a class loaded by the live loader
- otherwise a class is loaded from webapp classloader

For example if you have three classes A, B and C. B and C references A, C references B. If B is modified then only B and C are loaded by the live loader.

This new classloading policy in addition of saving heap, provides a better integration capabilities with services started outside of Juzu. For instance, one of our developers uses Juzu with Spring provided and has beans deployed by Spring that are not managed by Juzu, but his Juzu app uses those beans. Without this mode, there would be class identity issues such as ClassCastException. With the new behavior it does work.

Can you give a try and give me feedback ?

On Oct 12, 2013, at 12:16 AM, Damien B <ka...@cinemasie.com> wrote:

Reply all
Reply to author
Forward
0 new messages