Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/BundleComponentActivator.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/BundleComponentActivator.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/BundleComponentActivator.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/BundleComponentActivator.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,659 @@ +/* + * 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.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.StringTokenizer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.felix.scr.impl.config.ComponentHolder; +import org.apache.felix.scr.impl.config.ScrConfiguration; +import org.apache.felix.scr.impl.helper.Logger; +import org.apache.felix.scr.impl.manager.AbstractComponentManager; +import org.apache.felix.scr.impl.manager.DependencyManager; +import org.apache.felix.scr.impl.metadata.ComponentMetadata; +import org.apache.felix.scr.impl.metadata.XmlHandler; +import org.apache.felix.scr.impl.parser.KXml2SAXParser; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +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. + */ +public class BundleComponentActivator implements Logger +{ + // global component registration + private final ComponentRegistry m_componentRegistry; + + // The bundle owning the registered component + private final Bundle m_bundle; + + // The bundle context owning the registered component + private final BundleContext m_context; + + // This is a list of component instance managers that belong to a particular bundle + private final List<ComponentHolder<?>> m_managers = new ArrayList<ComponentHolder<?>>(); + + // The Configuration Admin tracker providing configuration for components + private final ServiceTracker<LogService, LogService> m_logService; + + // thread acting upon configurations + private final ComponentActorThread m_componentActor; + + // true as long as the dispose method is not called + private final AtomicBoolean m_active = new AtomicBoolean(true); + private final CountDownLatch m_closeLatch = new CountDownLatch(1); + + // the configuration + private final ScrConfiguration m_configuration; + + + /** + * 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, ScrConfiguration configuration ) throws ComponentException + { + // keep the parameters for later + m_componentRegistry = componentRegistry; + m_componentActor = componentActor; + m_context = context; + m_bundle = context.getBundle(); + + // have the LogService handy (if available) + m_logService = new ServiceTracker<LogService, LogService>( context, Activator.LOGSERVICE_CLASS, null ); + m_logService.open(); + m_configuration = configuration; + + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] active", + new Object[] {m_bundle.getBundleId()}, null, null, null ); + + // Get the Metadata-Location value from the manifest + String descriptorLocations = ( String ) m_bundle.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 ) + { + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] descriptor locations {1}", + new Object[] {m_bundle.getBundleId(), descriptorLocations}, null, null, null ); + + // 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[] descriptorURLs = findDescriptors( m_bundle, descriptorLocation ); + if ( descriptorURLs.length == 0 ) + { + // 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 ''{0}'' not found", new Object[] + { descriptorLocation }, null, null, null ); + continue; + } + + // load from the descriptors + for ( URL descriptorURL : descriptorURLs ) + { + loadDescriptor( descriptorURL ); + } + } + } + + + /** + * Called outside the constructor so that the m_managers field is completely initialized. + * A component might possibly start a thread to enable other components, which could access m_managers + */ + void initialEnable() + { + //enable all the enabled components + for ( ComponentHolder<?> componentHolder : m_managers ) + { + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] May enable component holder {1}", + new Object[] {m_bundle.getBundleId(), componentHolder.getComponentMetadata().getName()}, null, null, null ); + + if ( componentHolder.getComponentMetadata().isEnabled() ) + { + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] Enabling component holder {1}", + new Object[] {m_bundle.getBundleId(), componentHolder.getComponentMetadata().getName()}, null, null, null ); + + try + { + componentHolder.enableComponents( false ); + } + catch ( Throwable t ) + { + // caught on unhandled RuntimeException or Error + // (e.g. ClassDefNotFoundError) + + // make sure the component is properly disabled, just in case + try + { + componentHolder.disableComponents( false ); + } + catch ( Throwable ignore ) + { + } + + log( LogService.LOG_ERROR, + "BundleComponentActivator : Bundle [{0}] Unexpected failure enabling component holder {1}", + new Object[] { m_bundle.getBundleId(), componentHolder.getComponentMetadata().getName() }, null, null, t ); + } + } + else + { + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] Will not enable component holder {1}", + new Object[] {m_bundle.getBundleId(), componentHolder.getComponentMetadata().getName()}, null, null, null ); + } + } + } + + + /** + * Finds component descriptors based on descriptor location. + * + * @param bundle bundle to search for descriptor files + * @param descriptorLocation descriptor location + * @return array of descriptors or empty array if none found + */ + static URL[] findDescriptors( final Bundle bundle, final String descriptorLocation ) + { + if ( bundle == null || descriptorLocation == null || descriptorLocation.trim().length() == 0 ) + { + return new URL[0]; + } + + // split pattern and path + final int lios = descriptorLocation.lastIndexOf( "/" ); + final String path; + final String filePattern; + if ( lios > 0 ) + { + path = descriptorLocation.substring( 0, lios ); + filePattern = descriptorLocation.substring( lios + 1 ); + } + else + { + path = "/"; + filePattern = descriptorLocation; + } + + // find the entries + final Enumeration<URL> entries = bundle.findEntries( path, filePattern, false ); + if ( entries == null || !entries.hasMoreElements() ) + { + return new URL[0]; + } + + // create the result list + List<URL> urls = new ArrayList<URL>(); + while ( entries.hasMoreElements() ) + { + urls.add( entries.nextElement() ); + } + return urls.toArray( new URL[urls.size()] ); + } + + + private void loadDescriptor( final URL descriptorURL ) + { + // simple path for log messages + final String descriptorLocation = descriptorURL.getPath(); + + InputStream stream = null; + try + { + stream = descriptorURL.openStream(); + + BufferedReader in = new BufferedReader( new InputStreamReader( stream, "UTF-8" ) ); + XmlHandler handler = new XmlHandler( m_bundle, this, getConfiguration().isFactoryEnabled(), getConfiguration().keepInstances() ); + 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 + for ( Object o : handler.getComponentMetadataList() ) + { + ComponentMetadata metadata = ( ComponentMetadata ) o; + ComponentRegistryKey key = null; + try + { + // check and reserve the component name (if not null) + if ( metadata.getName() != null ) + { + key = m_componentRegistry.checkComponentName( m_bundle, metadata.getName() ); + } + + // validate the component metadata + metadata.validate( this ); + + // Request creation of the component manager + ComponentHolder<?> holder = m_componentRegistry.createComponentHolder( this, metadata ); + + // register the component after validation + m_componentRegistry.registerComponentHolder( key, holder ); + m_managers.add( holder ); + + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] ComponentHolder created for {1}", + new Object[] {m_bundle.getBundleId(), metadata.getName()}, null, null, null ); + + } + 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, null, t ); + + // make sure the name is not reserved any more + if ( key != null ) + { + m_componentRegistry.unregisterComponentHolder( key ); + } + } + } + } + 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 ''{0}''", new Object[] + { descriptorLocation }, null, null, ex ); + } + catch ( Exception ex ) + { + log( LogService.LOG_ERROR, "General problem with descriptor entry ''{0}''", new Object[] + { descriptorLocation }, null, 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( int reason ) + { + if ( m_active.compareAndSet( true, false )) + { + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] will destroy {1} instances", new Object[] + { m_bundle.getBundleId(), m_managers.size() }, null, null, null ); + + for (ComponentHolder<?> holder: m_managers ) + { + try + { + holder.disposeComponents( reason ); + } + catch ( Exception e ) + { + log( LogService.LOG_ERROR, "BundleComponentActivator : Exception invalidating", holder + .getComponentMetadata(), null, e ); + } + finally + { + m_componentRegistry.unregisterComponentHolder( m_bundle, holder.getComponentMetadata() + .getName() ); + } + + } + + log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle [{0}] STOPPED", new Object[] + {m_bundle.getBundleId()}, null, null, null ); + + m_logService.close(); + m_closeLatch.countDown(); + } + else + { + try + { + m_closeLatch.await(m_configuration.lockTimeout(), TimeUnit.MILLISECONDS); + } + catch ( InterruptedException e ) + { + //ignore interruption during concurrent shutdown. + } + } + + } + + + /** + * Returns <true> if this instance is active, that is if components + * may be activated for this component. The active flag is set early + * in the constructor indicating the activator is basically active + * (not fully setup, though) and reset early in the process of + * {@link #dispose(int) disposing} this instance. + */ + public boolean isActive() + { + return m_active.get(); + } + + + /** + * Returns the BundleContext + * + * @return the BundleContext + */ + public BundleContext getBundleContext() + { + return m_context; + } + + + public ScrConfiguration getConfiguration() + { + return m_configuration; + } + + + /** + * Implements the <code>ComponentContext.enableComponent(String)</code> + * method by first finding the component(s) for the <code>name</code> and + * enabling them. The enable method will schedule activation. + * <p> + * + * @param name The name of the component to enable or <code>null</code> to + * enable all components. + */ + public void enableComponent( final String name ) + { + final List<ComponentHolder<?>> holder = getSelectedComponents( name ); + for ( ComponentHolder<?> aHolder : holder ) + { + try + { + log( LogService.LOG_DEBUG, "Enabling Component", aHolder.getComponentMetadata(), null, null ); + aHolder.enableComponents( true ); + } + catch ( Throwable t ) + { + log( LogService.LOG_ERROR, "Cannot enable component", aHolder.getComponentMetadata(), null, t ); + } + } + } + + + /** + * Implements the <code>ComponentContext.disableComponent(String)</code> + * method by first finding the component(s) for the <code>name</code> and + * disabling them. The disable method will schedule deactivation + * <p> + * + * @param name The name of the component to disable or <code>null</code> to + * disable all components. + */ + public void disableComponent( final String name ) + { + final List<ComponentHolder<?>> holder = getSelectedComponents( name ); + for ( ComponentHolder<?> aHolder : holder ) + { + try + { + log( LogService.LOG_DEBUG, "Disabling Component", aHolder.getComponentMetadata(), null, null ); + aHolder.disableComponents( true ); + } + catch ( Throwable t ) + { + log( LogService.LOG_ERROR, "Cannot disable component", aHolder.getComponentMetadata(), null, t ); + } + } + } + + + /** + * Returns an array of {@link ComponentHolder} 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 List<ComponentHolder<?>> getSelectedComponents( String name ) + { + // if all components are selected + if ( name == null ) + { + return m_managers; + } + + ComponentHolder<?> componentHolder = m_componentRegistry.getComponentHolder( m_bundle, name ); + if (componentHolder != null) + { + return Collections.<ComponentHolder<?>>singletonList( componentHolder ); + } + + // if the component is not known + return Collections.emptyList(); + } + + + //---------- Component ID support + + public long registerComponentId(AbstractComponentManager<?> componentManager) { + return m_componentRegistry.registerComponentId(componentManager); + } + + public void unregisterComponentId(AbstractComponentManager<?> componentManager) { + m_componentRegistry.unregisterComponentId(componentManager.getId()); + } + + //---------- 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 + */ + public void schedule( Runnable task ) + { + if ( isActive() ) + { + ComponentActorThread cat = m_componentActor; + if ( cat != null ) + { + cat.schedule( task ); + } + else + { + log( LogService.LOG_DEBUG, "Component Actor Thread not running, calling synchronously", null, null, null ); + try + { + synchronized ( this ) + { + task.run(); + } + } + catch ( Throwable t ) + { + log( LogService.LOG_WARNING, "Unexpected problem executing task", null, null, t ); + } + } + } + else + { + log( LogService.LOG_WARNING, "BundleComponentActivator is not active; not scheduling {0}", new Object[] + { task }, null, null, null ); + } + } + + + /** + * Returns <code>true</code> if logging for the given level is enabled. + */ + public boolean isLogEnabled( int level ) + { + return m_configuration.getLogLevel() >= level; + } + + + /** + * 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 pattern The <code>java.text.MessageFormat</code> message format + * string for preparing the message + * @param arguments The format arguments for the <code>pattern</code> + * string. + * @param componentId + * @param ex An optional <code>Throwable</code> whose stack trace is written, + */ + public void log( int level, String pattern, Object[] arguments, ComponentMetadata metadata, Long componentId, Throwable ex ) + { + if ( isLogEnabled( level ) ) + { + final String message = MessageFormat.format( pattern, arguments ); + log( level, message, metadata, componentId, ex ); + } + } + + + /** + * 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 componentId + * @param ex An optional <code>Throwable</code> whose stack trace is written, + */ + public void log( int level, String message, ComponentMetadata metadata, Long componentId, Throwable ex ) + { + if ( isLogEnabled( level ) ) + { + // prepend the metadata name to the message + if ( metadata != null ) + { + if ( componentId != null ) + { + message = "[" + metadata.getName() + "(" + componentId + ")] " + message; + } + else + { + message = "[" + metadata.getName() + "] " + message; + } + } + + ServiceTracker<LogService, LogService> logService = m_logService; + if ( logService != null ) + { + LogService logger = logService.getService(); + if ( logger == null ) + { + Activator.log( level, m_bundle, message, ex ); + } + else + { + logger.log( level, message, ex ); + } + } + else + { + // BCA has been disposed off, bundle context is probably invalid. Try to log something. + Activator.log( level, null, message, ex ); + } + } + } + + public void missingServicePresent( ServiceReference<?> serviceReference ) + { + m_componentRegistry.missingServicePresent( serviceReference, m_componentActor ); + } + + public <T> void registerMissingDependency( DependencyManager<?, T> dependencyManager, ServiceReference<T> serviceReference, int trackingCount ) + { + m_componentRegistry.registerMissingDependency(dependencyManager, serviceReference, trackingCount ); + } +}
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentActorThread.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentActorThread.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentActorThread.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentActorThread.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,154 @@ +/* + * 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.util.LinkedList; + +import org.osgi.service.log.LogService; + + +/** + * The <code>ComponentActorThread</code> is the thread used to act upon registered + * components of the service component runtime. + */ +class ComponentActorThread implements Runnable +{ + + // sentinel task to terminate this thread + private static final Runnable TERMINATION_TASK = new Runnable() + { + public void run() + { + } + + + public String toString() + { + return "Component Actor Terminator"; + } + }; + + // the queue of Runnable instances to be run + private LinkedList<Runnable> tasks; + + + ComponentActorThread() + { + tasks = new LinkedList<Runnable>(); + } + + + // waits on Runnable instances coming into the queue. As instances come + // in, this method calls the Runnable.run method, logs any exception + // happening and keeps on waiting for the next Runnable. If the Runnable + // taken from the queue is this thread instance itself, the thread + // terminates. + public void run() + { + Activator.log( LogService.LOG_DEBUG, null, "Starting ComponentActorThread", null ); + + for ( ;; ) + { + final Runnable task; + synchronized ( tasks ) + { + while ( tasks.isEmpty() ) + { + try + { + tasks.wait(); + } + catch ( InterruptedException ie ) + { + Thread.currentThread().interrupt(); + // don't care + } + } + + task = tasks.removeFirst(); + } + + try + { + // return if the task is this thread itself + if ( task == TERMINATION_TASK ) + { + Activator.log( LogService.LOG_DEBUG, null, "Shutting down ComponentActorThread", null ); + return; + } + + // otherwise execute the task, log any issues + Activator.log( LogService.LOG_DEBUG, null, "Running task: " + task, null ); + task.run(); + } + catch ( Throwable t ) + { + Activator.log( LogService.LOG_ERROR, null, "Unexpected problem executing task " + task, t ); + } + finally + { + synchronized ( tasks ) + { + tasks.notifyAll(); + } + } + } + } + + + // cause this thread to terminate by adding this thread to the end + // of the queue + void terminate() + { + schedule( TERMINATION_TASK ); + synchronized ( tasks ) + { + while ( !tasks.isEmpty() ) + { + try + { + tasks.wait(); + } + catch ( InterruptedException e ) + { + Thread.currentThread().interrupt(); + Activator.log( LogService.LOG_ERROR, null, "Interrupted exception waiting for queue to empty", e ); + } + } + } + } + + + // queue the given runnable to be run as soon as possible + void schedule( Runnable task ) + { + synchronized ( tasks ) + { + // append to the task queue + tasks.add( task ); + + Activator.log( LogService.LOG_DEBUG, null, "Adding task [{0}] as #{1} in the queue" + , new Object[] {task, tasks.size()}, null ); + + // notify the waiting thread + tasks.notifyAll(); + } + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistry.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistry.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistry.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistry.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,621 @@ +/* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.felix.scr.impl.config.ComponentHolder; +import org.apache.felix.scr.impl.config.ConfigurableComponentHolder; +import org.apache.felix.scr.impl.config.ConfigurationSupport; +import org.apache.felix.scr.impl.manager.AbstractComponentManager; +import org.apache.felix.scr.impl.manager.DependencyManager; +import org.apache.felix.scr.impl.metadata.ComponentMetadata; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentConstants; +import org.osgi.service.component.ComponentException; +import org.osgi.service.log.LogService; + + +/** + * The <code>ComponentRegistry</code> class acts as the global registry for + * components by name and by component ID. + */ +public class ComponentRegistry implements ServiceListener +{ + + // the name of the ConfigurationAdmin service + public static final String CONFIGURATION_ADMIN = "org.osgi.service.cm.ConfigurationAdmin"; + + // the bundle context + private BundleContext m_bundleContext; + + /** + * The map of known components indexed by component name. The values are + * either null (for name reservations) or implementations + * of the {@link ComponentHolder} interface. + * <p> + * The {@link #checkComponentName(String)} will first add an entry to this + * map with null value to reserve the name. After setting up + * the component, the {@link #registerComponentHolder(String, ComponentHolder)} + * method replaces the value of the named entry with the actual + * {@link ComponentHolder}. + * + * @see #checkComponentName(String) + * @see #registerComponentHolder(String, ComponentHolder) + * @see #unregisterComponentHolder(String) + */ + private final Map<ComponentRegistryKey, ComponentHolder<?>> m_componentHoldersByName; + + /** + * The map of known components indexed by component configuration pid. The values are + * Sets of the {@link ComponentHolder} interface. Normally, the configuration pid + * is the component name, but since DS 1.2 (OSGi 4.3), a component may specify a specific + * pid, and it is possible that different components refer to the same pid. That's why + * the values of this map are Sets of ComponentHolders, allowing to lookup all components + * which are using a given configuration pid. + * This map is used when the ConfigurationSupport detects that a CM pid is updated. When + * a PID is updated, the ConfigurationSupport listener class invokes the + * {@link #getComponentHoldersByPid(String)} method which returns an iterator over all + * components that are using the given pid for configuration. + * <p> + * + * @see #registerComponentHolder(String, ComponentHolder) + * @see #unregisterComponentHolder(String) + * @see ConfigurationSupport#configurationEvent(org.osgi.service.cm.ConfigurationEvent) + */ + private final Map<String, Set<ComponentHolder<?>>> m_componentHoldersByPid; + + /** + * Map of components by component ID. This map indexed by the component + * ID number (<code>java.lang.Long</code>) contains the actual + * {@link AbstractComponentManager} instances existing in the system. + * + * @see #registerComponentId(AbstractComponentManager) + * @see #unregisterComponentId(long) + */ + private final Map<Long, AbstractComponentManager<?>> m_componentsById; + + /** + * Counter to setup the component IDs as issued by the + * {@link #registerComponentId(AbstractComponentManager)} method. This + * counter is only incremented. + */ + private long m_componentCounter = -1; + + // ConfigurationAdmin support -- created on demand upon availability of + // the ConfigurationAdmin service + private ConfigurationSupport configurationSupport; + + private final Map<ServiceReference<?>, List<Entry<?, ?>>> m_missingDependencies = new HashMap<ServiceReference<?>, List<Entry<?, ?>>>( ); + + protected ComponentRegistry( final BundleContext context ) + { + m_bundleContext = context; + m_componentHoldersByName = new HashMap<ComponentRegistryKey, ComponentHolder<?>>(); + m_componentHoldersByPid = new HashMap<String, Set<ComponentHolder<?>>>(); + m_componentsById = new HashMap<Long, AbstractComponentManager<?>>(); + + // keep me informed on ConfigurationAdmin state changes + try + { + context.addServiceListener(this, "(objectclass=" + CONFIGURATION_ADMIN + ")"); + } + catch (InvalidSyntaxException ise) + { + // not expected (filter is tested valid) + } + + // If the Configuration Admin Service is already registered, setup + // configuration support immediately + if (context.getServiceReference(CONFIGURATION_ADMIN) != null) + { + getOrCreateConfigurationSupport(); + } + } + + public void dispose() + { + m_bundleContext.removeServiceListener(this); + + if (configurationSupport != null) + { + configurationSupport.dispose(); + configurationSupport = null; + } + } + + + + //---------- ComponentManager registration by component Id + + /** + * Assigns a unique ID to the component, internally registers the + * component under that ID and returns the assigned component ID. + * + * @param componentManager The {@link AbstractComponentManager} for which + * to assign a component ID and which is to be internally registered + * + * @return the assigned component ID + */ + final long registerComponentId( final AbstractComponentManager<?> componentManager ) + { + long componentId; + synchronized ( m_componentsById ) + { + componentId = ++m_componentCounter; + + m_componentsById.put( componentId, componentManager ); + } + + return componentId; + } + + + /** + * Unregisters the component with the given component ID from the internal + * registry. After unregistration, the component ID should be considered + * invalid. + * + * @param componentId The ID of the component to be removed from the + * internal component registry. + */ + final void unregisterComponentId( final long componentId ) + { + synchronized ( m_componentsById ) + { + m_componentsById.remove( componentId ); + } + } + + + //---------- ComponentHolder registration by component name + + /** + * Checks whether the component name is "globally" unique or not. If it is + * unique, it is reserved until the actual component is registered with + * {@link #registerComponentHolder(String, ComponentHolder)} or until + * it is unreserved by calling {@link #unregisterComponentHolder(String)}. + * If a component with the same name has already been reserved or registered + * a ComponentException is thrown with a descriptive message. + * + * @param bundle the bundle registering the component + * @param name the component name to check and reserve + * @throws ComponentException if the name is already in use by another + * component. + */ + final ComponentRegistryKey checkComponentName( final Bundle bundle, final String name ) + { + // register the name if no registration for that name exists already + final ComponentRegistryKey key = new ComponentRegistryKey( bundle, name ); + ComponentHolder<?> existingRegistration = null; + boolean present; + synchronized ( m_componentHoldersByName ) + { + present = m_componentHoldersByName.containsKey( key ); + if ( !present ) + { + m_componentHoldersByName.put( key, null ); + } + else + { + existingRegistration = m_componentHoldersByName.get( key ); + } + } + + // there was a registration already, throw an exception and use the + // existing registration to provide more information if possible + if ( present ) + { + String message = "The component name '" + name + "' has already been registered"; + + if ( existingRegistration != null ) + { + Bundle cBundle = existingRegistration.getActivator().getBundleContext().getBundle(); + ComponentMetadata cMeta = existingRegistration.getComponentMetadata(); + + StringBuffer buf = new StringBuffer( message ); + buf.append( " by Bundle " ).append( cBundle.getBundleId() ); + if ( cBundle.getSymbolicName() != null ) + { + buf.append( " (" ).append( cBundle.getSymbolicName() ).append( ")" ); + } + buf.append( " as Component of Class " ).append( cMeta.getImplementationClassName() ); + message = buf.toString(); + } + + throw new ComponentException( message ); + } + + return key; + } + + + /** + * Registers the given component under the given name. If the name has not + * already been reserved calling {@link #checkComponentName(String)} this + * method throws a {@link ComponentException}. + * + * @param name The name to register the component under + * @param componentHolder The component to register + * + * @throws ComponentException if the name has not been reserved through + * {@link #checkComponentName(String)} yet. + */ + final void registerComponentHolder( final ComponentRegistryKey key, ComponentHolder<?> componentHolder ) + { + Activator.log(LogService.LOG_DEBUG, null, + "Registering component with pid {0} for bundle {1}", + new Object[] {componentHolder.getComponentMetadata().getConfigurationPid(), key.getBundleId()}, + null); + synchronized ( m_componentHoldersByName ) + { + // only register the component if there is a m_registration for it ! + if ( m_componentHoldersByName.get( key ) != null ) + { + // this is not expected if all works ok + throw new ComponentException( "The component name '{0}" + componentHolder.getComponentMetadata().getName() + + "' has already been registered." ); + } + + m_componentHoldersByName.put( key, componentHolder ); + } + + synchronized (m_componentHoldersByPid) + { + // See if the component declares a specific configuration pid (112.4.4 configuration-pid) + List<String> configurationPids = componentHolder.getComponentMetadata().getConfigurationPid(); + + for ( String configurationPid: configurationPids ) + { + // Since several components may refer to the same configuration pid, we have to + // store the component holder in a Set, in order to be able to lookup every + // components from a given pid. + Set<ComponentHolder<?>> set = m_componentHoldersByPid.get( configurationPid ); + if ( set == null ) + { + set = new HashSet<ComponentHolder<?>>(); + m_componentHoldersByPid.put( configurationPid, set ); + } + set.add( componentHolder ); + } + } + + if (configurationSupport != null) + { + configurationSupport.configureComponentHolder(componentHolder); + } + + } + + /** + * Returns the component registered under the given name or <code>null</code> + * if no component is registered yet. + */ + public final ComponentHolder<?> getComponentHolder( final Bundle bundle, final String name ) + { + synchronized ( m_componentHoldersByName ) + { + return m_componentHoldersByName.get( new ComponentRegistryKey( bundle, name ) ); + } + } + + /** + * Returns the set of ComponentHolder instances whose configuration pids are matching + * the given pid. + * @param pid the pid candidate + * @return the set of ComponentHolders matching the singleton pid supplied + */ + public final Collection<ComponentHolder<?>> getComponentHoldersByPid(TargetedPID targetedPid) + { + String pid = targetedPid.getServicePid(); + Set<ComponentHolder<?>> componentHoldersUsingPid = new HashSet<ComponentHolder<?>>(); + synchronized (m_componentHoldersByPid) + { + Set<ComponentHolder<?>> set = m_componentHoldersByPid.get(pid); + // only return the entry if non-null and not a reservation + if (set != null) + { + for (ComponentHolder<?> holder: set) + { + if (targetedPid.matchesTarget(holder)) + { + componentHoldersUsingPid.add( holder ); + } + } + } + } + return componentHoldersUsingPid; + } + + /** + * Returns an array of all values currently stored in the component holders + * map. The entries in the array are either String types for component + * name reservations or {@link ComponentHolder} instances for actual + * holders of components. + */ + public final List<ComponentHolder<?>> getComponentHolders() + { + List<ComponentHolder<?>> all = new ArrayList<ComponentHolder<?>>(); + synchronized ( m_componentHoldersByName ) + { + all.addAll(m_componentHoldersByName.values()); + } + return all; + } + + public final List<ComponentHolder<?>> getComponentHolders(Bundle...bundles) + { + List<ComponentHolder<?>> all =getComponentHolders(); + List<ComponentHolder<?>> holders = new ArrayList<ComponentHolder<?>>(); + for ( ComponentHolder<?> holder: all) + { + BundleComponentActivator activator = holder.getActivator(); + if (activator != null) + { + Bundle holderBundle = activator.getBundleContext().getBundle(); + for (Bundle b: bundles) + { + if (b == holderBundle) + { + holders.add(holder); + } + } + } + } + return holders; + } + + + /** + * Removes the component registered under that name. If no component is + * yet registered but the name is reserved, it is unreserved. + * <p> + * After calling this method, the name can be reused by other components. + */ + final void unregisterComponentHolder( final Bundle bundle, final String name ) + { + unregisterComponentHolder( new ComponentRegistryKey( bundle, name ) ); + } + + + /** + * Removes the component registered under that name. If no component is + * yet registered but the name is reserved, it is unreserved. + * <p> + * After calling this method, the name can be reused by other components. + */ + final void unregisterComponentHolder( final ComponentRegistryKey key ) + { + ComponentHolder<?> component; + synchronized ( m_componentHoldersByName ) + { + component = m_componentHoldersByName.remove( key ); + } + + if (component != null) { + Activator.log(LogService.LOG_DEBUG, null, + "Unregistering component with pid {0} for bundle {1}", + new Object[] {component.getComponentMetadata().getConfigurationPid(), key.getBundleId()}, null); + synchronized (m_componentHoldersByPid) + { + List<String> configurationPids = component.getComponentMetadata().getConfigurationPid(); + for ( String configurationPid: configurationPids ) + { + Set<ComponentHolder<?>> componentsForPid = m_componentHoldersByPid.get( configurationPid ); + if ( componentsForPid != null ) + { + componentsForPid.remove( component ); + if ( componentsForPid.size() == 0 ) + { + m_componentHoldersByPid.remove( configurationPid ); + } + } + } + } + } + } + + //---------- base configuration support + + /** + * Factory method to issue {@link ComponentHolder} instances to manage + * components described by the given component <code>metadata</code>. + */ + public <S> ComponentHolder<S> createComponentHolder( BundleComponentActivator activator, ComponentMetadata metadata ) + { + return new ConfigurableComponentHolder<S>(activator, metadata); + } + + + //---------- ServiceListener + + /** + * Called if the Configuration Admin service changes state. This + * implementation is mainly interested in the Configuration Admin service + * being registered <i>after</i> the Declarative Services setup to be able + * to forward existing configuration. + * + * @param event The service change event + */ + public void serviceChanged(ServiceEvent event) + { + if (event.getType() == ServiceEvent.REGISTERED) + { + ConfigurationSupport configurationSupport = getOrCreateConfigurationSupport(); + + final ServiceReference<ConfigurationAdmin> caRef = (ServiceReference<ConfigurationAdmin>) event.getServiceReference(); + final ConfigurationAdmin service = m_bundleContext.getService(caRef); + if (service != null) + { + try + { + configurationSupport.configureComponentHolders(caRef, service); + } + finally + { + m_bundleContext.ungetService(caRef); + } + } + } + else if (event.getType() == ServiceEvent.UNREGISTERING) + { + disposeConfigurationSupport(); + } + } + + //---------- Helper method + + /** + * Returns <code>true</code> if the <code>bundle</code> is to be considered + * active from the perspective of declarative services. + * <p> + * As of R4.1 a bundle may have lazy activation policy which means a bundle + * remains in the STARTING state until a class is loaded from that bundle + * (unless that class is declared to not cause the bundle to start). And + * thus for DS 1.1 this means components are to be loaded for lazily started + * bundles being in the STARTING state (after the LAZY_ACTIVATION event) has + * been sent. Hence DS must consider a bundle active when it is really + * active and when it is a lazily activated bundle in the STARTING state. + * + * @param bundle The bundle check + * @return <code>true</code> if <code>bundle</code> is not <code>null</code> + * and the bundle is either active or has lazy activation policy + * and is in the starting state. + * + * @see <a href="https://issues.apache.org/jira/browse/FELIX-1666">FELIX-1666</a> + */ + static boolean isBundleActive( final Bundle bundle ) + { + if ( bundle != null ) + { + if ( bundle.getState() == Bundle.ACTIVE ) + { + return true; + } + + if ( bundle.getState() == Bundle.STARTING ) + { + // according to the spec the activationPolicy header is only + // set to request a bundle to be lazily activated. So in this + // simple check we just verify the header is set to assume + // the bundle is considered a lazily activated bundle + return bundle.getHeaders().get( Constants.BUNDLE_ACTIVATIONPOLICY ) != null; + } + } + + // fall back: bundle is not considered active + return false; + } + + private ConfigurationSupport getOrCreateConfigurationSupport() + { + if (configurationSupport == null) + { + configurationSupport = new ConfigurationSupport(m_bundleContext, this); + } + return configurationSupport; + } + + private void disposeConfigurationSupport() + { + if (configurationSupport != null) + { + this.configurationSupport.dispose(); + this.configurationSupport = null; + } + } + + public synchronized <T> void missingServicePresent( final ServiceReference<T> serviceReference, ComponentActorThread actor ) + { + final List<Entry<?, ?>> dependencyManagers = m_missingDependencies.remove( serviceReference ); + if ( dependencyManagers != null ) + { + actor.schedule( new Runnable() + { + + public void run() + { + for ( Entry<?, ?> entry : dependencyManagers ) + { + ((DependencyManager<?, T>)entry.getDm()).invokeBindMethodLate( serviceReference, entry.getTrackingCount() ); + } + } + + @Override + public String toString() + { + return "Late binding task of reference " + serviceReference + " for dependencyManagers " + dependencyManagers; + } + + } ); + } + } + + public synchronized <S, T> void registerMissingDependency( DependencyManager<S, T> dependencyManager, ServiceReference<T> serviceReference, int trackingCount ) + { + //check that the service reference is from scr + if ( serviceReference.getProperty( ComponentConstants.COMPONENT_NAME ) == null || serviceReference.getProperty( ComponentConstants.COMPONENT_ID ) == null ) + { + return; + } + List<Entry<?, ?>> dependencyManagers = m_missingDependencies.get( serviceReference ); + if ( dependencyManagers == null ) + { + dependencyManagers = new ArrayList<Entry<?, ?>>(); + m_missingDependencies.put( serviceReference, dependencyManagers ); + } + dependencyManagers.add( new Entry<S, T>( dependencyManager, trackingCount ) ); + } + + private static class Entry<S,T> + { + private final DependencyManager<S, T> dm; + private final int trackingCount; + + private Entry( DependencyManager<S, T> dm, int trackingCount ) + { + this.dm = dm; + this.trackingCount = trackingCount; + } + + public DependencyManager<S, T> getDm() + { + return dm; + } + + public int getTrackingCount() + { + return trackingCount; + } + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistryKey.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistryKey.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistryKey.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ComponentRegistryKey.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,83 @@ +/* + * 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 org.osgi.framework.Bundle; + + +/** + * The <code>ComponentRegistryKey</code> isused as the key in the + * component register to register components by their names. + * <p> + * Two instances of this class are equal if they are the same or if there + * component name and bundle ID is equal. + */ +final class ComponentRegistryKey +{ + + private final long bundleId; + private final String componentName; + + + ComponentRegistryKey( final Bundle bundle, final String componentName ) + { + this.bundleId = bundle.getBundleId(); + this.componentName = componentName; + } + + + public int hashCode() + { + int code = ( int ) this.bundleId; + code += 31 * this.componentName.hashCode(); + return code; + } + + + public boolean equals( Object obj ) + { + if ( this == obj ) + { + return true; + } + + if ( obj instanceof ComponentRegistryKey ) + { + ComponentRegistryKey other = ( ComponentRegistryKey ) obj; + return this.bundleId == other.bundleId && this.componentName.equals( other.componentName ); + } + + return false; + } + + + public long getBundleId() + { + return bundleId; + } + + + public String getComponentName() + { + return componentName; + } + + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrCommand.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrCommand.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrCommand.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrCommand.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,575 @@ +/* + * 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.PrintWriter; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import org.apache.felix.scr.info.ScrInfo; +import org.apache.felix.scr.impl.config.ScrConfiguration; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.dto.ServiceReferenceDTO; +import org.osgi.service.component.runtime.ServiceComponentRuntime; +import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO; +import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO; +import org.osgi.service.component.runtime.dto.ReferenceDTO; +import org.osgi.service.component.runtime.dto.SatisfiedReferenceDTO; +import org.osgi.service.component.runtime.dto.UnsatisfiedReferenceDTO; + +/** + * The <code>ScrCommand</code> class provides the implementations for the + * Apache Felix Gogo and legacy Apache Felix Shell commands. The + * {@link #register(BundleContext, ScrService, ScrConfiguration)} method + * instantiates and registers the Gogo and Shell commands as possible. + */ +public class ScrCommand implements ScrInfo +{ + + private final BundleContext bundleContext; + private final ServiceComponentRuntime scrService; + private final ScrConfiguration scrConfiguration; + + private ServiceRegistration<ScrInfo> reg; + private ServiceRegistration<?> gogoReg; + private ServiceRegistration<?> shellReg; + + static ScrCommand register(BundleContext bundleContext, ServiceComponentRuntime scrService, ScrConfiguration scrConfiguration) + { + final ScrCommand cmd = new ScrCommand(bundleContext, scrService, scrConfiguration); + + cmd.registerCommands(bundleContext, scrService); + return cmd; + } + + //used by ComponentTestBase + protected ScrCommand(BundleContext bundleContext, ServiceComponentRuntime scrService, ScrConfiguration scrConfiguration) + { + this.bundleContext = bundleContext; + this.scrService = scrService; + this.scrConfiguration = scrConfiguration; + } + + private void registerCommands(BundleContext bundleContext, + ServiceComponentRuntime scrService) + { + /* + * Register the Gogo Command as a service of its own class. + * Due to a race condition during project building (this class is + * compiled for Java 1.3 compatibility before the required + * ScrGogoCommand class compiled for Java 5 compatibility) this uses + * reflection to load and instantiate the class. Any failure during this + * process is just ignored. + */ + try + { + final String scrGogoCommandClassName = "org.apache.felix.scr.impl.ScrGogoCommand"; + final Class<?> scrGogoCommandClass = scrService.getClass().getClassLoader().loadClass(scrGogoCommandClassName); + final Constructor c = scrGogoCommandClass.getConstructor(new Class[] + { ScrCommand.class }); + final Object gogoCmd = c.newInstance(new Object[] + { this }); + final Hashtable<String, Object> props = new Hashtable<String, Object>(); + props.put("osgi.command.scope", "scr"); + props.put("osgi.command.function", new String[] + { "config", "disable", "enable", "info", "list" }); + props.put(Constants.SERVICE_DESCRIPTION, "SCR Gogo Shell Support"); + props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + gogoReg = bundleContext.registerService(scrGogoCommandClassName, gogoCmd, props); + } + catch (Throwable t) + { + // Might be thrown if running in a pre-Java 5 VM + } + + // We dynamically import the impl service API, so it + // might not actually be available, so be ready to catch + // the exception when we try to register the command service. + try + { + // Register "scr" impl command service as a + // wrapper for the bundle repository service. + final Hashtable<String, Object> props = new Hashtable<String, Object>(); + props.put(Constants.SERVICE_DESCRIPTION, "SCR Legacy Shell Support"); + props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + shellReg = bundleContext.registerService(org.apache.felix.shell.Command.class, new ScrShellCommand(this), + props); + } + catch (Throwable th) + { + // Ignore. + } + } + + void unregister() + { + if (gogoReg != null) + { + gogoReg.unregister(); + gogoReg = null; + } + if ( shellReg != null ) + { + shellReg.unregister(); + shellReg = null; + } + } + + // ---------- Actual implementation + + + public void update( boolean infoAsService ) + { + if (infoAsService) + { + if ( reg == null ) + { + final Hashtable<String, Object> props = new Hashtable<String, Object>(); + props.put(Constants.SERVICE_DESCRIPTION, "SCR Info service"); + props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + reg = bundleContext.registerService( ScrInfo.class, this, props ); + } + } + else + { + if ( reg != null ) + { + reg.unregister(); + reg = null; + } + } + } + + /* (non-Javadoc) + * @see org.apache.felix.scr.impl.ScrInfo#list(java.lang.String, java.io.PrintStream, java.io.PrintStream) + */ + public void list(final String bundleIdentifier, final PrintWriter out) + { + List<ComponentDescriptionDTO> components; + + if (bundleIdentifier != null) + { + Bundle bundle = null; + try + { + long bundleId = Long.parseLong(bundleIdentifier); + bundle = bundleContext.getBundle(bundleId); + } + catch (NumberFormatException nfe) + { + // might be a bundle symbolic name + Bundle[] bundles = bundleContext.getBundles(); + for (int i = 0; i < bundles.length; i++) + { + if (bundleIdentifier.equals(bundles[i].getSymbolicName())) + { + bundle = bundles[i]; + break; + } + } + } + + if (bundle == null) + { + throw new IllegalArgumentException("Missing bundle with ID " + bundleIdentifier); + } + if (ComponentRegistry.isBundleActive(bundle)) + { + components = new ArrayList<ComponentDescriptionDTO>(scrService.getComponentDescriptionDTOs(bundle)); + if (components.isEmpty()) + { + out.println("Bundle " + bundleIdentifier + " declares no components"); + return; + } + } + else + { + out.println("Bundle " + bundleIdentifier + " is not active"); + return; + } + } + else + { + components = new ArrayList<ComponentDescriptionDTO>(scrService.getComponentDescriptionDTOs()); + if (components.isEmpty()) + { + out.println("No components registered"); + return; + } + } + + Collections.sort( components, new Comparator<ComponentDescriptionDTO>() + { + + public int compare(ComponentDescriptionDTO c1, ComponentDescriptionDTO c2) + { + return c1.name.compareTo(c2.name); + } + + }); + + out.println(" Name BundleId DefaultEnabled"); + for ( ComponentDescriptionDTO component : components ) + { + out.println( String.format( "[%1$s] [%2$4d] [%3$b]", component.name, component.bundle.id, component.defaultEnabled ) ); + } + out.flush(); + } + + /* (non-Javadoc) + * @see org.apache.felix.scr.impl.ScrInfo#info(java.lang.String, java.io.PrintStream, java.io.PrintStream) + */ + public void info(final String componentId, PrintWriter out) + { + Collection<ComponentDescriptionDTO> components = getComponentFromArg(componentId); + if (components == null) + { + return; + } + + Collections.sort( new ArrayList<ComponentDescriptionDTO>(components), new Comparator<ComponentDescriptionDTO>() + { + + public int compare(ComponentDescriptionDTO c1, ComponentDescriptionDTO c2) + { + long bundleId1 = c1.bundle.id; + long bundleId2 = c2.bundle.id; + int result = Long.signum(bundleId1 - bundleId2); + if ( result == 0) + { + result = c1.name.compareTo(c2.name); + } + return result; + } + + }); + + long bundleId = -1; + + for ( ComponentDescriptionDTO component : components ) + { + if ( component.bundle.id != bundleId ) + { + if ( bundleId != -1 ) + { + out.println(); + out.println(); + } + bundleId = component.bundle.id; + out.println(String.format("*** Bundle: %1$s (%2$d)", component.bundle.symbolicName, bundleId)); + } + out.println( "Component Description:"); + out.print( " Name: " ); + out.println( component.name ); + out.print( " Default State: " ); + out.println( component.defaultEnabled ? "enabled" : "disabled" ); + out.print( " Activation: " ); + out.println( component.immediate ? "immediate" : "delayed" ); + + // DS 1.1 new features + out.print( " Configuration Policy: " ); + out.println( component.configurationPolicy ); + out.print( " Activate Method: " ); + out.print( component.activate ); + out.println(); + out.print( " Deactivate Method: " ); + out.print( component.deactivate ); + out.println(); + out.print( " Modified Method: " ); + if ( component.modified != null ) + { + out.print( component.modified ); + } + else + { + out.print( "-" ); + } + out.println(); + + out.print( " Configuration Pid: " ); + out.print( Arrays.asList(component.configurationPid) ); + out.println(); + + if ( component.factory != null ) + { + out.print( " Factory: " ); + out.println( component.factory ); + } + + String[] services = component.serviceInterfaces; + if ( services != null ) + { + out.print( " Services: " ); + for ( String service: services ) + { + out.print( " " ); + out.println( service ); + } + out.print( " Service Scope: " ); + out.println( component.scope ); + } + + ReferenceDTO[] refs = component.references; + if ( refs != null ) + { + for ( ReferenceDTO ref : refs ) + { + out.print( " Reference: " ); + out.println( ref.name ); + out.print( " Interface Name: " ); + out.println( ref.interfaceName ); + if ( ref.target != null ) + { + out.print( " Target Filter: " ); + out.println( ref.target ); + } + out.print( " Cardinality: " ); + out.println( ref.cardinality ); + out.print( " Policy: " ); + out.println( ref.policy ); + out.print( " Policy option: " ); + out.println( ref.policyOption ); + out.print( " Reference Scope: "); + out.println( ref.scope); + + } + } + + Map<String, Object> props = component.properties; + propertyInfo(props, out, ""); + for (ComponentConfigurationDTO cc: scrService.getComponentConfigurationDTOs(component)) + { + info(cc, out); + } + } + + out.flush(); + } + + void propertyInfo(Map<String, Object> props, PrintWriter out, String prefix) + { + if ( props != null ) + { + out.print( prefix ); + out.println( " Properties:" ); + TreeMap<String, Object> keys = new TreeMap<String, Object>( props ); + for ( Entry<String, Object> entry: keys.entrySet() ) + { + out.print( prefix ); + out.print( " " ); + out.print( entry.getKey() ); + out.print( " = " ); + + Object prop = entry.getValue(); + if ( prop.getClass().isArray() ) + { + out.print("["); + int length = Array.getLength(prop); + for (int i = 0; i< length; i++) + { + out.print(Array.get(prop, i)); + if ( i < length - 1) + { + out.print(", "); + } + } + out.println("]"); + } + else + { + out.println( prop ); + } + } + } + } + + private void info(ComponentConfigurationDTO cc, PrintWriter out) + { + out.println( " Component Configuration:"); + out.print(" ComponentId: "); + out.println( cc.id ); + out.print(" State: "); + out.println( toStateString(cc.state)); + for ( SatisfiedReferenceDTO ref: cc.satisfiedReferences) + { + out.print( " SatisfiedReference: "); + out.println( ref.name ); + out.print( " Target: " ); + out.println( ref.target ); + ServiceReferenceDTO[] serviceRefs = ref.boundServices; + if ( serviceRefs != null ) + { + out.print( " Bound to:" ); + for ( ServiceReferenceDTO sr: serviceRefs ) + { + out.print( " " ); + out.println( sr.id ); + propertyInfo(sr.properties, out, " "); + } + } + else + { + out.println( " (unbound)" ); + } + + } + for ( UnsatisfiedReferenceDTO ref: cc.unsatisfiedReferences) + { + out.print( " UnsatisfiedReference: "); + out.println( ref.name ); + out.print( " Target: " ); + out.println( ref.target ); + ServiceReferenceDTO[] serviceRefs = ref.targetServices; + if ( serviceRefs != null ) + { + out.print( " Target services:" ); + for ( ServiceReferenceDTO sr: serviceRefs ) + { + out.print( " " ); + out.println( sr.id ); + } + } + else + { + out.println( " (unbound)" ); + } + + } + propertyInfo( cc.properties, out, " "); + } + + void change(final String componentIdentifier, PrintWriter out, boolean enable) + { + Collection<ComponentDescriptionDTO> components = getComponentFromArg(componentIdentifier); + ArrayList<String> disposed = new ArrayList<String>(); + if (components == null) + { + return; + } + + for ( ComponentDescriptionDTO component : components ) + { + if ( enable ) + { + if ( !scrService.isComponentEnabled(component) ) + { + scrService.enableComponent(component); + out.println( "Component " + component.name + " enabled" ); + } + else + { + out.println( "Component " + component.name + " already enabled" ); + } + } + else + { + if ( scrService.isComponentEnabled(component) ) + { + scrService.disableComponent(component); + out.println( "Component " + component.name + " disabled" ); + } + else + { + out.println( "Component " + component.name + " already disabled" ); + } + } + } + out.flush(); + if ( !disposed.isEmpty() ) + { + throw new IllegalArgumentException( "Components " + disposed + " already disposed, cannot change state" ); + + } + } + + /* (non-Javadoc) + * @see org.apache.felix.scr.impl.ScrInfo#config(java.io.PrintStream) + */ + public void config(PrintWriter out) + { + out.print("Log Level: "); + out.println(scrConfiguration.getLogLevel()); + out.print("Obsolete Component Factory with Factory Configuration: "); + out.println(scrConfiguration.isFactoryEnabled() ? "Supported" : "Unsupported"); + out.print("Keep instances with no references: "); + out.println(scrConfiguration.keepInstances() ? "Supported" : "Unsupported"); + out.print("Lock timeount milliseconds: "); + out.println(scrConfiguration.lockTimeout()); + out.print("Stop timeount milliseconds: "); + out.println(scrConfiguration.stopTimeout()); + out.print("Global extender: "); + out.println(scrConfiguration.globalExtender()); + out.print("Info Service registered: "); + out.println(scrConfiguration.infoAsService() ? "Supported" : "Unsupported"); + } + + private String toStateString(int state) + { + switch (state) { + + case (ComponentConfigurationDTO.UNSATISFIED_REFERENCE): + return "unsatisfied reference"; + case (ComponentConfigurationDTO.ACTIVE): + return "active "; + case (ComponentConfigurationDTO.SATISFIED): + return "satisfied "; + default: + return "unkown: " + state; + } + } + + private Collection<ComponentDescriptionDTO> getComponentFromArg(final String componentIdentifier) + { + Collection<ComponentDescriptionDTO> components = scrService.getComponentDescriptionDTOs(); + if (componentIdentifier != null) + { + ArrayList<ComponentDescriptionDTO> cs = new ArrayList<ComponentDescriptionDTO>(components.size()); + Pattern p = Pattern.compile(componentIdentifier); + for (ComponentDescriptionDTO component: components) + { + if ( p.matcher( component.name).matches() ) + { + cs.add( component ); + } + } + if (cs.isEmpty()) + { + throw new IllegalArgumentException("No Component with ID or matching " + componentIdentifier); + } + components = cs; + } + + return components; + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrGogoCommand.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrGogoCommand.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrGogoCommand.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/ScrGogoCommand.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,130 @@ +/* + * 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.PrintWriter; + +import org.apache.felix.service.command.Descriptor; + +/** + * The <code>ScrGogoCommand</code> implements the Apache Felix Gogo Shell + * commands for the Apache Felix Declarative Services implementations. Supported + * commands are: + * <dl> + * <dt><code>scr:list</code></dt> + * <dd>List all components or components of a specific bundle</dd> + * <dt><code>scr:info</code></dt> + * <dd>Print full information of a single component</dd> + * <dt><code>scr:enable</code></dt> + * <dd>Enable a component</dd> + * <dt><code>scr:disable</code></dt> + * <dd>Disable a component</dd> + * <dt><code>scr:config</code></dt> + * <dd>Print configuration of the Apache Felix Declarative Services bundle</dd> + * </dl> + * <p> + * This class uses Java 5 annotations to provide descriptions for the commands + * and arguments. Thus the class must be compiled with Java 5 enabled and thus + * the commands will only be available in Java 5 or higher VMs. + */ +class ScrGogoCommand +{ + + // The actual implementation of the commands + private final ScrCommand scrCommand; + + // this constructor is public to ease the reflection based implementation + // See ScrCommand.register for fully disclosure of mechanics + public ScrGogoCommand(final ScrCommand scrCommand) + { + this.scrCommand = scrCommand; + } + + @Descriptor("List all components") + public void list() + { + try + { + scrCommand.list(null, new PrintWriter(System.out)); + } + catch ( IllegalArgumentException e ) + { + System.err.println(e.getMessage()); + } + } + + @Descriptor("List components of a specific bundle") + public void list(@Descriptor("Symbolic name or ID of the bundle") final String bundleIdentifier) + { + try + { + scrCommand.list(bundleIdentifier, new PrintWriter(System.out)); + } + catch ( IllegalArgumentException e ) + { + System.err.println(e.getMessage()); + } + } + + @Descriptor("Dump information of a component") + public void info(@Descriptor("Name or ID of the component") final String componentIdentifier) + { + try + { + scrCommand.info(componentIdentifier, new PrintWriter(System.out)); + } + catch ( IllegalArgumentException e ) + { + System.err.println(e.getMessage()); + } + } + + @Descriptor("Enable a disabled component") + public void enable(@Descriptor("Name or ID of the component") final String componentIdentifier) + { + try + { + scrCommand.change(componentIdentifier, new PrintWriter(System.out), true); + } + catch ( IllegalArgumentException e ) + { + System.err.println(e.getMessage()); + } + } + + @Descriptor("Disable an enabled component") + public void disable(@Descriptor("Name or ID of the component") final String componentIdentifier) + { + try + { + scrCommand.change(componentIdentifier, new PrintWriter(System.out), false); + } + catch ( IllegalArgumentException e ) + { + System.err.println(e.getMessage()); + } + } + + @Descriptor("Show the current SCR configuration") + public void config() + { + scrCommand.config(new PrintWriter(System.out)); + } + +}
