blob: e5612a9e00c2acd712093fb749af075716c210f2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.scr.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.felix.scr.impl.parser.KXml2SAXParser;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentException;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
/**
* The BundleComponentActivator is helper class to load and unload Components of
* a single bundle. It will read information from the metadata.xml file
* descriptors and create the corresponding managers.
*/
class BundleComponentActivator
{
// global component registration
private ComponentRegistry m_componentRegistry;
// The bundle context owning the registered component
private BundleContext m_context = null;
// This is a list of component instance managers that belong to a particular bundle
private List m_managers = new ArrayList();
// The Configuration Admin tracker providing configuration for components
private ServiceTracker m_configurationAdmin;
// The Configuration Admin tracker providing configuration for components
private ServiceTracker m_logService;
// thread acting upon configurations
private ComponentActorThread m_componentActor;
// true as long as the dispose method is not called
private boolean m_active;
// the logging level
private int m_logLevel;
/**
* Called upon starting of the bundle. This method invokes initialize() which
* parses the metadata and creates the instance managers
*
* @param componentRegistry The <code>ComponentRegistry</code> used to
* register components with to ensure uniqueness of component names
* and to ensure configuration updates.
* @param context The bundle context owning the components
*
* @throws ComponentException if any error occurrs initializing this class
*/
BundleComponentActivator( ComponentRegistry componentRegistry, ComponentActorThread componentActor,
BundleContext context, int logLevel ) throws ComponentException
{
// keep the parameters for later
m_componentRegistry = componentRegistry;
m_componentActor = componentActor;
m_context = context;
// mark this instance active
m_active = true;
// have the Configuration Admin Service handy (if available)
m_configurationAdmin = new ServiceTracker( context, ConfigurationAdmin.class.getName(), null );
m_configurationAdmin.open();
// have the LogService handy (if available)
m_logService = new ServiceTracker( context, Activator.LOGSERVICE_CLASS, null );
m_logService.open();
m_logLevel = logLevel;
// Get the Metadata-Location value from the manifest
String descriptorLocations = ( String ) m_context.getBundle().getHeaders().get( "Service-Component" );
if ( descriptorLocations == null )
{
throw new ComponentException( "Service-Component entry not found in the manifest" );
}
initialize( descriptorLocations );
}
/**
* Gets the MetaData location, parses the meta data and requests the processing
* of binder instances
*
* @param descriptorLocations A comma separated list of locations of
* component descriptors. This must not be <code>null</code>.
*
* @throws IllegalStateException If the bundle has already been uninstalled.
*/
private void initialize( String descriptorLocations )
{
// 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle
StringTokenizer st = new StringTokenizer( descriptorLocations, ", " );
while ( st.hasMoreTokens() )
{
String descriptorLocation = st.nextToken();
URL descriptorURL = m_context.getBundle().getResource( descriptorLocation );
if ( descriptorURL == null )
{
// 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
// fragments, SCR must log an error message with the Log Service, if present, and continue.
log( LogService.LOG_ERROR, "Component descriptor entry '" + descriptorLocation + "' not found", null,
null );
continue;
}
InputStream stream = null;
try
{
stream = descriptorURL.openStream();
BufferedReader in = new BufferedReader( new InputStreamReader( stream ) );
XmlHandler handler = new XmlHandler( m_context.getBundle() );
KXml2SAXParser parser;
parser = new KXml2SAXParser( in );
parser.parseXML( handler );
// 112.4.2 Component descriptors may contain a single, root component element
// or one or more component elements embedded in a larger document
Iterator i = handler.getComponentMetadataList().iterator();
while ( i.hasNext() )
{
ComponentMetadata metadata = ( ComponentMetadata ) i.next();
try
{
// check and reserve the component name
m_componentRegistry.checkComponentName( metadata.getName() );
// validate the component metadata
metadata.validate();
// Request creation of the component manager
ComponentManager manager;
if ( metadata.isFactory() )
{
// 112.2.4 SCR must register a Component Factory
// service on behalf ot the component
// as soon as the component factory is satisfied
manager = new ComponentFactoryImpl( this, metadata, m_componentRegistry );
}
else
{
manager = ManagerFactory.createManager( this, metadata, m_componentRegistry
.createComponentId() );
}
// register the component after validation
m_componentRegistry.registerComponent( metadata.getName(), manager );
m_managers.add( manager );
// enable the component
if ( metadata.isEnabled() )
{
manager.enable();
}
}
catch ( Throwable t )
{
// There is a problem with this particular component, we'll log the error
// and proceed to the next one
log( LogService.LOG_ERROR, "Cannot register Component", metadata, t );
// make sure the name is not reserved any more
m_componentRegistry.unregisterComponent( metadata.getName() );
}
}
}
catch ( IOException ex )
{
// 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
// fragments, SCR must log an error message with the Log Service, if present, and continue.
log( LogService.LOG_ERROR, "Problem reading descriptor entry '" + descriptorLocation + "'", null, ex );
}
catch ( Exception ex )
{
log( LogService.LOG_ERROR, "General problem with descriptor entry '" + descriptorLocation + "'", null,
ex );
}
finally
{
if ( stream != null )
{
try
{
stream.close();
}
catch ( IOException ignore )
{
}
}
}
}
}
/**
* Dispose of this component activator instance and all the component
* managers.
*/
void dispose()
{
if ( m_context == null )
{
return;
}
// mark instance inactive (no more component activations)
m_active = false;
log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [" + m_context.getBundle().getBundleId()
+ "] will destroy " + m_managers.size() + " instances", null, null );
while ( m_managers.size() != 0 )
{
ComponentManager manager = ( ComponentManager ) m_managers.get( 0 );
try
{
m_managers.remove( manager );
manager.dispose();
}
catch ( Exception e )
{
log( LogService.LOG_ERROR, "BundleComponentActivator : Exception invalidating", manager
.getComponentMetadata(), e );
}
finally
{
m_componentRegistry.unregisterComponent( manager.getComponentMetadata().getName() );
}
}
// close the Configuration Admin tracker
if ( m_configurationAdmin != null )
{
m_configurationAdmin.close();
}
log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [" + m_context.getBundle().getBundleId()
+ "] STOPPED", null, null );
m_context = null;
}
/**
* Returns <true> if this instance is active, that is if components may be
* activated for this component. As soon as the {@link #dispose()} is called
* which means this instance is being shutdown. After the call to <code>dispose</code>
* this method always returns <code>false</code>.
*/
boolean isActive()
{
return m_active;
}
/**
* Returns the list of instance references currently associated to this activator
*
* @return the list of instance references
*/
protected List getInstanceReferences()
{
return m_managers;
}
/**
* Returns the BundleContext
*
* @return the BundleContext
*/
protected BundleContext getBundleContext()
{
return m_context;
}
/**
* Returns the <code>ConfigurationAdmin</code> service used to retrieve
* configuration data for components managed by this activator or
* <code>null</code> if no Configuration Admin Service is available in the
* framework.
*/
protected ConfigurationAdmin getConfigurationAdmin()
{
return ( ConfigurationAdmin ) m_configurationAdmin.getService();
}
/**
* Implements the <code>ComponentContext.enableComponent(String)</code>
* method by first finding the component(s) for the <code>name</code> and
* then starting a thread to actually enable all components found.
* <p>
* If no component matching the given name is found the thread is not
* started and the method does nothing.
*
* @param name The name of the component to enable or <code>null</code> to
* enable all components.
*/
void enableComponent( String name )
{
final ComponentManager[] cm = getSelectedComponents( name );
if ( cm == null )
{
return;
}
for ( int i = 0; i < cm.length; i++ )
{
try
{
cm[i].enable();
}
catch ( Throwable t )
{
log( LogService.LOG_ERROR, "Cannot enable component", cm[i].getComponentMetadata(), t );
}
}
}
/**
* Implements the <code>ComponentContext.disableComponent(String)</code>
* method by first finding the component(s) for the <code>name</code> and
* then starting a thread to actually disable all components found.
* <p>
* If no component matching the given name is found the thread is not
* started and the method does nothing.
*
* @param name The name of the component to disable or <code>null</code> to
* disable all components.
*/
void disableComponent( String name )
{
final ComponentManager[] cm = getSelectedComponents( name );
if ( cm == null )
{
return;
}
for ( int i = 0; i < cm.length; i++ )
{
try
{
log( LogService.LOG_DEBUG, "Disabling Component", cm[i].getComponentMetadata(), null );
cm[i].disable();
}
catch ( Throwable t )
{
log( LogService.LOG_ERROR, "Cannot disable component", cm[i].getComponentMetadata(), t );
}
}
}
/**
* Returns an array of {@link ComponentManager} instances which match the
* <code>name</code>. If the <code>name</code> is <code>null</code> an
* array of all currently known component managers is returned. Otherwise
* an array containing a single component manager matching the name is
* returned if one is registered. Finally, if no component manager with the
* given name is registered, <code>null</code> is returned.
*
* @param name The name of the component manager to return or
* <code>null</code> to return an array of all component managers.
*
* @return An array containing one or more component managers according
* to the <code>name</code> parameter or <code>null</code> if no
* component manager with the given name is currently registered.
*/
private ComponentManager[] getSelectedComponents( String name )
{
// if all components are selected
if ( name == null )
{
return (org.apache.felix.scr.impl.ComponentManager[] ) m_managers.toArray( new ComponentManager[m_managers.size()] );
}
if ( m_componentRegistry.getComponent( name ) != null )
{
// otherwise just find it
Iterator it = m_managers.iterator();
while ( it.hasNext() )
{
ComponentManager cm = ( ComponentManager ) it.next();
if ( name.equals( cm.getComponentMetadata().getName() ) )
{
return new ComponentManager[]
{ cm };
}
}
}
// if the component is not known
return null;
}
//---------- Asynchronous Component Handling ------------------------------
/**
* Schedules the given <code>task</code> for asynchrounous execution or
* synchronously runs the task if the thread is not running. If this instance
* is {@link #isActive() not active}, the task is not executed.
*
* @param task The component task to execute
*/
void schedule( Runnable task )
{
if ( isActive() )
{
ComponentActorThread cat = m_componentActor;
if ( cat != null )
{
cat.schedule( task );
}
else
{
log( LogService.LOG_INFO, "Component Actor Thread not running, calling synchronously", null, null );
try
{
synchronized ( this )
{
task.run();
}
}
catch ( Throwable t )
{
log( LogService.LOG_INFO, "Unexpected problem executing task", null, t );
}
}
}
else
{
log( LogService.LOG_INFO, "BundleComponentActivator is not active; not scheduling " + task, null, null );
}
}
/**
* Method to actually emit the log message. If the LogService is available,
* the message will be logged through the LogService. Otherwise the message
* is logged to stdout (or stderr in case of LOG_ERROR level messages),
*
* @param level The log level to log the message at
* @param message The message to log
* @param ex An optional <code>Throwable</code> whose stack trace is written,
* or <code>null</code> to not log a stack trace.
*/
void log( int level, String message, ComponentMetadata metadata, Throwable ex )
{
if ( m_logLevel >= level )
{
// prepend the metadata name to the message
if ( metadata != null )
{
message = "[" + metadata.getName() + "] " + message;
}
Object logger = m_logService.getService();
if ( logger == null )
{
Activator.log( level, getBundleContext().getBundle(), message, ex );
}
else
{
( ( LogService ) logger ).log( level, message, ex );
}
}
}
}