/* * Copyright 2008-2014 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaleidofoundry.core.i18n; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.ResourceBundle; import java.util.ResourceBundle.Control; import org.kaleidofoundry.core.context.RuntimeContext; import org.kaleidofoundry.core.i18n.model.I18nMessageLanguage; import org.kaleidofoundry.core.lang.annotation.Task; import org.kaleidofoundry.core.lang.annotation.TaskLabel; import org.kaleidofoundry.core.lang.annotation.Tasks; import org.kaleidofoundry.core.lang.annotation.ThreadSafe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author jraduget */ @ThreadSafe public class MessageBundleControl extends Control { private static final Logger LOGGER = LoggerFactory.getLogger(MessageBundleControl.class); private final RuntimeContext<I18nMessages> context; /** * @param context */ public MessageBundleControl(final RuntimeContext<I18nMessages> context) { this.context = context; } /* * (non-Javadoc) * @see java.util.ResourceBundle.Control#getFormats(java.lang.String) */ @Override public List<String> getFormats(final String baseName) { final List<String> lformat = new ArrayList<String>(MessageBundleControlFormat.values().length); for (final MessageBundleControlFormat format : MessageBundleControlFormat.values()) { lformat.add(format.name()); } return Collections.unmodifiableList(lformat); } /* * (non-Javadoc) * @see java.util.ResourceBundle.Control#newBundle(java.lang.String, java.util.Locale, java.lang.String, java.lang.ClassLoader, boolean) */ @Override public ResourceBundle newBundle(final String baseName, final Locale locale, final String format, final ClassLoader loader, final boolean reload) throws IllegalAccessException, InstantiationException, IOException { final MessageBundleControlFormat controlFormat = MessageBundleControlFormat.valueOf(format); if (controlFormat == null) { throw new IllegalArgumentException("format: " + format); } if (baseName == null || locale == null || format == null || loader == null) { throw new NullPointerException(); } return newInputStreamBundle(baseName, locale, controlFormat, loader, reload); } /** * inputStream properties loader * * @param baseName * @param locale * @param format * @param loader * @param reload * @return resource bundle * @throws IllegalAccessException * @throws InstantiationException * @throws IOException */ @Tasks(tasks = { @Task(comment = "Make messageBundleControl extensible via {@link Plugin} extention. Load can be done via xml, jpa... ?", labels = TaskLabel.Enhancement), @Task(comment = "Use fileStore to load bundle resource", labels = TaskLabel.Enhancement) }) ResourceBundle newInputStreamBundle(final String baseName, final Locale locale, final MessageBundleControlFormat format, final ClassLoader loader, final boolean reload) throws IllegalAccessException, InstantiationException, IOException { final String bundleName = toBundleName(baseName, locale); final String resourceName = toResourceName(bundleName, format.getExtention()); DefaultMessageBundle messageBundle = null; final Properties properties = new Properties(); InputStream inProperties = null; boolean foundResource = false; if (LOGGER.isDebugEnabled()) { LOGGER.debug("newBundle resolver for"); LOGGER.debug("\tbaseName={}", baseName); LOGGER.debug("\tlocale={}", locale.toString()); LOGGER.debug("\tformat={}", format.name()); LOGGER.debug("\tformat extention={}", format.getExtention()); LOGGER.debug("\treload={}", reload); LOGGER.debug("\t-> resourceName={}", resourceName); } // Loader... try { // standard properties datas.... if (format == MessageBundleControlFormat.STANDARD_PROPERTIES) { inProperties = newUrlInputStream(loader, resourceName, reload); if (inProperties != null) { properties.load(inProperties); foundResource = true; } } // xml properties datas... if (format == MessageBundleControlFormat.XML_PROPERTIES) { inProperties = newUrlInputStream(loader, resourceName, reload); if (inProperties != null) { properties.loadFromXML(inProperties); foundResource = true; } } // jpa entity datas if (format == MessageBundleControlFormat.JPA_ENTITY_PROPERTIES && I18nMessagesProvider.isJpaEnabledForI18n()) { @Task(comment = "create a service injector (local / guice / spring / ejb3 local or remote...)") final I18nMessageController messageService = new I18nMessageController(); final List<I18nMessageLanguage> messagesLanguage = messageService.findMessagesByLocale(baseName, locale); if (!messagesLanguage.isEmpty()) { for (final I18nMessageLanguage ml : messagesLanguage) { properties.put(ml.getMessage().getCode(), ml.getContent()); } foundResource = true; } } } catch (final RuntimeException rie) { // usefull for jpa entity manager error if (LOGGER.isDebugEnabled()) { LOGGER.debug("debug resourceBundle control resolver error: '{}'", rie.getMessage()); } throw rie; } finally { if (inProperties != null) { inProperties.close(); } } if (foundResource) { messageBundle = new DefaultMessageBundle(resourceName, properties, context); LOGGER.debug("\t-> resource found !"); } else { LOGGER.debug("\t-> resource not found..."); } return messageBundle; } /** * Http file resource loading * * @param loader * @param resourceName * @param reload * @return resource input stream * @throws IOException */ InputStream newUrlInputStream(final ClassLoader loader, final String resourceName, final boolean reload) throws IOException { InputStream stream = null; if (reload) { final URL url = loader.getResource(resourceName); if (url != null) { final URLConnection connection = url.openConnection(); if (connection != null) { // Disable caches to get fresh data for reloading. connection.setUseCaches(false); stream = connection.getInputStream(); } } } else { stream = loader.getResourceAsStream(resourceName); } return stream; } /* * (non-Javadoc) * @see java.util.ResourceBundle.Control#getFallbackLocale(java.lang.String, java.util.Locale) */ @Override public Locale getFallbackLocale(final String baseName, final Locale locale) { return locale.equals(Locale.ROOT) ? null : Locale.ROOT; } }