5 Replies Latest reply on Jul 12, 2010 5:13 PM by Rajendra prasad

    Resource Bundle for view

    mseminaro Newbie
      I have a Seam application (using seam 2.1.2) that is having some performance issues, When I turned on debug logging, I see thousands of messages like the one below just for a simple page in my application. It appears that Seam is trying to load a bundle that matches the view name, but this is not defined. I don't want to use page level bundles, so is there any way I can disable this behavior to avoid all these pointless calls? This is happening on every page in my application, so I probably have something configured incorrectly.

      2009-12-14 17:09:41,699 [erThread-8080-0] DEBUG jboss.seam.core.ResourceLoader  - resource bundle missing: home/dashboard

      Any help would be greatly appreciated!
        • 1. Re: Resource Bundle for view
          nathan dennis Expert

          having the same issue after referencing a message from a backing bean. did you ever figure this out?

          • 2. Re: Resource Bundle for view
            mseminaro Newbie

            Unfortunately not. Looking at the SEAM source, there doesn't appear to be any way to disable this feature.


            Resource files are cached for a period of time, if they exist. But the lookup failure is not cached, unfortunately, so the classpath is searched every time. A workaround I have considered is to just create empty bundles per page so that the caching would be activated. But I haven't tried this as yet.


            I will report back on this thread when I do have time to learn more.

            • 3. Re: Resource Bundle for view
              Rajendra prasad Newbie

              Hopefully you got the solution already,


              We had the same problem earlier,
              Seam gives us the feature of page level message overrides, unfortunately this does mean that when viewId is an EL we evaluate the bean again and again.


              Got it resolved by bypassing seam's default behavior just by overriding
              org.jboss.seam.international.Messages and SeamResourceBundle classes.


              Its very simple, can send you the details if you still having the problem.

              • 4. Re: Resource Bundle for view
                mseminaro Newbie

                No, I never did figure out a solution to this. I would appreciate it if you could share the details so I can leverage that.


                Thanks!

                • 5. Re: Resource Bundle for view
                  Rajendra prasad Newbie
                  Hi,

                  First one just override the SeamResourceBunlde, Most of the code just copied from source code, here still using Seam 2.0.1GA version.

                  SeamResourceBundle will use the Page level ResourceBundles, i.e, method getPageResourceBundles().
                  We should bypass this behavior by using locale resource bundle always.
                  Here most of the code from super class other than getKeys() and handleGetObject() methods.

                  public class CustomResourceBundle extends SeamResourceBundle {
                      private static Map<Locale, List<ResourceBundle>> bundleCache = new ConcurrentHashMap<Locale, List<ResourceBundle>>();

                      public static java.util.ResourceBundle getBundle() {
                          return java.util.ResourceBundle.getBundle(CustomResourceBundle.class.getName(), org.jboss.seam.core.Locale.instance());
                      }

                      @Override
                      public Enumeration<String> getKeys() {
                          List<ResourceBundle> bundles = getBundlesForCurrentLocale();
                          Enumeration<String>[] enumerations = new Enumeration[bundles.size()];
                          int i = 0;
                          for (ResourceBundle bundle : bundles) {
                              enumerations[i++] = bundle.getKeys();
                          }

                          return new EnumerationEnumeration<String>(enumerations);
                      }

                      @Override
                      public Locale getLocale() {
                          return org.jboss.seam.core.Locale.instance();
                      }

                      @Override
                      protected Object handleGetObject(String key) {
                          if (!Contexts.isApplicationContextActive()) {
                              return null;
                          }
                          for (java.util.ResourceBundle littleBundle : getBundlesForCurrentLocale()) {
                              try {
                                  return interpolate(littleBundle.getObject(key));
                              } catch (MissingResourceException mre) {
                              }
                          }

                          return null; // superclass is responsible for throwing MRE
                      }

                      /**
                       * Gets bundles for current locale from the cache, creating them if need be.
                       * @return
                       */
                      private List<java.util.ResourceBundle> getBundlesForCurrentLocale() {
                          Locale instance = org.jboss.seam.core.Locale.instance();
                          List<ResourceBundle> bundles = bundleCache.get(instance);
                          if (bundles == null) {
                              bundles = loadBundlesForCurrentLocale();
                              bundleCache.put(instance, bundles);
                          }
                          return bundles;
                      }

                      /**
                       * One time load of bundles for a locale
                       * @return
                       */
                      private List<ResourceBundle> loadBundlesForCurrentLocale() {
                          List<ResourceBundle> bundles = new ArrayList<ResourceBundle>();
                          ResourceLoader resourceLoader = ResourceLoader.instance();
                          for (String bundleName : resourceLoader.getBundleNames()) {
                              ResourceBundle bundle = resourceLoader.loadBundle(bundleName);
                              if (bundle != null)
                                  bundles.add(bundle);
                          }
                          ResourceBundle bundle = resourceLoader.loadBundle("ValidatorMessages");
                          if (bundle != null) {
                              bundles.add(bundle);
                          }
                          bundle = resourceLoader.loadBundle("org/hibernate/validator/resources/DefaultValidatorMessages");
                          if (bundle != null)
                              bundles.add(bundle);
                          bundle = resourceLoader.loadBundle("javax.faces.Messages");
                          if (bundle != null)
                              bundles.add(bundle);
                          return Collections.unmodifiableList(bundles);
                      }


                      private Object interpolate(Object message) {
                          return message != null && message instanceof String ? Interpolator.instance().interpolate((String) message) : message;
                      }

                  }


                  And the second part is our application need to use our CustomResourceBundle instead SeamResourceBundle,
                  this can be done by overriding the Messages behavior.

                  Extend the Messages with same @Name as it super class with @Install(precedence=APPLICATION).
                  Override createMap method, just to copy from the source, only change is need to get the bundle instance from our custom implementation of ResourceBundle.

                  The Implementation goes something like this.

                  @Scope(ScopeType.STATELESS)
                  @BypassInterceptors
                  @Name("org.jboss.seam.international.messagesFactory")
                  @Install(precedence = APPLICATION)
                  public class Messages extends org.jboss.seam.international.Messages {
                      @SuppressWarnings("unchecked")
                      protected Map createMap() {
                          // AbstractMap uses the implementation of entrySet to perform all its
                          // operations - for a resource bundle this is very inefficient for keys
                          return new AbstractMap<String, String>() {
                              private java.util.ResourceBundle bundle = CustomResourceBundle.getBundle();

                              @Override
                              public String get(Object key) {
                                  if (key instanceof String) {
                                      String resourceKey = (String) key;
                                      String resource = null;
                                      if (bundle != null) {
                                          try {
                                              resource = bundle.getString(resourceKey);
                                          } catch (MissingResourceException mre) {
                                              // Just swallow
                                          }
                                      }
                                      return resource == null ? resourceKey : resource;
                                  } else {
                                      return null;
                                  }
                              }

                              @Override
                              public Set<Map.Entry<String, String>> entrySet() {
                                  Enumeration<String> keys = bundle.getKeys();
                                  Map<String, String> map = new HashMap<String, String>();
                                  while (keys.hasMoreElements()) {
                                      String key = keys.nextElement();
                                      map.put(key, get(key));
                                  }
                                  return Collections.unmodifiableSet(map.entrySet());
                              }

                              @Override
                              public boolean containsKey(Object key) {
                                  return get(key) != null;
                              }

                              @Override
                              public Set<String> keySet() {
                                  Enumeration<String> keys = bundle.getKeys();
                                  return new HashSet<String>(Collections.list(keys));
                              }

                              @Override
                              public int size() {
                                  return keySet().size();
                              }

                          };
                      }
                  }


                  Thats it..
                  Hope this will help you.