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;
}
}