admin@glassfish.java.net

Re: HK2 / v3 Issues related to admingui pluggability infrastructure

From: Ken Paulsen <Ken.Paulsen_at_Sun.COM>
Date: Tue, 26 Feb 2008 23:59:02 -0800

I have:

@Contract ConsoleProvider <-- Each module providing GUI
"IntegrationPoints" must implement this as a @Service; defines the URL
getConfiguration() method which points to an XML file for building the
IntegrationPoint objects.

@Service ConsolePluginService <-- I don't have a @Contract for this, but
I wanted to be able to locate this via the Habitat and have
ConsoleProvider[] @Injected into it... this class does the work of
iterating over the ConsoleProvider impls and getting all the
"IntegrationPoint" objects by type.

@Configured IntegrationPoint <-- configured from XML descriptors;
describe where in the GUI an extension will occur; may refer to other
objects that need to be loaded by the same ClassLoader

@Configured ConsoleConfig <-- Just contains IntegrationPoints.

In the GUI code, I use the Habitat to obtain the ConsolePluginService.
Getting a hold of that injects the ConsoleProviders from all modules. I
iterate over this, using the HK2 parser w/ the URL from each to
instantiate IntegrationPoint Objects and store them by "type".

In the GUI code, if I cast the object returned from:

habitat.getByType(ConsolePluginService.class);

To (ConsolePluginService), I get a ClassCastException. I believe this is
b/c a ConsolePluginService is created by a different classloader.

To get around this, I'm currently return Object instead of
ConsolePluginService from my "getPluginService()" method. Then using
reflection to invoke the getIntegrationPoints(String) method on it, as I
was unable to cast it to its proper type.

This is my first attempt at using HK2 concepts (@Contract/_at_Service,
Habitat, etc.), so please let me know if you have any suggestions on how
I can do things better.

For reference, here are the files involved:

v3/common/glassfish-api/src/main/java/org/glassfish/api/admingui/ConsoleProvider.java

v3/admingui/plugin/src/main/java/org/glassfish/admingui/plugin/ConsolePluginService.java
v3/admingui/plugin/src/main/java/org/glassfish/admingui/plugin/IntegrationPoint.java
v3/admingui/plugin/src/main/java/org/glassfish/admingui/plugin/ConsoleConfig.java

And I'm accessing this from:

v3/admingui/core/src/main/java/org/glassfish/admingui/handlers/PluginHandlers.java

Thanks!

Ken

