The Provider @Contract (the actual class name is ConsoleProvider) is
defined in common/glassfish-api and is a module. Although I just checked
and we apparently have it in our WEB-INF/lib directory also. But the
actual instances of each Provider implementation in the array has a
different classloader, so I still would have thought it would blow
up.... unless they're all loaded from a shared classloader, yet retain
the classloader associated with the module when I call
getClass().getClassLoader()?
Anyway, I was mostly curious, I don't need an answer to this.
Thanks,
Ken
Jerome Dochez wrote:
> that's a very good question... where is the Provider interface defined
> ? is a jar or a module ?
> who imports that artifacts ?
>
> On Feb 27, 2008, at 12:41 PM, Ken Paulsen wrote:
>
>>
>> Hi Jerome,
>>
>> Quick question... why does this work?
>>
>> @Inject Provider[] providers; <-- Each provider is from a different
>> HK2 module and I assume a different classloader??
>>
>> In my code I do:
>>
>> for (Provider provider : providers) { <-- Why no ClassCastException
>> here? Or maybe during the Inject?
>> provider.getConfiguration();
>> ...
>> provider.getClass().getClassLoader().getResource(url) <-- B/c the CL
>> is specific to provider, it returns the correct result
>> }
>>
>> Ken
>>
>> Jerome Dochez wrote:
>>> yes so I think I need to provide a good way for you to import a few
>>> HK2 modules from the core web application which is not a module
>>> itself.... in the meantime, the reflection based approach is your
>>> only way out.
>>>
>>> jerome
>>>
>>> On Feb 26, 2008, at 11:59 PM, Ken Paulsen wrote:
>>>
>>>>
>>>> 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
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>
>>>>>
>>>
>