blob: ad893cf829e7f30e63df3c05a4a206ae4dcfb809 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.felix.inventory.impl.webconsole;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
/**
* The ResourceBundleManager manages resource bundle instance per OSGi Bundle.
* It contains a local cache, for bundles, but when a bundle is being
* unistalled,
* its resources stored in the cache are cleaned up.
*/
public class ResourceBundleManager implements BundleListener
{
private final BundleContext bundleContext;
private final Map resourceBundleCaches;
/**
* Creates a new object and adds self as a bundle listener
*
* @param bundleContext the bundle context of the Web Console.
*/
public ResourceBundleManager(final BundleContext bundleContext)
{
this.bundleContext = bundleContext;
this.resourceBundleCaches = new HashMap();
bundleContext.addBundleListener(this);
}
/**
* Removes the bundle lister.
*/
public void dispose()
{
bundleContext.removeBundleListener(this);
}
/**
* This method is used to retrieve a /cached/ instance of the i18n resource
* associated
* with a given bundle.
*
* @param provider the bundle, provider of the resources
* @param locale the requested locale.
*/
public ResourceBundle getResourceBundle(final Bundle provider)
{
ResourceBundle cache;
final Long key = new Long(provider.getBundleId());
synchronized (resourceBundleCaches)
{
cache = (ResourceBundle) resourceBundleCaches.get(key);
if (cache == null && !resourceBundleCaches.containsKey(key))
{
cache = this.loadResourceBundle(provider);
resourceBundleCaches.put(key, cache);
}
}
return cache;
}
// ---------- BundleListener
/**
* @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
*/
public final void bundleChanged(BundleEvent event)
{
if (event.getType() == BundleEvent.STOPPED)
{
final Long key = new Long(event.getBundle().getBundleId());
synchronized (resourceBundleCaches)
{
resourceBundleCaches.remove(key);
}
}
}
private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
private ResourceBundle loadResourceBundle(final Bundle bundle)
{
final String path = "_" + DEFAULT_LOCALE.toString(); //$NON-NLS-1$
final URL source = (URL) getResourceBundleEntries(bundle).get(path);
if (source != null)
{
try
{
return new PropertyResourceBundle(source.openStream());
}
catch (final IOException ignore)
{
// ignore
}
}
return null;
}
// TODO : Instead of getting all property files, we could just get the one
// for the default locale
private synchronized Map getResourceBundleEntries(final Bundle bundle)
{
String file = (String) bundle.getHeaders().get(Constants.BUNDLE_LOCALIZATION);
if (file == null)
{
file = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
}
// remove leading slash
if (file.startsWith("/")) //$NON-NLS-1$
{
file = file.substring(1);
}
// split path and base name
int slash = file.lastIndexOf('/');
String fileName = file.substring(slash + 1);
String path = (slash <= 0) ? "/" : file.substring(0, slash); //$NON-NLS-1$
HashMap resourceBundleEntries = new HashMap();
Enumeration locales = bundle.findEntries(path, fileName + "*.properties", false); //$NON-NLS-1$
if (locales != null)
{
while (locales.hasMoreElements())
{
URL entry = (URL) locales.nextElement();
// calculate the key
String entryPath = entry.getPath();
final int start = entryPath.lastIndexOf('/') + 1 + fileName.length(); // path,
// slash
// and
// base
// name
final int end = entryPath.length() - 11; // .properties suffix
entryPath = entryPath.substring(start, end);
// the default language is "name.properties" thus the entry
// path is empty and must default to "_"+DEFAULT_LOCALE
if (entryPath.length() == 0)
{
entryPath = "_" + DEFAULT_LOCALE; //$NON-NLS-1$
}
// only add this entry, if the "language" is not provided
// by the main bundle or an earlier bound fragment
if (!resourceBundleEntries.containsKey(entryPath))
{
resourceBundleEntries.put(entryPath, entry);
}
}
}
return resourceBundleEntries;
}
}