Jerome Dochez wrote:
>
> On Feb 26, 2008, at 7:56 PM, Ken Paulsen wrote:
>
>>
>> Hi Jerome / Shing Wai,
>>
>> Thanks for providing the Habitat via the ServletContext. This is now
>> working for us and we're able to get the ConsolePluginService which
>> gives us the IntegrationPoint's that are specified in various modules
>> w/o doing anything funny at server startup.
>>
>> However.... since I am using multiple classloaders, I cannot cast
>> ConsolePluginService to ConsolePluginService within our web app code.
>> I am currently using reflection to invoke the method I need on the
>> ConsolePluginService provider to avoid the ClassCastException. What
>> is the preferred way to handle this? Can I set the context
>> classloader on the thread before I access each object from a
>> different classloader?
> so I am not sure I understand what you are trying to do... u have a
> ConsolePluginService interface (and implementations) which are loaded
> by different class loaders ? and then you need to invoke a method of
> that interface on different implementations ?
>
> jerome
>
>>
>>
>> Thanks,
>>
>> Ken
>>
>> Jerome Dochez wrote:
>>> ok for now, it's stored in your servlet context and the attribute
>>> name is com.sun.appserv.jsf.habitat
>>>
>>> that should get you going...
>>>
>>> jerome
>>>
>>> Ken Paulsen wrote:
>>>>
>>>> Hi Jerome,
>>>>
>>>> Hmm... I realized I can't init on startup b/c a module's
>>>> classloader is different than my web app's. So I get a different
>>>> instance. So I think I'm stuck until I have a way to Inject the
>>>> Habitat, or my @Service directly in my web app. Let me know if you
>>>> have any other ideas.
>>>>
>>>> Thanks,
>>>>
>>>> Ken
>>>>
>>>> Ken Paulsen wrote:
>>>>>
>>>>> Jerome Dochez wrote:
>>>>>>
>>>>>> so far, admin gui is a plain war file, right ?
>>>>> Yes.
>>>>>> looks like I need to extend @Startup service to have an
>>>>>> Application scope. Applications could optionally implement it and
>>>>>> that would be called when applications are started... that would
>>>>>> be enough right ?
>>>>> Yes, that sounds like it would work well. Any idea on how much
>>>>> effort that will take / when it will be available? In the mean
>>>>> time, I think I'll initialize on server startup so I can keep
>>>>> working.
>>>>>
>>>>> Thanks!
>>>>>
>>>>> Ken
>>>>>> jerome
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> On Feb 25, 2008, at 11:39 AM, Ken Paulsen wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Jerome Dochez wrote:
>>>>>>>>
>>>>>>>> On Feb 25, 2008, at 10:43 AM, Ken Paulsen wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Hi Jerome,
>>>>>>>>>
>>>>>>>>> Thanks for the quick reply.
>>>>>>>>>
>>>>>>>>> With plain apt, my annotations work. In the meantime, I was
>>>>>>>>> able to add extra checks in my annotation processors to keep
>>>>>>>>> them from processing annotation they aren't meant to process.
>>>>>>>>> In looking into this issue, it seems that HK2's use of
>>>>>>>>> CompositeAnnotationProcessors may be the
>>>>>>>>
>>>>>>>>> cause. In any case, I'm no longer blocked.
>>>>>>>> ok, I will look into that when I have some time...
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> As for the configuration info... I would like to have
>>>>>>>>> @Configured objects populated from XML files which describe
>>>>>>>>> gui integration points. Here's a small example of one of the
>>>>>>>>> XML files:
>>>>>>>>>
>>>>>>>>> <?xml version="1.0" encoding="UTF-8"?>
>>>>>>>>>
>>>>>>>>> <console-config id="myIntegration">
>>>>>>>>> <integration-point id="myTab" type="webApplicationTab"
>>>>>>>>> priority="22" parentId="webApplicationTab" uri="/myTab.jsf" />
>>>>>>>>> <integration-point id="jbiRootNode" type="tree" priority="840"
>>>>>>>>> parentId="rootNode" uri="/myTreeNode.jsf" content="test2"/>
>>>>>>>>> </console-config>
>>>>>>>>>
>>>>>>>>> I have this working... as long as the XML file is in the same
>>>>>>>>> module as my Service which parses them (using HK2's
>>>>>>>>> ConfigParser). I need a way to search all modules for these
>>>>>>>>> files. I will also need a way to read resources (images, other
>>>>>>>>> files text files, classes, etc.) from the same modules which
>>>>>>>>> contain these files.
>>>>>>>>
>>>>>>>> looks like you need to define a Contract for that, something like
>>>>>>>> @Contract
>>>>>>>> public interface GuiProvider {
>>>>>>>> /**
>>>>>>>> * @Return a URL for its configuration (xml file).
>>>>>>>> URL getConfiguration();
>>>>>>>> }
>>>>>>>>
>>>>>>>> then each module implements this GuiProvider with their own XML
>>>>>>>> configuration.
>>>>>>>>
>>>>>>>> when you need them you can just look up for all GuiProvider
>>>>>>>> implementations : @Inject
>>>>>>>> GuiProvider[] providers
>>>>>>>>
>>>>>>>> .... later in the code ...
>>>>>>>>
>>>>>>>> for (GuidProvider provider : providers) {
>>>>>>>> ConfigParser.parse(... provider.getConfiguration() ...);
>>>>>>>>
>>>>>>>> for accessing resources, you should use the implementation
>>>>>>>> class loader so for instance...
>>>>>>>>
>>>>>>>> for (GuiProvider provider : providers) {
>>>>>>>>
>>>>>>>> provider.getClass().getClassLoader().loadResource("META-INF/default-icon.gif);
>>>>>>>>
>>>>>>>>
>>>>>>>> }
>>>>>>>>
>>>>>>>> does that answer your question ?
>>>>>>> Yes. I was hoping to avoid needing to require people to create a
>>>>>>> Java class, but it's an easy step to do and will make things
>>>>>>> "just work" better. I'll do this.
>>>>>> this is the easiest for now, we might want to revisit this once
>>>>>> we have switched to OSGi
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> New question: How can I get the default Habitat? I tried
>>>>>>>>> "Globals.getGlobals().getDefaultHabitat()", but getGlobals()
>>>>>>>>> returned (null). To back up, I am simply trying to get a
>>>>>>>>> @Service implementation via Java code. I was trying via
>>>>>>>>> habitat.getByType(...), but can't seem to get a valid Habitat.
>>>>>>>> so you need to do this lookup in code that never had access to
>>>>>>>> injection ? can't you get the habitat injected when you
>>>>>>>> initialize and save it in your runtime for this kind of use
>>>>>>>> later on your code paths ?
>>>>>>> Ok, I can do that... if you can tell me how I should be
>>>>>>> initializing. I can make a @Service which implements Populator
>>>>>>> and store it... but I don't think I should be doing this on
>>>>>>> Server Startup.
>>>>>> no that would be wrong...
>>>>>>> Is there another interface or technique I can use to only
>>>>>>> initialize this when the Admin GUI is accessed for the first time?
>>>>>> so far, admin gui is a plain war file, right ?
>>>>>>
>>>>>> looks like I need to extend @Startup service to have an
>>>>>> Application scope. Applications could optionally implement it and
>>>>>> that would be called when applications are started... that would
>>>>>> be enough right ?
>>>>>>
>>>>>> jerome
>>>>>>>
>>>>>>>
>>>>>>> Thanks!!
>>>>>>>
>>>>>>> Ken
>>>>>>>>
>>>>>>>> Jerome
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks!
>>>>>>>>>
>>>>>>>>> Ken
>>>>>>>>>
>>>>>>>>> Jerome Dochez wrote:
>>>>>>>>>>
>>>>>>>>>> On Feb 24, 2008, at 8:13 PM, Ken Paulsen wrote:
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Hi Kohsuke / Jerome,
>>>>>>>>>>>
>>>>>>>>>>> I have attempted to implement the admin gui pluggability
>>>>>>>>>>> code and have run into some issues and questions. Here they
>>>>>>>>>>> are:
>>>>>>>>>>>
>>>>>>>>>>> * When creating a @Configured element with an
>>>>>>>>>>> @Attribute(required=true), the "required=true" does not
>>>>>>>>>>> seem to do anything. I noticed the generated
>>>>>>>>>>> ObjectNameInjector.java file did not have any check in it
>>>>>>>>>>> for (null). (not critical, but doesn't validate the data)
>>>>>>>>>>>
>>>>>>>>>> ok I will let Kohsuke fix that later...
>>>>>>>>>>>
>>>>>>>>>>> *
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> * In the admin-gui module when compiling w/ JSFTemplating's
>>>>>>>>>>> @annotations, it does a couple strange things (*this is
>>>>>>>>>>> blocking me*):
>>>>>>>>>>>
>>>>>>>>>>> 1) It generates the FormatDefinition.map &
>>>>>>>>>>> UIComponentFactory.map files when these do not exist when
>>>>>>>>>>> compiling as we did in v2 (ant).
>>>>>>>>>>> 2) It throws a NPE when an HK2 annotation is present w/
>>>>>>>>>>> JSFTemplating anntations in the same build:
>>>>>>>>>>>
>>>>>>>>>>> java.lang.NullPointerException
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.jsftemplating.annotation.HandlerAP.process(HandlerAP.java:121)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.mirror.apt.AnnotationProcessors$CompositeAnnotationProcessor.process(AnnotationProcessors.java:60)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.mirror.apt.AnnotationProcessors$CompositeAnnotationProcessor.process(AnnotationProcessors.java:60)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.mirror.apt.AnnotationProcessors$CompositeAnnotationProcessor.process(AnnotationProcessors.java:60)
>>>>>>>>>>>
>>>>>>>>>>> at com.sun.tools.apt.comp.Apt.main(Apt.java:454)
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.tools.apt.main.JavaCompiler.compile(JavaCompiler.java:258)
>>>>>>>>>>>
>>>>>>>>>>> at com.sun.tools.apt.main.Main.compile(Main.java:1102)
>>>>>>>>>>> at com.sun.tools.apt.main.Main.compile(Main.java:964)
>>>>>>>>>>> at com.sun.tools.apt.Main.processing(Main.java:95)
>>>>>>>>>>> at com.sun.tools.apt.Main.process(Main.java:85)
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.enterprise.module.maven.HK2CompileMojo$1.compileInProcess(HK2CompileMojo.java:87)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.enterprise.module.maven.AptCompiler.compile(AptCompiler.java:67)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.plugin.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:485)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.enterprise.module.maven.CompilerMojo.execute(CompilerMojo.java:113)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> com.sun.enterprise.module.maven.HK2CompileMojo.execute(HK2CompileMojo.java:101)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:443)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:539)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:480)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:459)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:311)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:278)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:143)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:334)
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.DefaultMaven.execute(DefaultMaven.java:125)
>>>>>>>>>>> at
>>>>>>>>>>> org.apache.maven.cli.MavenCli.main(MavenCli.java:280)
>>>>>>>>>>> at
>>>>>>>>>>> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>>>>>>>>>> at
>>>>>>>>>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>>>>>>>>>>>
>>>>>>>>>>> at java.lang.reflect.Method.invoke(Method.java:597)
>>>>>>>>>>> at
>>>>>>>>>>> org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
>>>>>>>>>>> at
>>>>>>>>>>> org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
>>>>>>>>>>>
>>>>>>>>>>> at
>>>>>>>>>>> org.codehaus.classworlds.Launcher.main(Launcher.java:375)
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I have 3 different Annotation processor factories. In each
>>>>>>>>>>> of them I return the supported types, which I believe
>>>>>>>>>>> should limit the annotations which are processed by the
>>>>>>>>>>> AnnotationProcessor created by the factory. However, based
>>>>>>>>>>> on the above errors, and tests that I've done, I think they
>>>>>>>>>>> are getting called for more than that. For example, my
>>>>>>>>>>> FormatDefinitionAPFactory is getting called for my @Handler
>>>>>>>>>>> annotations... this shouldn't happen. Perhaps the
>>>>>>>>>>> HK2Compiler is doing things differently than the normal apt
>>>>>>>>>>> compiler?
>>>>>>>>>>>
>>>>>>>>>> have you tried to just use the plain APT invocation from the
>>>>>>>>>> command line ? does your processors also get called with HK2
>>>>>>>>>> annotations ? I don't remember doing anything special in the
>>>>>>>>>> APT invocation from maven...
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> * I need to find out how I should be finding configuration
>>>>>>>>>>> information for the admin console plugin data. There is
>>>>>>>>>>> no Java file (currently) which I can annotate to provide
>>>>>>>>>>> this. I am currently using @Configured Pojo's for
>>>>>>>>>>> storing this information... so I need a way to find these
>>>>>>>>>>> XML files, or an alternative approach.
>>>>>>>>>>>
>>>>>>>>>> which configuration data are u trying to access ?
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Thanks,
>>>>>>>>>>>
>>>>>>>>>>> Ken
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>
>>>>>>
>>>
>