blob: 2830818c98d2f78d699553874aa125c97c1d198b [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation
*
* 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.apache.felix.bundlerepository.impl;
import java.util.*;
import org.apache.felix.bundlerepository.BundleRecord;
import org.apache.felix.bundlerepository.IPackage;
import org.osgi.framework.*;
public class LocalState
{
private BundleContext m_context = null;
private List m_localRecordList = new ArrayList();
public LocalState(BundleContext context)
{
m_context = context;
initialize();
}
public BundleRecord findBundle(String symName, int[] version)
{
for (int i = 0; i < m_localRecordList.size(); i++)
{
BundleRecord brLocal = (BundleRecord) m_localRecordList.get(i);
String localSymName = (String)
brLocal.getAttribute(BundleRecord.BUNDLE_SYMBOLICNAME);
int[] localVersion = Util.parseVersionString((String)
brLocal.getAttribute(BundleRecord.BUNDLE_VERSION));
if ((localSymName != null) &&
localSymName.equals(symName) &&
(Util.compareVersion(localVersion, version) == 0))
{
return brLocal;
}
}
return null;
}
public BundleRecord[] findBundles(String symName)
{
List matchList = new ArrayList();
for (int i = 0; i < m_localRecordList.size(); i++)
{
BundleRecord brLocal = (BundleRecord) m_localRecordList.get(i);
String localSymName = (String)
brLocal.getAttribute(BundleRecord.BUNDLE_SYMBOLICNAME);
if ((localSymName != null) && localSymName.equals(symName))
{
matchList.add(brLocal);
}
}
return (BundleRecord[]) matchList.toArray(new BundleRecord[matchList.size()]);
}
public void update(BundleRecord oldRecord, BundleRecord newRecord)
{
// To update the old record we need to replace it with
// a new one, since BundleRecords are immutable. Make
// a new record that contains the attributes of the new
// record, but is associated with the local bundle of
// the old record.
if (oldRecord instanceof LocalBundleRecord)
{
String[] keys = newRecord.getAttributes();
Map map = new HashMap();
for (int i = 0; i < keys.length; i++)
{
map.put(keys, newRecord.getAttribute(keys[i]));
}
BundleRecord updatedRecord =
new LocalBundleRecord(
map, ((LocalBundleRecord) oldRecord).getBundle());
int idx = m_localRecordList.indexOf(oldRecord);
if (idx >= 0)
{
m_localRecordList.set(idx, updatedRecord);
}
}
}
public LocalBundleRecord findUpdatableBundle(BundleRecord record)
{
// Determine if any bundles with the specified symbolic
// name are already installed locally.
BundleRecord[] localRecords = findBundles(
(String)record.getAttribute(BundleRecord.BUNDLE_SYMBOLICNAME));
if (localRecords != null)
{
// Since there are local bundles with the same symbolic
// name installed, then we must determine if we can
// update an existing bundle or if we must install
// another one. Loop through all local bundles with same
// symbolic name and find the first one that can be updated
// without breaking constraints of existing bundles.
for (int i = 0; i < localRecords.length; i++)
{
// Check to make sure that the version of the target
// record is greater than the local bundle version,
// since we do not want to perform a downgrade.
// int[] vLocal = Util.parseVersionString((String)
// localRecords[i].getAttribute(BundleRecord.BUNDLE_VERSION));
// int[] vTarget = Util.parseVersionString((String)
// record.getAttribute(BundleRecord.BUNDLE_VERSION));
// TODO: VERIFY WHAT IS GOING ON HERE.
// If the target bundle is a newer version and it is
// export compatible with the local bundle, then return it.
if (isUpdatable(localRecords[i], record))
{
return (LocalBundleRecord) localRecords[i];
}
}
}
return null;
}
public boolean isUpdatable(BundleRecord oldVersion, BundleRecord newVersion)
{
// First get all of the potentially resolvable package declarations
// from the local bundles for the old version of the bundle.
Filter[] reqFilters = getResolvableImportDeclarations(oldVersion);
if (reqFilters == null)
{
return true;
}
// Now make sure that all of the resolvable import declarations
// for the old version of the bundle can also be satisfied by
// the new version of the bundle.
Map[] capMaps = (Map[])
newVersion.getAttribute("capability");
if (capMaps == null)
{
return false;
}
MapToDictionary mapDict = new MapToDictionary(null);
for (int reqIdx = 0; reqIdx < reqFilters.length; reqIdx++)
{
boolean satisfied = false;
for (int capIdx = 0; !satisfied && (capIdx < capMaps.length); capIdx++)
{
mapDict.setSourceMap(capMaps[capIdx]);
if (reqFilters[reqIdx].match(mapDict))
{
satisfied = true;
}
}
// If any of the previously resolvable package declarations
// cannot be resolved, then the bundle is not updatable.
if (!satisfied)
{
return false;
}
}
return true;
}
public Filter[] getResolvableImportDeclarations(BundleRecord record)
{
Map[] capMaps = (Map[])
record.getAttribute("capability");
if ((capMaps != null) && (capMaps.length > 0))
{
List filterList = new ArrayList();
// Use brute force to determine if any of the exports
// could possibly resolve any of the imports.
MapToDictionary mapDict = new MapToDictionary(null);
for (int capIdx = 0; capIdx < capMaps.length; capIdx++)
{
boolean added = false;
for (int recIdx = 0; !added && (recIdx < m_localRecordList.size()); recIdx++)
{
BundleRecord brLocal = (BundleRecord) m_localRecordList.get(recIdx);
Filter[] reqFilters = (Filter[])
brLocal.getAttribute("requirement");
for (int reqIdx = 0;
(reqFilters != null) && (reqIdx < reqFilters.length);
reqIdx++)
{
mapDict.setSourceMap(capMaps[capIdx]);
if (reqFilters[reqIdx].match(mapDict))
{
added = true;
filterList.add(reqFilters[reqIdx]);
}
}
}
}
return (Filter[])
filterList.toArray(new Filter[filterList.size()]);
}
return null;
}
public boolean isResolvable(Filter reqFilter)
{
MapToDictionary mapDict = new MapToDictionary(null);
for (int brIdx = 0; brIdx < m_localRecordList.size(); brIdx++)
{
BundleRecord brLocal = (BundleRecord) m_localRecordList.get(brIdx);
Map[] capMaps = (Map[]) brLocal.getAttribute("capability");
for (int capIdx = 0; (capMaps != null) && (capIdx < capMaps.length); capIdx++)
{
mapDict.setSourceMap(capMaps[capIdx]);
if (reqFilter.match(mapDict))
{
return true;
}
}
}
return false;
}
private void initialize()
{
Bundle[] bundles = m_context.getBundles();
for (int i = 0; (bundles != null) && (i < bundles.length); i++)
{
Dictionary dict = bundles[i].getHeaders();
// Create a case-insensitive map.
Map bundleMap = new TreeMap(new Comparator() {
public int compare(Object o1, Object o2)
{
return o1.toString().compareToIgnoreCase(o2.toString());
}
});
for (Enumeration keys = dict.keys(); keys.hasMoreElements(); )
{
Object key = keys.nextElement();
bundleMap.put(key, dict.get(key));
}
// Remove and convert any import package declarations
// into requirement filters.
String target = (String) bundleMap.remove(BundleRecord.IMPORT_PACKAGE);
if (target != null)
{
IPackage[] pkgs = R4Package.parseImportOrExportHeader(target);
Filter[] filters = new Filter[(pkgs == null) ? 0 : pkgs.length];
for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
{
try
{
String low = pkgs[pkgIdx].getVersionLow().isInclusive()
? "(version>=" + pkgs[pkgIdx].getVersionLow() + ")"
: "(!(version<=" + pkgs[pkgIdx].getVersionLow() + ")";
if (pkgs[pkgIdx].getVersionHigh() != null)
{
String high = pkgs[pkgIdx].getVersionHigh().isInclusive()
? "(version<=" + pkgs[pkgIdx].getVersionHigh() + ")"
: "(!(version>=" + pkgs[pkgIdx].getVersionHigh() + ")";
filters[pkgIdx] = m_context.createFilter(
"(&(type=Export-Package)(name="
+ pkgs[pkgIdx].getId() + ")"
+ low + high + ")");
}
else
{
filters[pkgIdx] = m_context.createFilter(
"(&(type=Export-Package)(name="
+ pkgs[pkgIdx].getId() + ")"
+ low + ")");
}
}
catch (InvalidSyntaxException ex)
{
// Ignore, since it should not happen.
}
}
bundleMap.put("requirement", filters);
}
// Remove and convert any export package declarations
// into capability maps.
target = (String) bundleMap.remove(BundleRecord.EXPORT_PACKAGE);
if (target != null)
{
IPackage[] pkgs = R4Package.parseImportOrExportHeader(target);
Map[] capMaps = new Map[(pkgs == null) ? 0 : pkgs.length];
for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
{
// Create a case-insensitive map.
capMaps[pkgIdx] = new TreeMap(new Comparator() {
public int compare(Object o1, Object o2)
{
return o1.toString().compareToIgnoreCase(o2.toString());
}
});
capMaps[pkgIdx].put("type", "Export-Package");
capMaps[pkgIdx].put("name", pkgs[pkgIdx].getId());
capMaps[pkgIdx].put("version", pkgs[pkgIdx].getVersionLow());
}
bundleMap.put("capability", capMaps);
}
// For the system bundle, add a special platform capability.
if (bundles[i].getBundleId() == 0)
{
// Create a case-insensitive map.
Map map = new TreeMap(new Comparator() {
public int compare(Object o1, Object o2)
{
return o1.toString().compareToIgnoreCase(o2.toString());
}
});
map.put(
Constants.FRAMEWORK_VERSION,
m_context.getProperty(Constants.FRAMEWORK_VERSION));
map.put(
Constants.FRAMEWORK_VENDOR,
m_context.getProperty(Constants.FRAMEWORK_VENDOR));
map.put(
Constants.FRAMEWORK_LANGUAGE,
m_context.getProperty(Constants.FRAMEWORK_LANGUAGE));
map.put(
Constants.FRAMEWORK_OS_NAME,
m_context.getProperty(Constants.FRAMEWORK_OS_NAME));
map.put(
Constants.FRAMEWORK_OS_VERSION,
m_context.getProperty(Constants.FRAMEWORK_OS_VERSION));
map.put(
Constants.FRAMEWORK_PROCESSOR,
m_context.getProperty(Constants.FRAMEWORK_PROCESSOR));
// map.put(
// FelixConstants.FELIX_VERSION_PROPERTY,
// m_context.getProperty(FelixConstants.FELIX_VERSION_PROPERTY));
Map[] capMaps = (Map[]) bundleMap.get("capability");
if (capMaps == null)
{
capMaps = new Map[] { map };
}
else
{
Map[] newCaps = new Map[capMaps.length + 1];
newCaps[0] = map;
System.arraycopy(capMaps, 0, newCaps, 1, capMaps.length);
capMaps = newCaps;
}
bundleMap.put("capability", capMaps);
}
m_localRecordList.add(new LocalBundleRecord(bundleMap, bundles[i]));
}
}
public static class LocalBundleRecord extends BundleRecord
{
private Bundle m_bundle = null;
LocalBundleRecord(Map attrMap, Bundle bundle)
{
super(attrMap);
m_bundle = bundle;
}
public Bundle getBundle()
{
return m_bundle;
}
}
}