package org.liveSense.server.i18n.service.I18nService;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.LocaleUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.i18n.ResourceBundleProvider;
import org.liveSense.core.BundleProxyClassLoader;
import org.liveSense.core.service.OSGIClassLoaderManager;
import org.liveSense.server.i18n.CompositeProxyResourceBundle;
import org.liveSense.server.i18n.I18N;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(immediate=true)
@Service
public class I18nServiceImpl implements I18nService {
Logger log = LoggerFactory.getLogger(I18nServiceImpl.class);
@Reference(cardinality=ReferenceCardinality.MANDATORY_UNARY, policy=ReferencePolicy.DYNAMIC)
private OSGIClassLoaderManager dynamicClassLoaderManager;
@Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY, policy=ReferencePolicy.DYNAMIC)
ResourceBundleProvider slingI18nService;
BundleContext context = null;
ClassLoader classLoader = I18nServiceImpl.class.getClassLoader();
Map<String, ClassLoader> bundleResourceCaches = new ConcurrentHashMap<String, ClassLoader>();
private ClassLoader getClassLoader(String resourceName, ClassLoader classLoader) {
// if (classLoader != null) {
// return classLoader;
// } else {
// if (bundleResourceCaches.containsKey(resourceName)) {
// return bundleResourceCaches.get(resourceName);
// }
return this.classLoader;
}
@Activate
protected void activate(BundleContext context) {
I18N.resetCache();
this.context = context;
this.classLoader = dynamicClassLoaderManager.getPackageAdminClassLoader(context);
}
@Override
public <T> T create(Class<T> itf) throws IOException {
return I18N.create(itf, (Locale)null, getClassLoader(itf.getName(), null), getResourceBundleInternal(itf.getName(), getLocale(null), getClassLoader(itf.getName(), null)));
}
@Override
public <T> T create(Class<T> itf, Locale locale) throws IOException {
return I18N.create(itf, locale, getClassLoader(itf.getName(), null), getResourceBundleInternal(itf.getName(), getLocale(locale), getClassLoader(itf.getName(), null)));
}
@Override
public <T> T create(Class<T> itf, Locale locale, ClassLoader classLoader) throws IOException {
return I18N.create(itf, locale, classLoader, getResourceBundleInternal(itf.getName(), getLocale(locale), getClassLoader(itf.getName(), classLoader)));
}
@Override
public <T> T create(Class<T> itf, String lang) throws IOException {
return I18N.create(itf, lang, getResourceBundleInternal(itf.getName(), getLocale(LocaleUtils.toLocale(lang)), getClassLoader(itf.getName(), null)));
}
@Override
public <T> T create(Class<T> itf, String lang, ClassLoader classLoader) throws IOException {
return I18N.create(itf, lang, classLoader, getResourceBundleInternal(itf.getName(), getLocale(LocaleUtils.toLocale(lang)), getClassLoader(itf.getName(), classLoader)));
}
private Locale getLocale(Locale locale) {
if (locale != null) {
return locale;
} else
return Locale.getDefault();
}
@SuppressWarnings("unchecked")
private <T> T getClassByName(String className, ClassLoader classLoader) throws ClassNotFoundException, ClassCastException {
Object ret = null;
if (classLoader != null) {
try {
ret = classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
}
}
if (ret == null) {
try {
ret = getClassLoader(className, null).loadClass(className);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException("Class not found: "+className+" (liveSense I18N Service)");
}
}
return (T)ret;
}
private <T> T getClassByName(String className) throws ClassNotFoundException {
return getClassByName(className, null);
}
@Override
public Object create(String className) throws IOException, ClassNotFoundException {
return I18N.create((Class<?>) getClassByName(className), getResourceBundleInternal(className, getLocale(null), getClassLoader(className, null)));
}
@Override
public Object create(String className, Locale locale) throws IOException, ClassNotFoundException {
return I18N.create((Class<?>)getClassByName(className), locale, getResourceBundleInternal(className, getLocale(locale), getClassLoader(className, null)));
}
@Override
public Object create(String className, Locale locale, ClassLoader classLoader) throws IOException, ClassNotFoundException {
return I18N.create((Class<?>)getClassByName(className, classLoader), locale, getClassLoader(className, classLoader), getResourceBundleInternal(className, getLocale(locale), getClassLoader(className, classLoader)));
}
@Override
public Object create(String className, String lang) throws IOException, ClassNotFoundException {
return I18N.create((Class<?>)getClassByName(className), lang, getResourceBundleInternal(className, getLocale(LocaleUtils.toLocale(lang)), getClassLoader(className, null)));
}
@Override
public Object create(String className, String lang, ClassLoader classLoader) throws IOException, ClassNotFoundException {
return I18N.create((Class<?>)getClassByName(className, classLoader), lang, getClassLoader(className, classLoader), getResourceBundleInternal(className, getLocale(LocaleUtils.toLocale(lang)), getClassLoader(className, classLoader)));
}
private ResourceBundle getResourceBundleInternal(String className, Locale locale, ClassLoader classLoader) {
if (slingI18nService != null) {
return slingI18nService.getResourceBundle(className, locale);
} else {
return ResourceBundle.getBundle(className.replaceAll("\\.", "/"), locale, classLoader);
}
}
// Resource Bundle specific
@Override
public ResourceBundle getResourceBundle(String className) throws IOException, ClassNotFoundException {
return getResourceBundleInternal(className, getLocale(null), getClassLoader(className, null));
}
@Override
public ResourceBundle getResourceBundle(String className, Locale locale) throws IOException, ClassNotFoundException {
return getResourceBundleInternal(className, getLocale(locale), getClassLoader(className, null));
}
@Override
public ResourceBundle getResourceBundle(String className, Locale locale, ClassLoader classLoader)
throws IOException, ClassNotFoundException {
return getResourceBundleInternal(className, getLocale(locale), getClassLoader(className, classLoader));
}
@Override
public ResourceBundle getResourceBundle(String className, String lang) throws IOException, ClassNotFoundException {
return getResourceBundleInternal(className, getLocale(I18N.createLocaleFromLang(lang)), getClassLoader(className, null));
}
@Override
public ResourceBundle getResourceBundle(String className, String lang, ClassLoader classLoader) throws IOException,
ClassNotFoundException {
return getResourceBundleInternal(className, getLocale(I18N.createLocaleFromLang(lang)), getClassLoader(className, classLoader));
}
// Composite Resource Loader
Map<Locale, CompositeProxyResourceBundle> resourceBundles = new HashMap<Locale, CompositeProxyResourceBundle>();
Set<String> classNames = new HashSet<String>();
private void addToCache(String className, Locale locale, CompositeProxyResourceBundle crb) {
if (locale == null) locale = Locale.getDefault();
try {
crb.addToCache(className, getResourceBundle(className, locale, getClassLoader(className, null)));
} catch (IOException e) {
log.error("Could not refresh bundle: "+className+" Locale: "+locale == null ? "" : locale.toString()+" Is the resource in the Export-Package list?", e);
} catch (ClassNotFoundException e) {
log.error("Could not refresh bundle: "+className+" Locale: "+locale == null ? "" : locale.toString()+" Is the resource in the Export-Package list?", e);
}
}
private void removeFromCache(String className, Locale locale, CompositeProxyResourceBundle crb) {
if (locale == null) locale = Locale.getDefault();
crb.removeFromCache(className);
}
private CompositeProxyResourceBundle getCompositeProxyResourceBundleFromCache(Locale locale) {
// If CompositeResource bundle does not exists for the given locale, load all message classes for
// the given locale
if (resourceBundles.get(locale) == null) {
CompositeProxyResourceBundle crb = new CompositeProxyResourceBundle();
resourceBundles.put(locale, crb);
// Iterate over all registered classNames and load ResourceBundles for it.
for (String cn : classNames) {
addToCache(cn, locale, crb);
}
}
return resourceBundles.get(locale);
}
@Override
public ResourceBundle getDynamicResourceBundle(Locale locale) {
synchronized (classNames) {
return getCompositeProxyResourceBundleFromCache(locale);
}
}
@Override
public ResourceBundle getDynamicResourceBundle() {
synchronized (classNames) {
return getCompositeProxyResourceBundleFromCache(Locale.getDefault());
}
}
@Override
public void registerResourceBundle(Bundle bundle, String className) {
synchronized (classNames) {
ResourceBundle.clearCache(getClassLoader(className, null));
classNames.add(className);
bundleResourceCaches.put(className, new BundleProxyClassLoader(bundle));
// Iterate over existing locales in cache and add new entry
for (Locale locale : resourceBundles.keySet()) {
addToCache(className, locale, getCompositeProxyResourceBundleFromCache(locale));
}
}
}
@Override
public void unregisterResourceBundle(Bundle bundle, String className) {
synchronized (classNames) {
ResourceBundle.clearCache(getClassLoader(className, null));
classNames.remove(className);
bundleResourceCaches.remove(className);
// Iterate over existing locales in cache and add new entry
for (Locale locale : resourceBundles.keySet()) {
removeFromCache(className, locale, getCompositeProxyResourceBundleFromCache(locale));
}
}
}
}