package org.ops4j.pax.exam.it; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.*; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.packageadmin.PackageAdmin; import org.ops4j.pax.exam.Inject; public class OSGiHelper { /** * The bundle context. */ @Inject private BundleContext context; /** * List of get references. */ private List<ServiceReference> m_references = new ArrayList<ServiceReference>(); public OSGiHelper() { // bundle context expected to be injected } public OSGiHelper( BundleContext context ) { this.context = context; } public void dispose() { // Unget services for( int i = 0; i < m_references.size(); i++ ) { context.ungetService( (ServiceReference) m_references.get( i ) ); } m_references.clear(); } /** * Gets the Bundle Context. * * @return the bundle context. */ public BundleContext getContext() { return context; } /** * Returns the service object of a service provided by the specified bundle, * offering the specified interface and matching the given filter. * * @param bundle the bundle from which the service is searched. * @param itf the interface provided by the searched service. * @param filter an additional filter (can be {@code null}). * * @return the service object provided by the specified bundle, offering the * specified interface and matching the given filter. */ public static Object getServiceObject( Bundle bundle, String itf, String filter ) { ServiceReference ref = getServiceReference( bundle, itf, filter ); if( ref != null ) { return getBundleContext( bundle ).getService( ref ); } else { return null; } } /** * Returns the service objects of the services provided by the specified * bundle, offering the specified interface and matching the given filter. * * @param bundle the bundle from which services are searched. * @param itf the interface provided by the searched services. * @param filter an additional filter (can be {@code null}). * * @return the service objects provided by the specified bundle, offering * the specified interface and matching the given filter. */ public static Object[] getServiceObjects( Bundle bundle, String itf, String filter ) { ServiceReference[] refs = getServiceReferences( bundle, itf, filter ); if( refs != null ) { Object[] list = new Object[refs.length]; for( int i = 0; i < refs.length; i++ ) { list[ i ] = getBundleContext( bundle ).getService( refs[ i ] ); } return list; } else { return new Object[0]; } } /** * Returns the service reference of a service provided by the specified * bundle, offering the specified interface and matching the given filter. * * @param bundle the bundle from which the service is searched. * @param itf the interface provided by the searched service. * @param filter an additional filter (can be {@code null}). * * @return a service reference provided by the specified bundle, offering * the specified interface and matching the given filter. If no * service is found, {@code null} is returned. */ public static ServiceReference getServiceReference( Bundle bundle, String itf, String filter ) { ServiceReference[] refs = getServiceReferences( bundle, itf, filter ); if( refs.length != 0 ) { return refs[ 0 ]; } else { // No service found return null; } } /** * Checks if the service is available. * * @param itf the service interface * * @return <code>true</code> if the service is available, <code>false</code> * otherwise. */ public boolean isServiceAvailable( String itf ) { ServiceReference ref = getServiceReference( itf, null ); return ref != null; } /** * Checks if the service is available. * * @param itf the service interface * @param pid the service pid * * @return <code>true</code> if the service is available, <code>false</code> * otherwise. */ public boolean isServiceAvailableByPID( String itf, String pid ) { ServiceReference ref = getServiceReferenceByPID( itf, pid ); return ref != null; } /** * Returns the service reference of the service provided by the specified * bundle, offering the specified interface and having the given persistent * ID. * * @param bundle the bundle from which the service is searched. * @param itf the interface provided by the searched service. * @param pid the persistent ID of the searched service. * * @return a service provided by the specified bundle, offering the * specified interface and having the given persistent ID. */ public static ServiceReference getServiceReferenceByPID( Bundle bundle, String itf, String pid ) { String filter = "(" + "service.pid" + "=" + pid + ")"; ServiceReference[] refs = getServiceReferences( bundle, itf, filter ); if( refs == null ) { return null; } else if( refs.length == 1 ) { return refs[ 0 ]; } else { throw new IllegalStateException( "A service lookup by PID returned several providers (" + refs.length + ")" + " for " + itf + " with pid=" + pid ); } } /** * Returns the service reference of all the services provided in the * specified bundle, offering the specified interface and matching the given * filter. * * @param bundle the bundle from which services are searched. * @param itf the interface provided by the searched services. * @param filter an additional filter (can be {@code null}). * * @return all the service references provided in the specified bundle, * offering the specified interface and matching the given filter. * If no service matches, an empty array is returned. */ public static ServiceReference[] getServiceReferences( Bundle bundle, String itf, String filter ) { ServiceReference[] refs = null; try { // Get all the service references refs = getBundleContext( bundle ).getServiceReferences( itf, filter ); } catch( InvalidSyntaxException e ) { throw new IllegalArgumentException( "Cannot get service references: " + e.getMessage() ); } if( refs == null ) { return new ServiceReference[0]; } else { return refs; } } /** * Returns the service object of a service provided by the local bundle, * offering the specified interface and matching the given filter. * * @param itf the interface provided by the searched service. * @param filter an additional filter (can be {@code null}). * * @return the service object provided by the local bundle, offering the * specified interface and matching the given filter. */ public Object getServiceObject( String itf, String filter ) { ServiceReference ref = getServiceReference( itf, filter ); if( ref != null ) { m_references.add( ref ); return context.getService( ref ); } else { return null; } } /** * Returns the service object of a service provided by the local bundle, * offering the specified interface . * * @param itf the interface provided by the searched service. * * @return the service object provided by the local bundle, offering the specified interface. */ public <T> T getServiceObject( Class<T> itf ) { ServiceReference ref = getServiceReference( itf.getName(), null ); if( ref != null ) { m_references.add( ref ); return (T) context.getService( ref ); } else { return null; } } /** * Returns the service object associated with this service reference. * * @param ref service reference * * @return the service object. */ public Object getServiceObject( ServiceReference ref ) { if( ref != null ) { m_references.add( ref ); return context.getService( ref ); } else { return null; } } /** * Returns the service objects of the services provided by the local bundle, * offering the specified interface and matching the given filter. * * @param itf the interface provided by the searched services. * @param filter an additional filter (can be {@code null}). * * @return the service objects provided by the local bundle, offering the * specified interface and matching the given filter. */ public Object[] getServiceObjects( String itf, String filter ) { ServiceReference[] refs = getServiceReferences( itf, filter ); if( refs != null ) { Object[] list = new Object[refs.length]; for( int i = 0; i < refs.length; i++ ) { m_references.add( refs[ i ] ); list[ i ] = context.getService( refs[ i ] ); } return list; } else { return new Object[0]; } } /** * Returns the service reference of a service provided by the local bundle, * offering the specified interface and matching the given filter. * * @param itf the interface provided by the searched service. * @param filter an additional filter (can be {@code null}). * * @return a service reference provided by the local bundle, offering the * specified interface and matching the given filter. If no service * is found, {@code null} is returned. */ public ServiceReference getServiceReference( String itf, String filter ) { return getServiceReference( context.getBundle(), itf, filter ); } /** * Returns the service reference of a service provided offering the * specified interface. * * @param itf the interface provided by the searched service. * * @return a service reference provided by the local bundle, offering the * specified interface and matching the given filter. If no service * is found, {@code null} is returned. */ public ServiceReference getServiceReference( String itf ) { return getServiceReference( context.getBundle(), itf, null ); } /** * Returns the service reference of the service provided by the local * bundle, offering the specified interface and having the given persistent * ID. * * @param itf the interface provided by the searched service. * @param pid the persistent ID of the searched service. * * @return a service provided by the local bundle, offering the specified * interface and having the given persistent ID. */ public ServiceReference getServiceReferenceByPID( String itf, String pid ) { return getServiceReferenceByPID( context.getBundle(), itf, pid ); } /** * Returns the service reference of all the services provided in the local * bundle, offering the specified interface and matching the given filter. * * @param itf the interface provided by the searched services. * @param filter an additional filter (can be {@code null}). * * @return all the service references provided in the local bundle, offering * the specified interface and matching the given filter. If no * service matches, an empty array is returned. */ public ServiceReference[] getServiceReferences( String itf, String filter ) { return getServiceReferences( context.getBundle(), itf, filter ); } /** * Gets the package admin exposed by the framework. * Fails if the package admin is not available. * * @return the package admin service. */ public PackageAdmin getPackageAdmin() { PackageAdmin pa = (PackageAdmin) getServiceObject( PackageAdmin.class.getName(), null ); if( pa == null ) { fail( "No package admin available" ); } return pa; } /** * Refresh the packages. * Fails if the package admin service is not available. */ public void refresh() { getPackageAdmin().refreshPackages( null ); } /** * Waits for a service. Fails on timeout. * If timeout is set to 0, it sets the timeout to 10s. * * @param itf the service interface * @param filter the filter * @param timeout the timeout */ public void waitForService( String itf, String filter, long timeout ) { if( timeout == 0 ) { timeout = 10000; // Default 10 secondes. } ServiceReference[] refs = getServiceReferences( itf, filter ); long begin = System.currentTimeMillis(); if( refs.length != 0 ) { return; } else { while( refs.length == 0 ) { try { Thread.sleep( 5 ); } catch( InterruptedException e ) { // Interrupted } long now = System.currentTimeMillis(); if( ( now - begin ) > timeout ) { fail( "Timeout ... no services matching with the request after " + timeout + "ms" ); } refs = getServiceReferences( itf, filter ); } } } /** * Installs a bundle. * Fails if the bundle cannot be installed. * Be aware that you have to uninstall the bundle yourself. * * @param url bundle url * * @return the installed bundle */ public Bundle installBundle( String url ) { try { return context.installBundle( url ); } catch( BundleException e ) { fail( "Cannot install the bundle " + url + " : " + e.getMessage() ); } return null; // Can not happen } /** * Installs a bundle. * Fails if the bundle cannot be installed. * Be aware that you have to uninstall the bundle yourself. * * @param url bundle url * @param stream input stream containing the bundle * * @return the installed bundle */ public Bundle installBundle( String url, InputStream stream ) { try { return context.installBundle( url, stream ); } catch( BundleException e ) { fail( "Cannot install the bundle " + url + " : " + e.getMessage() ); } return null; // Can not happen } /** * Installs and starts a bundle. * Fails if the bundle cannot be installed or an error occurs * during startup. Be aware that you have to uninstall the bundle * yourself. * * @param url the bundle url * * @return the Bundle object. */ public Bundle installAndStart( String url ) { Bundle bundle = installBundle( url ); try { bundle.start(); } catch( BundleException e ) { fail( "Cannot start the bundle " + url + " : " + e.getMessage() ); } return bundle; } /** * Installs and starts a bundle. * Fails if the bundle cannot be installed or an error occurs * during startup. Be aware that you have to uninstall the bundle * yourself. * * @param url the bundle url * @param stream input stream containing the bundle * * @return the Bundle object. */ public Bundle installAndStart( String url, InputStream stream ) { Bundle bundle = installBundle( url, stream ); try { bundle.start(); } catch( BundleException e ) { fail( "Cannot start the bundle " + url + " : " + e.getMessage() ); } return bundle; } /** * Get the bundle by its id. * * @param bundleId the bundle id. * * @return the bundle with the given id. */ public Bundle getBundle( long bundleId ) { return context.getBundle( bundleId ); } /** * Gets a bundle by its symbolic name. * Fails if no bundle matches. * * @param name the symbolic name of the bundle * * @return the bundle object. */ public Bundle getBundle( String name ) { Bundle[] bundles = context.getBundles(); for( int i = 0; i < bundles.length; i++ ) { if( name.equals( bundles[ i ].getSymbolicName() ) ) { return bundles[ i ]; } } fail( "No bundles with the given symbolic name " + name ); return null; // should not happen } /** * Discovers the bundle context for a bundle. If the bundle is an 4.1.0 or greather bundle it should have a method * that just returns the bundle context. Otherwise uses reflection to look for an internal bundle context. * * @param bundle the bundle from which the bundle context is needed * * @return corresponding bundle context or null if bundle context cannot be discovered */ public static BundleContext getBundleContext( final Bundle bundle ) { try { // first try to find the getBundleContext method (OSGi spec >= 4.10) final Method method = bundle.getClass().getDeclaredMethod( "getBundleContext" ); if( !method.isAccessible() ) { method.setAccessible( true ); } return (BundleContext) method.invoke( bundle ); } catch( Exception e ) { // then try to find a field in the bundle that looks like a bundle context try { final Field[] fields = bundle.getClass().getDeclaredFields(); for( Field field : fields ) { if( BundleContext.class.isAssignableFrom( field.getType() ) ) { if( !field.isAccessible() ) { field.setAccessible( true ); } return (BundleContext) field.get( bundle ); } } } catch( IllegalAccessException ignore ) { // ignore } } // well, discovery failed return null; } }