/* | |
* 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.jmood.utils; | |
import java.util.Dictionary; | |
import java.util.Enumeration; | |
import java.util.Hashtable; | |
import java.util.Vector; | |
import org.apache.felix.jmood.AgentContext; | |
import org.apache.felix.jmood.core.BundleNotAvailableException; | |
import org.apache.felix.jmood.core.ServiceNotAvailableException; | |
import org.osgi.framework.Bundle; | |
import org.osgi.framework.Constants; | |
import org.osgi.framework.ServiceReference; | |
import org.osgi.service.packageadmin.ExportedPackage; | |
import org.osgi.service.packageadmin.PackageAdmin; | |
import org.osgi.service.packageadmin.RequiredBundle; | |
/** | |
* This class contains helper methods | |
* | |
* | |
*/ | |
public class InstrumentationSupport { | |
private AgentContext ac; | |
public InstrumentationSupport(AgentContext ac){ | |
this.ac=ac; | |
} | |
/** | |
* <p>For each BundleInfo, this method returns the symbolic name String, which we define as the concatenation of | |
* the getSymbolicName of the <code>Bundle</code> interface and the bundle version as specified | |
* in the bundle header. Both parts are divided by a semicolon. An example would be:</p> | |
* <p> | |
* <code>com.acme.foo;1.0.0</code> | |
* </p> | |
* @param bundles The <code>Bundle</code> array to be converted | |
* @return The String array | |
* @see org.osgi.framework.Bundle#getSymbolicName() | |
*/ | |
public static String[] getSymbolicNames(Bundle[] bundles) { | |
if(bundles==null) return null; | |
String[] names=new String[bundles.length]; | |
for (int i = 0; i < names.length; i++) { | |
names[i]=getSymbolicName(bundles[i]); | |
} | |
return names; | |
} | |
public static String getSymbolicName(Bundle bundle){ | |
return bundle.getSymbolicName()+";"+bundle.getHeaders().get(Constants.BUNDLE_VERSION); | |
} | |
/** | |
* <p> | |
* | |
* OSGi exported packages can be uniquely identified by the tuple (packageName, packageVersion). | |
* This methods returns a String array representing those packages with the following syntax: | |
* </p> | |
* <p> | |
* <i>packageName</i>;<i>packageVersion</i> | |
* </p><p> | |
* where packageName is as returned by the method <i>getName()</i> and packageVersion as returned by the method <i>getVersion()</i> | |
* in package admin's <code>ExportedPackage</code> class. | |
* </p> | |
* @param packages The <code>ExportedPackage</code> array to be converted | |
* @return The String array | |
* @see org.osgi.service.packageadmin.ExportedPackage | |
*/ | |
public static String[] getPackageNames(ExportedPackage[] packages) { | |
if (packages==null) return null; | |
String[] names=new String[packages.length]; | |
for (int i = 0; i < names.length; i++) { | |
names[i]=getPackageName(packages[i]); | |
} | |
return names; | |
} | |
public static String getPackageName(ExportedPackage pkg) { | |
return pkg.getName()+";"+pkg.getVersion().toString(); | |
} | |
/** | |
* <p> | |
* OSGi Services can be registered under more than one interface (objectClass in | |
* the spec). Services have a mandatory unique service id (as defined in the SERVICE_ID property of the org.osgi.framework.Constants interface), during their lifetime (i.e, until they are | |
* garbage collected). To show this information in a consistent way, we use the following String representation | |
* of the service: | |
* </p> | |
* <p> | |
* <i>objectClass1</i>[;<i>objectClass2</i>[;<i>objectClass3</i>...]]:<i>service.id</i> | |
* </p><p> | |
* where objectClass1..objectClassN are the elements of the mandatory objectClass array | |
* included in the service property dictionary (and set by the framework at registration time. The property name is defined in <code>org.osgi.framework.Constants#OBJECTCLASS</code> | |
* </p> | |
* @param services The <code>ServiceReference</code> array to be converted | |
* @return The String array | |
* @see org.osgi.framework.Constants#OBJECTCLASS | |
* @see org.osgi.framework.Constants#SERVICE_ID | |
* @see org.osgi.framework.ServiceReference | |
*/ | |
public static String[] getServiceNames(ServiceReference[] services) { | |
if(services==null) return null; | |
String[] names=new String[services.length]; | |
for (int i = 0; i < names.length; i++) { | |
String[] objectClass=(String[])services[i].getProperty(Constants.OBJECTCLASS); | |
//We asume that the framework always returns a non-empty, non-null array. | |
StringBuffer sb=new StringBuffer(objectClass[0]); | |
for (int j = 1; j < objectClass.length; j++) { | |
sb.append(";"); | |
sb.append(objectClass[j]); | |
} | |
sb.append(services[i].getProperty(Constants.SERVICE_ID)); | |
names[i]=sb.toString(); | |
} | |
return names; | |
} | |
public static ExportedPackage[] getImportedPackages(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException { | |
Vector imported = new Vector(); | |
Bundle[] allBundles = ac.getBundleContext().getBundles(); | |
for (int i=0; i<allBundles.length;i++) { | |
Bundle b=allBundles[i]; | |
ExportedPackage[] eps=ac.getPackageadmin() | |
.getExportedPackages(b); | |
if(eps==null) continue; | |
exported: for (int j=0;j<eps.length;j++) { | |
ExportedPackage ep = eps[j]; | |
Bundle[] imp=ep.getImportingBundles(); | |
if(imp==null) continue; | |
for (int k=0;k<imp.length;k++) { | |
Bundle b2 =imp[k]; | |
if (b2.getBundleId() == bundle.getBundleId()) { | |
imported.add(ep); | |
continue exported; | |
} | |
} | |
} | |
} | |
if (imported.size() == 0) | |
return null; | |
else return (ExportedPackage[])imported.toArray(new ExportedPackage[imported.size()]); | |
} | |
public static Bundle[] getRequiringBundles(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException{ | |
if (bundle==null) throw new IllegalArgumentException("Bundle argument should not be null"); | |
if (bundle.getSymbolicName()==null) return null; | |
RequiredBundle[] required=ac.getPackageadmin().getRequiredBundles(bundle.getSymbolicName()); | |
if (required==null) return null; | |
RequiredBundle b=null; | |
for (int i=0;i<required.length;i++) { | |
if(bundle.getBundleId()==required[i].getBundle().getBundleId()) { | |
b=required[i]; | |
break; | |
} | |
} | |
if(b==null) { | |
ac.error(InstrumentationSupport.class.getName()+": required bundle should not be null!!!!", new Exception()); | |
return null; | |
} | |
return b.getRequiringBundles(); | |
} | |
public static Bundle[] getBundleDependencies(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException{ | |
Bundle[] all=ac.getBundleContext().getBundles(); | |
Vector required=new Vector(); | |
for (int i = 0; i < all.length; i++) { | |
Bundle[] requiring=getRequiringBundles(all[i], ac); | |
if (requiring==null) continue; | |
for (int j = 0; j < requiring.length; j++) { | |
//If the solicited bundle is requiring the bundle all[i] we mark it as required | |
if (requiring[j].getBundleId()==bundle.getBundleId()) required.add(all[i]); | |
} | |
} | |
if (required.size()==0) return null; | |
return (Bundle[])required.toArray(new Bundle[required.size()]); | |
} | |
public static boolean isBundleRequired(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException { | |
if (getRequiredBundle(bundle, ac)==null) return false; | |
return true; | |
} | |
public static boolean isRequiredBundleRemovalPending(Bundle bundle, AgentContext ac)throws ServiceNotAvailableException{ | |
RequiredBundle r=getRequiredBundle(bundle, ac); | |
if (r==null) return false; | |
return r.isRemovalPending(); | |
} | |
public static RequiredBundle getRequiredBundle(Bundle bundle, AgentContext ac)throws ServiceNotAvailableException{ | |
RequiredBundle[] required=ac.getPackageadmin().getRequiredBundles(bundle.getSymbolicName()); | |
if(required==null) return null; | |
for (int i = 0; i < required.length; i++) { | |
if (required[i].getBundle().getBundleId()==bundle.getBundleId()) return required[i]; | |
} | |
return null; | |
} | |
public static String getState(int state) { | |
switch (state) { | |
case Bundle.ACTIVE: | |
return "ACTIVE"; | |
case Bundle.INSTALLED: | |
return "INSTALLED"; | |
case Bundle.RESOLVED: | |
return "RESOLVED"; | |
case Bundle.STARTING: | |
return "STARTING"; | |
case Bundle.STOPPING: | |
return "STOPPING"; | |
case Bundle.UNINSTALLED: | |
return "UNINSTALLED"; | |
} | |
return null; | |
} | |
public static boolean isBundlePersistentlyStarted(Bundle bundle, AgentContext ac)throws ServiceNotAvailableException{ | |
// BUG in KNOPFLERFISH: isPersistentlyStarted throws NPE if called | |
// on System bundle | |
// Workaround: system bundle should always be persistently started, return true | |
if (bundle.getBundleId()==0) return true; | |
else | |
return ac.getStartLevel().isBundlePersistentlyStarted(bundle); | |
// End workaround | |
} | |
public static int getBundleStartLevel(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException { | |
return ac.getStartLevel().getBundleStartLevel(bundle); | |
} | |
public static ExportedPackage[] getExportedPackages(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException{ | |
return ac.getPackageadmin().getExportedPackages(bundle); | |
} | |
public static boolean isFragment(Bundle bundle, AgentContext ac) throws ServiceNotAvailableException{ | |
if(ac.getPackageadmin().getBundleType(bundle)==PackageAdmin.BUNDLE_TYPE_FRAGMENT) return true; | |
return false; | |
} | |
public static Hashtable getHashtable(Dictionary dic){ | |
Hashtable ht = new Hashtable(); | |
for (Enumeration keys = dic.keys(); keys.hasMoreElements();) { | |
Object key = keys.nextElement(); | |
// Not inmutable, but unlikely to change | |
ht.put(key, dic.get(key)); | |
} | |
return ht; | |
} | |
public static long getBundleId(String symbolicName, AgentContext ac) throws BundleNotAvailableException{ | |
if(symbolicName==null) throw new IllegalArgumentException("Symbolic name cannot be null"); | |
String [] s=symbolicName.split(";"); | |
if(s==null||s.length==0) throw new BundleNotAvailableException("Could not find bundle identified by "+symbolicName); | |
Bundle[] bundles=ac.getBundleContext().getBundles(); | |
long id=-1; | |
Vector candidates=new Vector(); | |
for(int i=0;i<bundles.length;i++) { | |
//First find all bundles with symbolicName | |
String name=bundles[i].getSymbolicName(); | |
if(name==null) continue; | |
if (s[0].equals(name)){ | |
candidates.add(new Long(bundles[i].getBundleId())); | |
} | |
} | |
//now search the one that matches the version | |
for(int i=0; i< candidates.size();i++){ | |
long cId=((Long)candidates.elementAt(i)).longValue(); | |
Bundle c=ac.getBundleContext().getBundle(cId); | |
String version=(String)c.getHeaders().get(Constants.BUNDLE_VERSION); | |
if(s.length==1){//no version available | |
ac.debug("no version available for "+symbolicName); | |
if(candidates.size()>1) throw new BundleNotAvailableException("could not distinguish among multiple candidates"); | |
if(candidates.size()==1) { | |
id=cId; | |
break; | |
} | |
} | |
if (version.equals(s[1])) id=cId; | |
} | |
if(id==-1) throw new BundleNotAvailableException("No " + symbolicName+ "installed"); | |
return id; | |
} | |
} |