7 Replies Latest reply on Jul 7, 2009 5:27 PM by Dieter Kaeppel

    ResourceLoader

    Pierre Ingmansson Newbie

      Hello!


      I am trying to get Seam to load bundles distributed over a number of jars. Every jar has it's own components.xml (in the META-INF directory, off course) and it's own message bundles. I have created my own seam-component that is @Startup-annotated to load the specified resource-bundles at startup. In this class I have a method annotated with @Create that uses ResourceLoader.instance() to fetch and add the resource-bundle name to it.


      My problem is that the ResourceLoader is stateless, and it doesn't matter how many resource-bundles I try adding, everytime you call ResourceLoader.instance() you get a new instance of it!


      This behaviour does not only make it impossible to add your own resource-bundles programmatically, but slows down performance as well. Why should a new instance of ResourceLoader be created everytime someone wants to access it (like everytime a JSF-page is loaded)?
      Would it be impossible to make ResourceLoader reside in the Application-context instead of stateless?

        • 1. Re: ResourceLoader
          Pete Muir Master

          You would be better off overriding ResourceLoader to customize the getBundleNames() method to do what you want.

          • 2. Re: ResourceLoader
            Gavin King Master

            You can do whatever you like by extending and overriding the built-in component.


            ie.



            @Name("org.jboss.seam.faces.resourceLoader")
            @Scope(APPLICATION)
            public class MyResourceLoader extends ResourceLoader {
               ...
            }

            • 3. Re: ResourceLoader
              Pierre Ingmansson Newbie

              Yepp, I tried that!


              Look at the method ResourceLoader.instance():



              public static ResourceLoader instance()
              {
                return (ResourceLoader) Component.getInstance(ResourceLoader.class, ScopeType.STATELESS);
              }



              This method is called from both Page and Pages in the org.jboss.seam.navigation-package several times for loading a single page. I just counted the calls made to ResourceLoader.instance() for a simple plage and there were 34 calls to this method.. everytime someone loads this particular page!


              And that also means that 34 instances of ResourceLoader will be initialized due to the static STATELESS-declaration in the instance()-method. There is no way I can create my own component that is in the Application-context and overrides the default ResourceLoader without change the way ResourceLoader.instance() works.


              Maby the Page- and Pages-classes should use a better way of fetching the ResourceLoader the using the instance()-method?

              • 4. Re: ResourceLoader
                Gavin King Master

                Hmmmm, interesting. Not quite sure of a good fix for that. We don't want to just call Component.getInstance(ResourceLoader.class) because that will do a search of all contexts, which will affect performance for something called so often.



                Submit the problem to JIRA, with a link to this thread, if you like....

                • 5. Re: ResourceLoader
                  Pierre Ingmansson Newbie

                  The reason why I am trying to replace the ResourceLoader is due to another problem that I have already posted to JIRA:


                  http://jira.jboss.com/jira/browse/JBSEAM-2364


                  Fixing that issue should eliminate this problem (for me at least). But the issue with the hard-coded scope-type of ResourceLoader is a problem in itself too.


                  I can create a new issue in JIRA, and link to both this thread and my other JIRA-issue I guess..


                  Thanks for the help!

                  • 6. Re: ResourceLoader
                    Pete Muir Master

                    Pierre Ingmansson wrote on Feb 19, 2008 04:36 PM:


                    But the issue with the hard-coded scope-type of ResourceLoader is a problem in itself too.

                    Thanks for the help!


                    Why?


                    A simple workaround is to move the expensive work you are doing into an application scoped component, and just call this component from the stateless ResourceLoader.

                    • 7. Re: ResourceLoader
                      Dieter Kaeppel Newbie

                      Maybe somebody is interested in my solution.


                      First I derived the Seam ResourceLoader:


                      @Scope(ScopeType.APPLICATION)
                      @BypassInterceptors
                      @Name("org.jboss.seam.core.resourceLoader")
                      public class ResourceLoader extends org.jboss.seam.core.ResourceLoader {
                           public static final String RESOURCE_LOADER_BUNDLE_NAMES = "org.jboss.seam.core.resourceLoader.bundleNames";
                           
                           private Set<String> bundleNameSet;
                      
                           @Override
                           public String[] getBundleNames() {
                                return getBundleNameSet().toArray(new String[getBundleNameSet().size()]);
                           }
                           @Override
                           public void setBundleNames(String[] bundleNames) {
                                getBundleNameSet().addAll(Arrays.asList(bundleNames));
                           }
                           
                           private Set<String> getBundleNameSet() {
                                if (bundleNameSet == null) {
                                     bundleNameSet = new HashSet<String>();
                                     if (Events.exists())
                                          Events.instance().raiseEvent(RESOURCE_LOADER_BUNDLE_NAMES, bundleNameSet);
                                }
                                return bundleNameSet;
                           }
                      }
                      



                      The trick is to fire an event when the bundleNames first accessed. The following class replaces bundle definitions:


                      public class ResourceBundle {
                           private String[] bundleNames;
                      
                           public String[] getBundleNames() {
                                return bundleNames;
                           }
                           public void setBundleNames(String[] bundleNames) {
                                this.bundleNames = bundleNames;
                           }
                           
                           @Observer(ResourceLoader.RESOURCE_LOADER_BUNDLE_NAMES)
                           public void loadBundleNames(Set<String> bundleNameSet) {
                                bundleNameSet.addAll(Arrays.asList(bundleNames));
                           }
                      }
                      



                      In the components.xml you write now:


                           <component name="transportBundles" class="your.package.ResourceBundle" scope="application" startup="true">
                                <property name="bundleNames">
                                     <value>some-messages</value>
                                </property>
                           </component>
                      



                      And the messages will add up. Any comments?