1 2 3 4 Previous Next 52 Replies Latest reply on Nov 17, 2007 3:10 PM by pmuir Go to original post
      • 30. Re: ResourceBundle in Database
        pmuir

        This seems like a useful thing to do. I'll try to write up a tutorial based on this thread over the next few days.

        • 31. Re: ResourceBundle in Database
          pmuir
          • 32. Re: ResourceBundle in Database
            gus888

            Great job, Pete. Thank you soooo much.

            • 33. Re: ResourceBundle in Database
              zerg-spirit

              Nice article, making things way easier than implementing a lot of class to do the controls.

              • 34. Re: ResourceBundle in Database
                amitev

                Great job! This should go in the wiki page.

                • 35. Re: ResourceBundle in Database
                  zerg-spirit

                  Though it's a nice article and helped me simplify the whole stuff, it doesn't really speak about my mine problem: the caching issue.

                  Say, starting with your example, that I want to cache every locale's bundle, and reload them only if they have changed (tagged as 'dirty').
                  Then, how to 'stay' the Seam's ResourceBundle to reload? Cause even if I'm putting a @Observer(localeSelected) on the loadBundle method, the newly created ResourceBundle won't be used by Seam, who'll continue to use the previous one.
                  I think that's my main problem, and having the key to solve it would probably help me implement the solution (although I'm having the strange feeling the Component.getInstance("MyResourceBundle") is apparently referring to a different instance than my component.

                  Sorry for bothering, I hope I can fix all that eventually.

                  • 36. Re: ResourceBundle in Database
                    zerg-spirit

                    Ok, the only way I managed to do it yet is to check inside the private ResourceBundle class if the Locale is dirty using a singleton Control class every time the handleGetObject method is called.
                    Seems to work at the moment, but I'm really not sure it's either elegant or the best way to do it.

                    • 37. Re: ResourceBundle in Database
                      pmuir

                      How are you caching them? I can perhaps extend the tutorial.

                      • 38. Re: ResourceBundle in Database
                        zerg-spirit


                        @Scope(APPLICATION)
                        @BypassInterceptors
                        @Name("org.jboss.seam.core.resourceLoader")
                        public class ExtendedResourceLoader extends
                        org.jboss.seam.core.ResourceLoader {
                        
                         /**
                         * Method called to load a bundle
                         * @param bundleName the name of the bundle to load.
                         * @return the ResourceBundle.
                         */
                         public ResourceBundle loadBundle(final String bundleName) {
                         return new CustomResourceBundle();
                         }
                        
                         /**
                         * Private ResourceBundle fetching messages from the database
                         */
                         private class CustomResourceBundle extends ResourceBundle{
                         java.util.Locale locale;
                         //the Locale of that ResourceBundle
                         Map<String,String> map;
                         //this map store the messages in the database
                        
                         public CustomResourceBundle(){
                         locale = Locale.instance();
                         }
                        
                         @Override
                         public Enumeration<String> getKeys() {
                         if(map == null){
                         map = new HashMap<String,String>();
                         }
                         map = fetchExpressionsForLocale(locale.toString());
                         Vector v = new Vector(map.keySet());
                         return v.elements();
                         }
                        
                         @Override
                         protected Object handleGetObject(String key) {
                         java.util.Locale locale = org.jboss.seam.core.Locale.instance();
                         if(map==null)
                         getKeys();
                         if(DBControl.instance().isDirtyLocale(locale.toString()) || DBControl.instance().isDirtyLocale("ALL")){
                         /*checking if this ResourceBundle is dirty, if so, re-fetching updated datas from the database. It might not be a nice way to check it, but I didn't find an other way.*/
                         getKeys();
                         DBControl.instance().removeDirtyLocale(locale.toString());
                         }
                         if(map.containsKey(key))
                         return map.get(key);
                         else
                         return key;
                         //returning the key if no proper message were found
                         }
                         }
                        
                         /**
                         * Fetching the expressions from the database matching the current Locale
                         * @param locale
                         * @return a map containing the translation
                         */
                         private Map<String,String> fetchExpressionsForLocale(String locale){
                         EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
                         Map<String,String> map = new HashMap<String,String>();
                         boolean found = false;
                         List<Message> messages = entityManager.createQuery("select message from Message message").getResultList();
                         for(Message m: messages){
                         entityManager.refresh(m);
                         /*I'm obliged to do so, cause if 2 Locales are loaded in a short time, and a change was performed in the database, the entityManager seems to use its cache and not the proper data from the db.*/
                         for(Expression e: m.getExpressions()){
                         //seeking the proper Expression for the current Locale
                         if(e.getLanguage().getCode().equals(locale.toString())){
                         map.put(m.getKeyValue(), e.getText());
                         found = true;
                         }
                         }
                         if(!found){
                         //If no proper Expression found for the current locale, adding the expression for the default Locale
                         for(Expression e: m.getExpressions()){
                         if(e.getLanguage().isDefaultLanguage()){
                         map.put(m.getKeyValue(), e.getText());
                         found = true;
                         }
                         }
                         if(!found){
                         //else, we put the key as its own translation
                         map.put(m.getKeyValue(), m.getKeyValue());
                         }
                         }
                         found = false;
                         }
                         return map;
                         }
                        }

                        Note: in my code, a 'Message' is an Entity containing the message keys, and an expression is its translation for a given Locale (Expression table has a link to Language table, Language.code being for example 'en'). Note that I also have a 'default' language to fetch an expression if none was found for a given Locale.
                        Now my little class DBControl, just a singleton to handle 'dirty locales':
                        /**
                         * Little singleton class handling dirty Locales
                         */
                        public class DBControl{
                        
                         private static DBControl instance;
                         private List<String> dirtyLocales;
                        
                         private DBControl(){}
                        
                         public static DBControl instance(){
                         if(instance==null)
                         instance = new DBControl();
                         return instance;
                         }
                        
                         public void setDirtyLocale(String locale){
                         if(dirtyLocales==null)
                         dirtyLocales = new ArrayList<String>();
                         if(!dirtyLocales.contains(locale))
                         dirtyLocales.add(locale);
                         }
                        
                         public void removeDirtyLocale(String locale){
                         if(dirtyLocales!=null){
                         for(String s: dirtyLocales){
                         if(s.equals(locale)){
                         dirtyLocales.remove(s);
                         break;
                         }
                         }
                         }
                         }
                         public boolean isDirtyLocale(String locale){
                         if(dirtyLocales!=null){
                         for(String s: dirtyLocales){
                         if(s.equals(locale))
                         return true;
                         }
                         return false;
                         }
                         else
                         return false;
                         }
                        }


                        Thanks to that class, I can access the 'dirtyLocales' list in both my Beans and my ResourceLoader.

                        Again, don't know if that's really a proper way to do it, waiting for feedback.

                        • 39. Re: ResourceBundle in Database
                          nickarls

                          Hi,

                          I've created a light version of the database-resources and put an @Observer("reload_messages") in my class but when I raise the event, seam expects it to be found in the superclass and throws an "method not found: reload for component: org.jboss.seam.core.resourceLoader". Do I have to fiddle around with @Install or should I redefine something in components.xml?

                          • 40. Re: ResourceBundle in Database
                            nickarls

                            Any theories/comments?

                            • 41. Re: ResourceBundle in Database
                              pmuir

                              Post code - no idea what you are doing.

                              • 42. Re: ResourceBundle in Database
                                nickarls

                                Sorry, I'm so used to you being clairvoyant ;-)

                                I have a reload method which reloads the cached messages

                                @Name("org.jboss.seam.core.resourceLoader")
                                @BypassInterceptors
                                public class DBMessages extends ResourceLoader {
                                
                                 @Observer("reload_messages")
                                 private void reload() { ...
                                


                                But when I raise the event from my admin panel I get the "method not found: reload for component: org.jboss.seam.core.resourceLoader". I'm wondering how to configure for "no, really use my DBMessages, always".

                                • 43. Re: ResourceBundle in Database
                                  pmuir

                                  Check your startup logs and make sure that your org.jboss.seam.core.resourceLoader component is definitely using DBMessages. Otherwise, try breaking around line 60 of org.jboss.seam.crore.Events and see if you can trace why you get that exception.

                                  • 44. Re: ResourceBundle in Database
                                    pmuir

                                    Ah, no, its that the method must be public.