Database-driven resource bundle in Spring
Asked Answered
S

3

7

I have problem to make "database-driven resource bundle" work. In example below TextDAO is properly injected during application start, but when messageSource is accessed, a new Messages object is created - that's the point. How to make this work ?

<!-- message source -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="someapp.bundle.Messages" />
</bean>

Messages.java

@Component
public class Messages extends ListResourceBundle {

    @Autowired
    private TextDAO textDAO;

    public Messages() {
        log.debug("CONSTRUCTOR");
    }

    @Override
    protected Object[][] getContents() {
        // loading messages from DB
        List<Text> texts = textDAO.findAll();  // textDAO is null
        ...
    }
}

RE-OPEN

As Bozho suggest i did my resource bundle as below, but have problem to dynamically reload it. I suppose that ReloadableResourceBundleMessageSource is for properties files, but maybe it is possible to work this, too.

public class DatabaseDrivenMessageSource extends ReloadableResourceBundleMessageSource {

    private Logger log = LoggerFactory.getLogger(getClass());

    private final Map<String, Map<String, String>> properties = new HashMap<String, Map<String, String>>();

    private TextDAO textDAO;

    @Autowired
    public DatabaseDrivenMessageSource(TextDAO textDAO) {
        this.textDAO = textDAO;
        reload();
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        String msg = getText(code, locale);
        MessageFormat result = createMessageFormat(msg, locale);
        return result;
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        return getText(code, locale);
    }

    private String getText(String code, Locale locale) {
        Map<String, String> localized = properties.get(code);
        String textForCurrentLanguage = null;
        if (localized != null) {
            textForCurrentLanguage = localized.get(locale.getLanguage());
            if (textForCurrentLanguage == null) {
                textForCurrentLanguage = localized.get(Locale.ENGLISH.getLanguage());
            }
        }
        return textForCurrentLanguage != null ? textForCurrentLanguage : code;
    }

    public void reload() {
        properties.clear();
        properties.putAll(loadTexts());
    }

    protected Map<String, Map<String, String>> loadTexts() {
        log.debug("loadTexts");
        Map<String, Map<String, String>> m = new HashMap<String, Map<String, String>>();
        List<Text> texts = textDAO.findAll();
        for(Text text: texts) {
            Map<String, String> v = new HashMap<String, String>();
            v.put("en", text.getEn());
            v.put("de", text.getDe());
            m.put(text.getKey(), v);
        }
        return m;
    }
}
Schnorrer answered 31/3, 2011 at 11:21 Comment(3)
the question is not quite clear - I don't get what, why, and how you are attempting to achieve it.Vociferation
Hmm... I need resource bundle that gets messages from db. Thats clear i think. So, in that class i need to inject textDAO to access db, but i see that Messages object is recreated and injected textDAO lost... What is not clear ?Schnorrer
I did not understand where Messages comes into the picture. basename is String, so value=someapp.bundle.Messages is just a string value, not a spring bean.Vociferation
V
3

basename is a string, so it is not a spring bean, and hence no injection there.

What you can try to do is to subclass ReloadableResourceBundleMessageSource and override some methods there (for example - getMessage(..)). The DAO should be injected in the subclass.

Vociferation answered 31/3, 2011 at 14:28 Comment(1)
Oh yes, You are right. I make a terrible mistake with <f:loadBundle basename="myPackage.MessageResources" var="msg"/> from JSF, where basename may be a class.Schnorrer
C
2

ReloadableResourceBundleMessageSource creates an instance of the class named by the basename property. This instance will not have any of its dependencies injected by Spring. That's why the textDAO field in your Messages object is null.

This Spring issue has the source code for an example JDBC backed MessageSource, as an attachment, which you can use instead of ReloadableResourceBundleMessageSource.

Catiline answered 31/3, 2011 at 15:7 Comment(0)
W
0

A bit old, but still relevant... I am using Spring Cloud Config server with JDBC backend as resource bundle for i18n. Zero code. Works awesome!

Wellheeled answered 29/4, 2019 at 12:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.