Applied patch (FELIX-27) to add support for bundle header localization.
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@497281 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index c3c724b..51779bb 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -23,6 +23,7 @@
import java.net.URL;
import java.util.*;
+import org.apache.felix.framework.util.MapToDictionary;
import org.osgi.framework.*;
class BundleImpl implements Bundle
@@ -123,6 +124,11 @@
public Dictionary getHeaders()
{
+ return getHeaders(Locale.getDefault().toString());
+ }
+
+ public Dictionary getHeaders(String locale)
+ {
Object sm = System.getSecurityManager();
if (sm != null)
@@ -130,7 +136,7 @@
((SecurityManager) sm).checkPermission(new AdminPermission(this,
AdminPermission.METADATA));
}
- return m_felix.getBundleHeaders(this);
+ return m_felix.getBundleHeaders(this, locale);
}
public long getLastModified()
@@ -303,8 +309,7 @@
public String getSymbolicName()
{
- return (String) m_felix.getBundleHeaders(this).get(
- Constants.BUNDLE_SYMBOLICNAME);
+ return (String) m_felix.getBundleSymbolicName(this);
}
public boolean hasPermission(Object obj)
@@ -394,25 +399,6 @@
return "[" + getBundleId() +"]";
}
- //
- // PLACE FOLLOWING METHODS INTO PROPER LOCATION ONCE IMPLEMENTED.
- //
-
- public Dictionary getHeaders(String locale)
- {
- // TODO: Implement Bundle.getHeaders(String locale)
- // Should be done after [#FELIX-27] resolution
- Object sm = System.getSecurityManager();
-
- if (sm != null)
- {
- ((SecurityManager) sm).checkPermission(new AdminPermission(this,
- AdminPermission.METADATA));
- }
-
- return null;
- }
-
public boolean equals(Object obj)
{
if (obj instanceof BundleImpl)
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleInfo.java b/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
index 60a7ec3..ca7737c 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleInfo.java
@@ -18,9 +18,12 @@
*/
package org.apache.felix.framework;
-import java.util.Map;
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.moduleloader.IContentLoader;
import org.apache.felix.moduleloader.IModule;
import org.osgi.framework.*;
@@ -32,6 +35,9 @@
private int m_state = 0;
private BundleActivator m_activator = null;
private BundleContext m_context = null;
+ private Map m_cachedHeaders = new HashMap();
+ private long m_cachedHeadersTimestamp;
+
// Indicates whether the bundle is stale, meaning that it has
// been refreshed and completely removed from the framework.
private boolean m_stale = false;
@@ -201,6 +207,143 @@
}
}
+ public Map getCurrentLocalizedHeader(String locale)
+ {
+ synchronized (m_cachedHeaders)
+ {
+ // If the bundle has been updated, clear the cached headers
+ if (getLastModified() > m_cachedHeadersTimestamp)
+ {
+ m_cachedHeaders.clear();
+ }
+ else
+ {
+ // Check if headers for this locale have already been resolved
+ if (m_cachedHeaders.containsKey(locale))
+ {
+ return (Map) m_cachedHeaders.get(locale);
+ }
+ }
+ }
+
+ Map headers;
+ try
+ {
+ Map rawHeaders = m_archive.getRevision(m_archive.getRevisionCount() - 1).getManifestHeader();
+ headers = new HashMap(rawHeaders.size());
+ headers.putAll(rawHeaders);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error reading manifest from bundle archive.",
+ ex);
+ return null;
+ }
+
+ // Check to see if we actually need to localize anything
+ boolean needsLocalization = false;
+ for (Iterator it = headers.values().iterator(); it.hasNext(); )
+ {
+ if (((String) it.next()).startsWith("%"))
+ {
+ needsLocalization = true;
+ break;
+ }
+ }
+
+ if (!needsLocalization)
+ {
+ // If localization is not needed, just cache the headers and return them as-is
+ // Not sure if this is useful
+ updateHeaderCache(locale, headers);
+ return headers;
+ }
+
+ // Do localization here and return the localized headers
+ IContentLoader loader = this.getCurrentModule().getContentLoader();
+
+ String basename = (String) headers.get(Constants.BUNDLE_LOCALIZATION);
+ if (basename == null)
+ {
+ basename = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+ }
+
+ // Create ordered list of files to load properties from
+ List resourceList = createResourceList(basename, locale);
+
+ // Create a merged props file with all available props for this locale
+ Properties mergedProperties = new Properties();
+ for (Iterator it = resourceList.iterator(); it.hasNext(); )
+ {
+ URL temp = loader.getResource(it.next() + ".properties");
+ if (temp == null)
+ {
+ continue;
+ }
+ try
+ {
+ mergedProperties.load(temp.openConnection().getInputStream());
+ }
+ catch (IOException ex)
+ {
+ // File doesn't exist, just continue loop
+ }
+ }
+
+ // Resolve all localized header entries
+ for (Iterator it = headers.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ String value = (String) entry.getValue();
+ if (value.startsWith("%"))
+ {
+ String newvalue;
+ String key = value.substring(value.indexOf("%") + 1);
+ newvalue = mergedProperties.getProperty(key);
+ if (newvalue==null)
+ {
+ newvalue = key;
+ }
+ entry.setValue(newvalue);
+ }
+ }
+
+ updateHeaderCache(locale, headers);
+ return headers;
+ }
+
+ private void updateHeaderCache(String locale, Map localizedHeaders)
+ {
+ synchronized(m_cachedHeaders)
+ {
+ m_cachedHeaders.put(locale, localizedHeaders);
+ m_cachedHeadersTimestamp = System.currentTimeMillis();
+ }
+ }
+
+ private List createResourceList(String basename, String locale)
+ {
+ List result = new ArrayList(4);
+
+ StringTokenizer tokens;
+ StringBuffer tempLocale = new StringBuffer(basename);
+
+ result.add(tempLocale.toString());
+
+ if (locale.length() > 0)
+ {
+ tokens = new StringTokenizer(locale, "_");
+ while (tokens.hasMoreTokens())
+ {
+ tempLocale.append("_").append(tokens.nextToken());
+ result.add(tempLocale.toString());
+ }
+ }
+ return result;
+ }
+
public int getState()
{
return m_state;
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index f4cf15b..24aaab2 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -24,15 +24,13 @@
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.*;
-import org.apache.felix.framework.cache.BundleArchive;
-import org.apache.felix.framework.cache.BundleCache;
-import org.apache.felix.framework.cache.SystemBundleArchive;
+
+import org.apache.felix.framework.cache.*;
import org.apache.felix.framework.searchpolicy.*;
import org.apache.felix.framework.util.*;
import org.apache.felix.framework.util.manifestparser.*;
import org.apache.felix.moduleloader.*;
import org.osgi.framework.*;
-
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.startlevel.StartLevel;
@@ -1028,11 +1026,22 @@
//
/**
- * Implementation for Bundle.getHeaders().
+ * Implementation for Bundle.getSymbolicName().
**/
- protected Dictionary getBundleHeaders(BundleImpl bundle)
+ protected String getBundleSymbolicName(BundleImpl bundle)
{
- return new MapToDictionary(bundle.getInfo().getCurrentHeader());
+ return (String) bundle.getInfo().getCurrentHeader().get(Constants.BUNDLE_SYMBOLICNAME);
+ }
+
+ /**
+ * Get bundle headers and resolve any localized strings from resource bundles.
+ * @param bundle
+ * @param locale
+ * @return localized bundle headers dictionary.
+ **/
+ protected Dictionary getBundleHeaders(BundleImpl bundle, String locale)
+ {
+ return new MapToDictionary(bundle.getInfo().getCurrentLocalizedHeader(locale));
}
/**