Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleComponentManager.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleComponentManager.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleComponentManager.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleComponentManager.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,916 @@ +/* + * 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.manager; + + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.felix.scr.impl.BundleComponentActivator; +import org.apache.felix.scr.impl.config.ComponentContainer; +import org.apache.felix.scr.impl.config.ReferenceManager; +import org.apache.felix.scr.impl.helper.ActivatorParameter; +import org.apache.felix.scr.impl.helper.ComponentMethods; +import org.apache.felix.scr.impl.helper.MethodResult; +import org.apache.felix.scr.impl.helper.ModifiedMethod; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.ComponentConstants; +import org.osgi.service.component.ComponentInstance; +import org.osgi.service.log.LogService; +import org.osgi.util.promise.Deferred; + + +/** + * The default ComponentManager. Objects of this class are responsible for managing + * implementation object's lifecycle. + */ +public class SingleComponentManager<S> extends AbstractComponentManager<S> implements ServiceFactory<S> +{ + + // keep the using bundles as reference "counters" for instance deactivation + private final AtomicInteger m_useCount = new AtomicInteger( ); + + // The context that will be passed to the implementationObject + private volatile ComponentContextImpl<S> m_componentContext; + + // Merged properties from xml descriptor and all configurations + private Map<String, Object> m_configurationProperties; + + // optional properties provided in the ComponentFactory.newInstance method + private Map<String, Object> m_factoryProperties; + + // the component properties, also used as service properties + private Map<String, Object> m_properties; + + // properties supplied ot ExtComponentContext.updateProperties + // null if properties are not to be overwritten + private Dictionary<String, Object> m_serviceProperties; + + private final ThreadLocal<Boolean> m_circularReferences = new ThreadLocal<Boolean>(); + + /** + * The constructor receives both the activator and the metadata + * @param componentMethods + */ + public SingleComponentManager( ComponentContainer<S> container, ComponentMethods componentMethods ) + { + this(container, componentMethods, false); + } + + public SingleComponentManager( ComponentContainer<S> container, ComponentMethods componentMethods, + boolean factoryInstance ) + { + super( container, componentMethods, factoryInstance ); + } + + @Override + void clear() + { + m_container.disposed( this ); + + super.clear(); + } + + + // 1. Load the component implementation class + // 2. Create the component instance and component context + // 3. Bind the target services + // 4. Call the activate method, if present + // if this method is overwritten, the deleteComponent method should + // also be overwritten + private boolean createComponent(ComponentContextImpl<S> componentContext) + { + if ( !isStateLocked() ) + { + throw new IllegalStateException( "need write lock (createComponent)" ); + } + if ( m_componentContext == null ) + { + S tmpComponent = createImplementationObject( null, new SetImplementationObject<S>() + { + public void presetComponentContext( ComponentContextImpl<S> componentContext ) + { + m_componentContext = componentContext; + } + + + public void resetImplementationObject( S implementationObject ) + { + m_componentContext = null; + } + }, componentContext ); + + // if something failed creating the component instance, return false + if ( tmpComponent == null ) + { + return false; + } + + // otherwise set the context and component instance and return true + log( LogService.LOG_DEBUG, "Set implementation object for component {0}", new Object[] { getName() }, null ); + + //notify that component was successfully created so any optional circular dependencies can be retried + BundleComponentActivator activator = getActivator(); + if ( activator != null ) + { + activator.missingServicePresent( getServiceReference() ); + } + } + return true; + } + + + @Override + protected void deleteComponent( int reason ) + { + if ( !isStateLocked() ) + { + throw new IllegalStateException( "need write lock (deleteComponent)" ); + } + if ( m_componentContext != null ) + { + m_useCount.set( 0 ); + disposeImplementationObject( m_componentContext, reason ); + m_componentContext.cleanup(); + m_componentContext = null; + log( LogService.LOG_DEBUG, "Unset and deconfigured implementation object for component {0} in deleteComponent for reason {1}", new Object[] { getName(), REASONS[ reason ] }, null ); + clearServiceProperties(); + } + } + + void clearServiceProperties() + { + m_properties = null; + m_serviceProperties = null; + } + + + public ComponentInstance getComponentInstance() + { + return m_componentContext == null? null: m_componentContext.getComponentInstance(); + } + + + //********************************************************************************************************** + + /** + * Get the object that is implementing this descriptor + * + * @return the object that implements the services + */ + private S getInstance() + { + return m_componentContext == null? null: m_componentContext.getImplementationObject( true ); + } + + /** + * The <code>SetImplementationObject</code> interface provides an + * API for component managers to setup the implementation object and + * potentially other parts as part of the {@link #createImplementationObject} method + * processing. + */ + protected interface SetImplementationObject<S> + { + + /** + * Presets the implementation object. This method is called before + * the component's activator method is called and is intended to + * temporarily set the implementation object during the activator + * call. + */ + void presetComponentContext( ComponentContextImpl<S> componentContext ); + + + /** + * Resets the implementation object. This method is called after + * the activator method terminates with an error and is intended to + * revert any temporary settings done in the {@link #presetComponentContext(ComponentContextImpl)} + * method. + */ + void resetImplementationObject( S implementationObject ); + + } + + + protected S createImplementationObject( Bundle usingBundle, SetImplementationObject<S> setter, ComponentContextImpl<S> componentContext ) + { + final Class<S> implementationObjectClass; + final S implementationObject; + + // 1. Load the component implementation class + // 2. Create the component instance and component context + // If the component is not immediate, this is not done at this moment + Bundle bundle = getBundle(); + if (bundle == null) + { + log( LogService.LOG_WARNING, "Bundle shut down during instantiation of the implementation object", null); + return null; + } + try + { + // 112.4.4 The class is retrieved with the loadClass method of the component's bundle + implementationObjectClass = (Class<S>) bundle.loadClass( + getComponentMetadata().getImplementationClassName() ) ; + + // 112.4.4 The class must be public and have a public constructor without arguments so component instances + // may be created by the SCR with the newInstance method on Class + implementationObject = implementationObjectClass.newInstance(); + } + catch ( Throwable t ) + { + // failed to instantiate, return null + log( LogService.LOG_ERROR, "Error during instantiation of the implementation object", t ); + return null; + } + + componentContext.setImplementationObject(implementationObject); + + // 3. set the implementation object prematurely + setter.presetComponentContext( componentContext ); + + // 4. Bind the target services + + ReferenceManager<S, ?> failedDm = null; + for ( DependencyManager<S, ?> dm: getDependencyManagers()) + { + if ( failedDm == null ) + { + // if a dependency turned unresolved since the validation check, + // creating the instance fails here, so we deactivate and return + // null. + boolean open = dm.open( componentContext, componentContext.getEdgeInfo( dm ) ); + if ( !open ) + { + log( LogService.LOG_ERROR, "Cannot create component instance due to failure to bind reference {0}", + new Object[] { dm.getName() }, null ); + + failedDm = dm; + + } + } + else + { + componentContext.getEdgeInfo( dm ).ignore(); + } + } + if (failedDm != null) + { + // make sure, we keep no bindings. Only close the dm's we opened. + boolean skip = true; + for ( DependencyManager<S, ?> md: getReversedDependencyManagers() ) + { + if ( skip && failedDm == md ) + { + skip = false; + } + if ( !skip ) + { + md.close( componentContext, componentContext.getEdgeInfo( md ) ); + } + md.deactivate(); + } + setter.resetImplementationObject( implementationObject ); + return null; + + } + + // 5. Call the activate method, if present + final MethodResult result = getComponentMethods().getActivateMethod().invoke( implementationObject, new ActivatorParameter( + componentContext, 1 ), null, this ); + if ( result == null ) + { + // 112.5.8 If the activate method throws an exception, SCR must log an error message + // containing the exception with the Log Service and activation fails + for ( DependencyManager<S, ?> md: getReversedDependencyManagers() ) + { + md.close( componentContext, componentContext.getEdgeInfo( md ) ); + } + + // make sure the implementation object is not available + setter.resetImplementationObject( implementationObject ); + + return null; + } + else + { + componentContext.setImplementationAccessible( true ); + m_circularReferences.remove(); + //this may cause a getService as properties now match a filter. + setServiceProperties( result ); + } + + return implementationObject; + } + + + protected void disposeImplementationObject( ComponentContextImpl<S> componentContext, + int reason ) + { + componentContext.setImplementationAccessible( false ); + S implementationObject = componentContext.getImplementationObject( false ); + + if ( implementationObject != null ) + { + // 1. Call the deactivate method, if present + // don't care for the result, the error (acccording to 112.5.12 If the deactivate + // method throws an exception, SCR must log an error message containing the + // exception with the Log Service and continue) has already been logged + final MethodResult result = getComponentMethods().getDeactivateMethod().invoke( implementationObject, + new ActivatorParameter( componentContext, reason ), null, this ); + if ( result != null ) + { + setServiceProperties( result ); + } + // 2. Unbind any bound services + for ( DependencyManager<S, ?> md: getReversedDependencyManagers() ) + { + md.close( componentContext, componentContext.getEdgeInfo( md ) ); + } + } + + } + + @Override + boolean hasInstance() + { + return m_componentContext != null; + } + + @Override + <T> void invokeBindMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> refPair, int trackingCount ) + { + ComponentContextImpl<S> componentContext = m_componentContext; + if ( componentContext != null ) + { + EdgeInfo info = componentContext.getEdgeInfo( dependencyManager ); + dependencyManager.invokeBindMethod( componentContext, refPair, trackingCount, info ); + } + } + + @Override + <T> void invokeUpdatedMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> refPair, int trackingCount ) + { + ComponentContextImpl<S> componentContext = m_componentContext; + if ( componentContext != null ) + { + EdgeInfo info = componentContext.getEdgeInfo( dependencyManager ); + dependencyManager.invokeUpdatedMethod( componentContext, refPair, trackingCount, info ); + } + } + + @Override + <T> void invokeUnbindMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> oldRefPair, int trackingCount ) + { + ComponentContextImpl<S> componentContext = m_componentContext; + if ( componentContext != null ) + { + EdgeInfo info = componentContext.getEdgeInfo( dependencyManager ); + dependencyManager.invokeUnbindMethod( componentContext, oldRefPair, trackingCount, info ); + } + } + + protected void setFactoryProperties( Dictionary<String, ?> dictionary ) + { + m_factoryProperties = copyToMap( dictionary, true ); + } + + + @Override + void registerComponentId() + { + super.registerComponentId(); + this.m_properties = null; + } + + + @Override + void unregisterComponentId() + { + super.unregisterComponentId(); + this.m_properties = null; + } + + + /** + * Returns the (private copy) of the Component properties to be used + * for the ComponentContext as well as eventual service registration. + * <p/> + * Method implements the Component Properties provisioning as described + * in 112.6, Component Properties. + * + * @return a private map of component properties + */ + @Override + public Map<String, Object> getProperties() + { + + if ( m_properties == null ) + { + + + // 1. Merge all the config properties + Map<String, Object> props = new HashMap<String, Object>(); + if ( m_configurationProperties != null ) + { + props.putAll(m_configurationProperties); + } + if ( m_factoryProperties != null) + { + props.putAll(m_factoryProperties); + if (getComponentMetadata().getDSVersion().isDS13() && m_factoryProperties.containsKey(Constants.SERVICE_PID)) + { + final List<String> servicePids = new ArrayList<String>(); + final Object configPropServicePids = m_configurationProperties.get(Constants.SERVICE_PID); + if ( configPropServicePids instanceof List ) + { + servicePids.addAll((List)configPropServicePids); + } + else + { + servicePids.add(configPropServicePids.toString()); + } + if (m_factoryProperties.get(Constants.SERVICE_PID) instanceof String) + { + servicePids.add((String)m_factoryProperties.get(Constants.SERVICE_PID)); + } + + if ( servicePids.size() == 1 ) + { + props.put(Constants.SERVICE_PID, servicePids.get(0)); + } + else + { + props.put(Constants.SERVICE_PID, servicePids); + } + } + } + + // 2. set component.name and component.id + props.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() ); + props.put( ComponentConstants.COMPONENT_ID, getId() ); + + m_properties = props; + } + + return m_properties; + } + + @Override + public void setServiceProperties( Dictionary<String, ?> serviceProperties ) + { + if ( serviceProperties == null || serviceProperties.isEmpty() ) + { + m_serviceProperties = null; + } + else + { + m_serviceProperties = copyToDictionary( serviceProperties, false ); + // set component.name and component.id + m_serviceProperties.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() ); + m_serviceProperties.put( ComponentConstants.COMPONENT_ID, getId() ); + } + + updateServiceRegistration(); + } + + @Override + public Dictionary<String, Object> getServiceProperties() + { + if ( m_serviceProperties != null ) + { + return m_serviceProperties; + } + return super.getServiceProperties(); + } + + private void updateServiceRegistration() + { + ServiceRegistration<S> sr = getServiceRegistration(); + if ( sr != null ) + { + try + { + // Don't propagate if service properties did not change. + final Dictionary<String, Object> regProps = getServiceProperties(); + if ( !servicePropertiesMatches( sr, regProps ) ) + { + sr.setProperties( regProps ); + } + } + catch ( IllegalStateException ise ) + { + // service has been unregistered asynchronously, ignore + } + catch ( IllegalArgumentException iae ) + { + log( LogService.LOG_ERROR, + "Unexpected configuration property problem when updating service registration", iae ); + } + catch ( Throwable t ) + { + log( LogService.LOG_ERROR, "Unexpected problem when updating service registration", t ); + } + } + } + + /** + * Called by the Configuration Admin Service to update the component with + * Configuration properties. + * <p/> + * This causes the component to be reactivated with the new configuration + * unless no configuration has ever been set on this component and the + * <code>configuration</code> parameter is <code>null</code>. In this case + * nothing is to be done. If a configuration has previously been set and + * now the configuration is deleted, the <code>configuration</code> + * parameter is <code>null</code> and the component has to be reactivated + * with the default configuration. + * + * @param configuration The configuration properties for the component from + * the Configuration Admin Service or <code>null</code> if there is + * no configuration or if the configuration has just been deleted. + * @param configurationDeleted TODO + * @param changeCount Change count for the configuration + * @param targetedPID TargetedPID for the configuration + */ + @Override + public void reconfigure( Map<String, Object> configuration, boolean configurationDeleted ) + { + // store the properties + m_configurationProperties = configuration; + + reconfigure(configurationDeleted); + } + + void reconfigure(boolean configurationDeleted) + { + Deferred<Void> enableLatch = enableLatchWait(); + try + { + // clear the current properties to force using the configuration data + m_properties = null; + + + // reactivate the component to ensure it is provided with the + // configuration data + if ( m_disposed || !m_internalEnabled ) + { + // nothing to do for inactive components, leave this method + log( LogService.LOG_DEBUG, "Component can not be activated since it is in state {0}", new Object[] { getState() }, null ); + //enabling the component will set the target properties, do nothing now. + return; + } + + // unsatisfied component and non-ignored configuration may change targets + // to satisfy references + obtainActivationWriteLock( "reconfigure" ); + try + { + if ( !isSatisfied() && !getComponentMetadata().isConfigurationIgnored() ) + { + log( LogService.LOG_DEBUG, "Attempting to activate unsatisfied component", null ); + updateTargets( getProperties() ); + releaseActivationWriteeLock( "reconfigure.unsatisfied" ); + activateInternal( getTrackingCount().get() ); + return; + } + + if ( !modify(configurationDeleted) ) + { + // SCR 112.7.1 - deactivate if configuration is deleted or no modified method declared + log( LogService.LOG_DEBUG, "Deactivating and Activating to reconfigure from configuration", null ); + int reason = configurationDeleted ? ComponentConstants.DEACTIVATION_REASON_CONFIGURATION_DELETED + : ComponentConstants.DEACTIVATION_REASON_CONFIGURATION_MODIFIED; + + // FELIX-2368: cycle component immediately, reconfigure() is + // called through ConfigurationListener API which itself is + // called asynchronously by the Configuration Admin Service + releaseActivationWriteeLock( "reconfigure.modified.1" ); + //we have already determined that modify cannot be called. Therefore factory instances must be disposed. + boolean dispose = m_factoryInstance; + deactivateInternal( reason, dispose, dispose ); + if ( !dispose ) + { + obtainActivationWriteLock("reconfigure.deactivate.activate"); + try + { + updateTargets(getProperties()); + } + finally + { + releaseActivationWriteeLock("reconfigure.deactivate.activate"); + } + activateInternal(getTrackingCount().get()); + } + } + } + finally + { + //used if modify succeeds or if there's an exception. + releaseActivationWriteeLock( "reconfigure.end" );; + } + } + finally + { + enableLatch.resolve(null); + } + } + + private boolean modify(boolean configurationDeleted) + { + //0 SCR 112.7.1 If configuration is deleted, and version is < 1.3 and no flag set, then deactivate unconditionally. + // For version 1.3 and later, or with a flag, more sensible behavior is allowed. + if ( configurationDeleted && !getComponentMetadata().isDeleteCallsModify()){ + return false; + } + + // 1. no live update if there is no declared method + if ( getComponentMetadata().getModified() == null ) + { + log( LogService.LOG_DEBUG, "No modified method, cannot update dynamically", null ); + return false; + } + // invariant: we have a modified method name + + // 2. get and check configured method + // invariant: modify method is configured and found + + // 3. check whether we can dynamically apply the configuration if + // any target filters influence the bound services + final Map<String, Object> props = getProperties(); + for ( DependencyManager<S, ?> dm: getDependencyManagers() ) + { + if ( !dm.canUpdateDynamically( props ) ) + { + log( LogService.LOG_DEBUG, + "Cannot dynamically update the configuration due to dependency changes induced on dependency {0}", + new Object[] {dm.getName()}, null ); + return false; + } + } + // invariant: modify method existing and no static bound service changes + + // 4. call method (nothing to do when failed, since it has already been logged) + // (call with non-null default result to continue even if the + // modify method call failed) + obtainStateLock( "ImmediateComponentManager.modify" ); + try + { + //cf 112.5.12 where invoking modified method before updating target services is specified. + final MethodResult result = invokeModifiedMethod(); + updateTargets( props ); + if ( result == null ) + { + // log an error if the declared method cannot be found + log( LogService.LOG_ERROR, "Declared modify method ''{0}'' cannot be found, configuring by reactivation", + new Object[] {getComponentMetadata().getModified()}, null ); + return false; + } + + // 5. update the target filter on the services now, this may still + // result in unsatisfied dependencies, in which case we abort + // this dynamic update and have the component be deactivated + if ( !verifyDependencyManagers() ) + { + log( LogService.LOG_ERROR, + "Updating the service references caused at least on reference to become unsatisfied, deactivating component", + null ); + return false; + } + + // 6. update service registration properties if we didn't just do it + if ( result.hasResult() ) + { + setServiceProperties( result ); + } + else + { + updateServiceRegistration(); + } + + // 7. everything set and done, the component has been updated + return true; + } + finally + { + releaseStateLock( "ImmediateComponentManager.modify" ); + } + } + + protected MethodResult invokeModifiedMethod() + { + ModifiedMethod modifiedMethod = getComponentMethods().getModifiedMethod(); + if ( getInstance() != null ) + { + return modifiedMethod.invoke( getInstance(), new ActivatorParameter( m_componentContext, -1 ), + MethodResult.VOID, this ); + } + return MethodResult.VOID; + } + + /** + * Checks if the given service registration properties matches another set + * of properties. + * + * @param reg the service registration whose service properties will be + * compared to the props parameter + * @param props the properties to be compared with the registration + * service properties. + * @return <code>true</code> if the registration service properties equals + * the prop properties, false if not. + */ + private boolean servicePropertiesMatches( ServiceRegistration<S> reg, Dictionary<String, Object> props ) + { + Dictionary<String, Object> regProps = new Hashtable<String, Object>(); + String[] keys = reg.getReference().getPropertyKeys(); + for ( int i = 0; keys != null && i < keys.length; i++ ) + { + if ( !keys[i].equals( org.osgi.framework.Constants.OBJECTCLASS ) + && !keys[i].equals( org.osgi.framework.Constants.SERVICE_ID ) ) + { + regProps.put( keys[i], reg.getReference().getProperty( keys[i] ) ); + } + } + return regProps.equals( props ); + } + + public S getService( Bundle bundle, ServiceRegistration<S> serviceRegistration ) + { + boolean success = getServiceInternal(); + if ( success ) + { + m_useCount.incrementAndGet(); + return m_componentContext.getImplementationObject( true ); + } + else + { + return null; + } + } + + + @Override + boolean getServiceInternal() + { + if (m_circularReferences.get() != null) + { + log( LogService.LOG_ERROR, "Circular reference detected, getService returning null", null ); + dumpThreads(); + return false; + } + m_circularReferences.set( Boolean.TRUE ); + try + { + boolean success = true; + if ( m_componentContext == null ) + { + ComponentContextImpl<S> componentContext = new ComponentContextImpl<S>(this, this.getBundle()); + if ( collectDependencies(componentContext)) + { + log( + LogService.LOG_DEBUG, + "getService (single component manager) dependencies collected.", + null ); + } + else + { + log( + LogService.LOG_INFO, + "Could not obtain all required dependencies, getService returning null", + null ); + success = false; + } + obtainStateLock( "ImmediateComponentManager.getService.1" ); + try + { + if ( m_componentContext == null ) + { + //state should be "Registered" + S result = getService(componentContext ); + if ( result == null ) + { + success = false;; + } + else + { + m_activated = true; + } + } + } + finally + { + releaseStateLock( "ImmediateComponentManager.getService.1" ); + } + } + return success; + } + finally + { + //normally this will have been done after object becomes accessible. This is double-checking. + m_circularReferences.remove(); + } + } + + private S getService(ComponentContextImpl<S> componentContext) + { + //should be write locked + if (!isInternalEnabled()) + { + return null; + } + + if ( createComponent(componentContext) ) + { + return getInstance(); + } + + // log that the delayed component cannot be created (we don't + // know why at this moment; this should already have been logged) + log( LogService.LOG_ERROR, "Failed creating the component instance; see log for reason", null ); + + // component could not really be created. This may be temporary + // so we stay in the registered state but ensure the component + // instance is deleted + try + { + deleteComponent( ComponentConstants.DEACTIVATION_REASON_UNSPECIFIED ); + } + catch ( Throwable t ) + { + log( LogService.LOG_DEBUG, "Cannot delete incomplete component instance. Ignoring.", t ); + } + + // no service can be returned (be prepared for more logging !!) + return null; + + } + + public void ungetService( Bundle bundle, ServiceRegistration<S> serviceRegistration, S o ) + { + // the framework should not call ungetService more than it calls + // calls getService. Still, we want to be sure to not go below zero + if ( m_useCount.get() > 0 ) + { + int useCount = m_useCount.decrementAndGet(); + + // unget the service instance if no bundle is using it + // any longer unless delayed component instances have to + // be kept (FELIX-3039) + if ( useCount == 0 && !isImmediate() && !keepInstances() ) + { + obtainStateLock( "ImmediateComponentManager.ungetService.1" ); + try + { + if ( m_useCount.get() == 0 ) + { + ungetService( ); + } + } + finally + { + releaseStateLock( "ImmediateComponentManager.ungetService.1" ); + } + } + } + } + + void ungetService( ) + { + deleteComponent( ComponentConstants.DEACTIVATION_REASON_UNSPECIFIED ); + } + + private boolean keepInstances() + { + return getComponentMetadata().isDelayedKeepInstances(); + } + + @Override + public void getComponentManagers(List<AbstractComponentManager<S>> cms) + { + cms.add(this); + } + +}
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SinglePrototypeRefPair.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SinglePrototypeRefPair.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SinglePrototypeRefPair.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SinglePrototypeRefPair.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,75 @@ +/* + * 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.manager; + +import org.apache.felix.scr.impl.helper.SimpleLogger; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceObjects; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; + +/** + * @version $Rev: 1689302 $ $Date: 2015-07-06 03:31:39 +0200 (Mon, 06 Jul 2015) $ + */ +public class SinglePrototypeRefPair<S, T> extends SingleRefPair<S, T> +{ + private final ServiceObjects<T> serviceObjects; + + public SinglePrototypeRefPair( BundleContext context, ServiceReference<T> ref ) + { + super(ref); + this.serviceObjects = context.getServiceObjects(ref); + } + + @Override + public ServiceObjects<T> getServiceObjects() + { + return serviceObjects; + } + + @Override + public String toString() + { + return "[SinglePrototypeRefPair: ref: [" + getRef() + "] service: [" + getServiceObject(null) + "]]"; + } + + @Override + public boolean getServiceObject(ComponentContextImpl<S> key, BundleContext context, + SimpleLogger logger) + { + final T service = key.getComponentServiceObjectsHelper().getPrototypeRefInstance(this.getRef(), serviceObjects); + if ( service == null ) + { + setFailed(); + logger.log( + LogService.LOG_WARNING, + "Could not get service from serviceobjects for ref {0}", new Object[] {getRef()}, null ); + return false; + } + if (!setServiceObject(key, service)) + { + // Another thread got the service before, so unget our + serviceObjects.ungetService( service ); + } + return true; + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleRefPair.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleRefPair.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleRefPair.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/SingleRefPair.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,88 @@ +/* + * 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.manager; + +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.felix.scr.impl.helper.SimpleLogger; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; + +/** + * @version $Rev: 1615440 $ $Date: 2014-08-03 21:27:54 +0200 (Sun, 03 Aug 2014) $ + */ +public class SingleRefPair<S, T> extends RefPair<S, T> +{ + private AtomicReference<T> serviceObjectRef = new AtomicReference<T>(); + + public SingleRefPair( ServiceReference<T> ref ) + { + super(ref); + } + + public T getServiceObject(ComponentContextImpl<S> key) + { + return serviceObjectRef.get(); + } + + public boolean setServiceObject( ComponentContextImpl<S> key, T serviceObject ) + { + boolean set = serviceObjectRef.compareAndSet( null, serviceObject ); + if ( serviceObject != null) + { + failed = false; + } + return set; + } + + public T unsetServiceObject(ComponentContextImpl<S> key) + { + return serviceObjectRef.getAndSet( null ); + } + + @Override + public String toString() + { + return "[RefPair: ref: [" + getRef() + "] service: [" + serviceObjectRef.get() + "]]"; + } + + @Override + public boolean getServiceObject(ComponentContextImpl<S> key, BundleContext context, + SimpleLogger logger) + { + T service = context.getService( getRef() ); + if ( service == null ) + { + setFailed(); + logger.log( + LogService.LOG_WARNING, + "Could not get service from ref {0}", new Object[] {getRef()}, null ); + return false; + } + if (!setServiceObject(key, service)) + { + // Another thread got the service before, so unget our + context.ungetService( getRef() ); + } + return true; + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ThreadDump.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ThreadDump.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ThreadDump.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/manager/ThreadDump.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,51 @@ +/* + * 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.manager; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.Callable; + +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; + +public class ThreadDump implements Callable<String> +{ + + @IgnoreJRERequirement + public String call() throws Exception + { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + StringBuffer b = new StringBuffer( "Thread dump\n" ); + ThreadInfo[] infos = threadMXBean.dumpAllThreads( threadMXBean.isObjectMonitorUsageSupported(), threadMXBean.isSynchronizerUsageSupported() ); + for ( int i = 0; i < infos.length; i++ ) + { + ThreadInfo ti = infos[i]; + b.append( "\n\nThreadId: " ).append( ti.getThreadId() ).append( " : name: " ).append( ti.getThreadName() ).append( " State: " ).append( ti.getThreadState() ); + b.append( "\n LockInfo: " ).append( ti.getLockInfo() ).append( " LockOwnerId: " ).append( ti.getLockOwnerId() ).append( " LockOwnerName: ").append( ti.getLockOwnerName() ); + StackTraceElement[] stackTrace = ti.getStackTrace(); + for (int j = 0; j < stackTrace.length; j++ ) + { + b.append( "\n " ).append( stackTrace[j] ); + } + } + return b.toString(); + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/ComponentMetadata.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/ComponentMetadata.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/ComponentMetadata.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/ComponentMetadata.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,955 @@ +/* + * 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.metadata; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.felix.scr.impl.TargetedPID; +import org.apache.felix.scr.impl.helper.Logger; +import org.apache.felix.scr.impl.metadata.ServiceMetadata.Scope; +import org.osgi.service.component.ComponentException; + + +/** + * This class holds the information associated to a component in the descriptor * */ +public class ComponentMetadata +{ + // Configuration required for component activation (since DS 1.1) + public static final String CONFIGURATION_POLICY_REQUIRE = "require"; + + // Configuration not provided to component (since DS 1.1) + public static final String CONFIGURATION_POLICY_IGNORE = "ignore"; + + // Configuration optional (default) (since DS 1.1) + public static final String CONFIGURATION_POLICY_OPTIONAL = "optional"; + + // set of valid configuration policy settings + private static final Set<String> CONFIGURATION_POLICY_VALID; + + // marker value indicating duplicate implementation class setting + private static final String IMPLEMENTATION_CLASS_DUPLICATE = "icd"; + + // marker value indicating duplicate service setting + private static final ServiceMetadata SERVICE_DUPLICATE = new ServiceMetadata(); + + // the namespace code of the namespace declaring this component, this is + // one of the XmlHandler.DS_VERSION_* constants + private final DSVersion m_dsVersion; + + // 112.4.3: A Globally unique component name (required) + private String m_name; + + // 112.4.3: Controls whether the component is enabled when the bundle is started. (optional, default is true). + private boolean m_enabled = true; + + // 112.4.3: Factory identified. If set to a non empty string, it indicates that the component is a factory component (optional). + private String m_factory = null; + + // 112.4.3: Controls whether component configurations must be immediately activated after becoming + // satisfied or whether activation should be delayed. (optional, default value depends + // on whether the component has a service element or not). + private Boolean m_immediate = null; + + // 112.4.4 Implementation Element (required) + private String m_implementationClassName = null; + + // 112.5.8 activate can be specified (since DS 1.1) + private String m_activate = null; + + // 112.5.8 whether activate has been specified + private boolean m_activateDeclared = false; + + // 112.5.12 deactivate can be specified (since DS 1.1) + private String m_deactivate = null; + + // 112.5.12 whether deactivate has been specified + private boolean m_deactivateDeclared = false; + + // 112.??.?? modified method (configuration update, since DS 1.1) + private String m_modified = null; + + // 112.4.3 configuration-policy (since DS 1.1) + private String m_configurationPolicy = null; + + // 112.4.4 configuration-pid (since DS 1.2) + private List<String> m_configurationPid; + + // Associated properties (0..*) + private Map<String, Object> m_properties = new HashMap<String, Object>(); + + // List of Property metadata - used while building the meta data + // while validating the properties contained in the PropertyMetadata + // instances are copied to the m_properties Dictionary while this + // list will be cleared + private List<PropertyMetadata> m_propertyMetaData = new ArrayList<PropertyMetadata>(); + + // Provided services (0..1) + private ServiceMetadata m_service = null; + + // List of service references, (required services 0..*) + private List<ReferenceMetadata> m_references = new ArrayList<ReferenceMetadata>(); + + private boolean m_configurableServiceProperties; + private boolean m_persistentFactoryComponent; + private boolean m_deleteCallsModify; + private Boolean m_obsoleteFactoryComponentFactory; + private boolean m_configureWithInterfaces; + private boolean m_delayedKeepInstances; + + // Flag that is set once the component is verified (its properties cannot be changed) + private boolean m_validated = false; + + static + { + CONFIGURATION_POLICY_VALID = new TreeSet<String>(); + CONFIGURATION_POLICY_VALID.add( CONFIGURATION_POLICY_IGNORE ); + CONFIGURATION_POLICY_VALID.add( CONFIGURATION_POLICY_OPTIONAL ); + CONFIGURATION_POLICY_VALID.add( CONFIGURATION_POLICY_REQUIRE ); + } + + + public ComponentMetadata( DSVersion dsVersion ) + { + this.m_dsVersion = dsVersion; + } + + /////////////////////////////////////////// SETTERS ////////////////////////////////////// + + /** + * Setter for the configuration-pid component (since DS 1.2) + * @param configurationPid + */ + public void setConfigurationPid( String[] configurationPid ) + { + if ( m_validated ) + { + return; + } + m_configurationPid = new ArrayList<String>( Arrays.asList( configurationPid ) ); + } + + /** + * Setter for the name + * + * @param name + */ + public void setName( String name ) + { + if ( m_validated ) + { + return; + } + m_name = name; + } + + + /** + * Setter for the enabled property + * + * @param enabled + */ + public void setEnabled( boolean enabled ) + { + if ( m_validated ) + { + return; + } + m_enabled = enabled; + } + + + /** + * + * @param factoryIdentifier + */ + public void setFactoryIdentifier( String factoryIdentifier ) + { + if ( m_validated ) + { + return; + } + m_factory = factoryIdentifier; + } + + + /** + * Setter for the immediate property + * + * @param immediate + */ + public void setImmediate( boolean immediate ) + { + if ( m_validated ) + { + return; + } + m_immediate = immediate ? Boolean.TRUE : Boolean.FALSE; + } + + + /** + * Sets the name of the implementation class + * + * @param implementationClassName a class name + */ + public void setImplementationClassName( String implementationClassName ) + { + if ( m_validated ) + { + return; + } + + // set special flag value if implementation class is already set + if ( m_implementationClassName != null ) + { + m_implementationClassName = IMPLEMENTATION_CLASS_DUPLICATE; + } + else + { + m_implementationClassName = implementationClassName; + } + } + + + /** + * Sets the configuration policy + * + * @param configurationPolicy configuration policy + * @since 1.2.0 (DS 1.1) + */ + public void setConfigurationPolicy( String configurationPolicy ) + { + if ( m_validated ) + { + return; + } + m_configurationPolicy = configurationPolicy; + } + + + /** + * Sets the name of the activate method + * + * @param activate a method name + * @since 1.2.0 (DS 1.1) + */ + public void setActivate( String activate ) + { + if ( m_validated ) + { + return; + } + m_activate = activate; + m_activateDeclared = true; + } + + + /** + * Sets the name of the deactivate method + * + * @param deactivate a method name + * @since 1.2.0 (DS 1.1) + */ + public void setDeactivate( String deactivate ) + { + if ( m_validated ) + { + return; + } + m_deactivate = deactivate; + m_deactivateDeclared = true; + } + + + /** + * Sets the name of the modified method + * + * @param modified a method name + * @since 1.2.0 (DS 1.1) + */ + public void setModified( String modified ) + { + if ( m_validated ) + { + return; + } + m_modified = modified; + } + + + /** + * Used to add a property to the instance + * + * @param newProperty a property metadata object + */ + public void addProperty( PropertyMetadata newProperty ) + { + if ( m_validated ) + { + return; + } + if ( newProperty == null ) + { + throw new IllegalArgumentException( "Cannot add a null property" ); + } + m_propertyMetaData.add( newProperty ); + } + + + /** + * Used to set a ServiceMetadata object. + * + * @param service a ServiceMetadata + */ + public void setService( ServiceMetadata service ) + { + if ( m_validated ) + { + return; + } + + // set special flag value if implementation class is already set + if ( m_service != null ) + { + m_service = SERVICE_DUPLICATE; + } + else + { + m_service = service; + } + } + + + /** + * Used to add a reference metadata to the component + * + * @param newReference a new ReferenceMetadata to be added + */ + public void addDependency( ReferenceMetadata newReference ) + { + if ( m_validated ) + { + return; + } + if ( newReference == null ) + { + throw new IllegalArgumentException( "Cannot add a null ReferenceMetadata" ); + } + m_references.add( newReference ); + } + + public void setConfigurableServiceProperties( boolean configurableServiceProperties) { + if ( m_validated ) + { + return; + } + this.m_configurableServiceProperties = configurableServiceProperties; + } + + public void setPersistentFactoryComponent(boolean persistentFactoryComponent) { + if ( m_validated ) + { + return; + } + this.m_persistentFactoryComponent = persistentFactoryComponent; + } + + public void setDeleteCallsModify(boolean deleteCallsModify) { + if ( m_validated ) + { + return; + } + this.m_deleteCallsModify = deleteCallsModify; + } + + public void setObsoleteFactoryComponentFactory( boolean obsoleteFactoryComponentFactory) { + if ( m_validated ) + { + return; + } + this.m_obsoleteFactoryComponentFactory = obsoleteFactoryComponentFactory; + } + + public void setConfigureWithInterfaces(boolean configureWithInterfaces) { + this.m_configureWithInterfaces = configureWithInterfaces; + } + + public void setDelayedKeepInstances(boolean delayedKeepInstances) { + if ( m_validated ) + { + return; + } + this.m_delayedKeepInstances = delayedKeepInstances; + } + + + + /////////////////////////////////////////// GETTERS ////////////////////////////////////// + + /** + * Returns the namespace code of the namespace of the component element + * declaring this component. This is one of the XmlHandler.DS_VERSION_* + * constants. + */ + public DSVersion getDSVersion() + { + return m_dsVersion; + } + + + /** + * Returns the name of the component + * + * @return A string containing the name of the component + */ + public String getName() + { + // FELIX-2325: Be lenient here and return the name if set or + // the implementation class name. This allows for the + // BundleComponentActivator.loadComponents method to access the + // name before validating the component, which then makes sure + // that the name may only be unset for DS 1.1 and newer components + + if ( m_name != null ) + { + return m_name; + } + + // return the implementation class name if the name is not set + return getImplementationClassName(); + } + + /** + * Returns the configuration pid for the component. The pid is the one specified in the + * component's configuration-pid DS 1.2 attribute, if specified. Else the component name is used + * as the pid by default. + */ + public List<String> getConfigurationPid() + { + if ( !m_validated ) + { + throw new IllegalStateException("not yet validated"); + } + return m_configurationPid; + } + + public int getPidIndex(TargetedPID pid) + { + if ( !m_validated ) + { + throw new IllegalStateException("not yet validated"); + } + if (m_configurationPid == null ) + { + throw new IllegalStateException( "Apparently trying to configure a component " + m_name + " without a configurationPid using " + pid); + } + return m_configurationPid.indexOf(pid.getServicePid()); + } + + /** + * Returns whether the configuration-pid has been declared in the descriptor + * or not. + * + * @return whether the configuration-pid has method has been declared in the descriptor + * or not. + * @since DS 1.2 + */ + public boolean isConfigurationPidDeclared() + { + return m_configurationPid != null; + } + + /** + * Returns the value of the enabled flag + * + * @return a boolean containing the value of the enabled flag + */ + public boolean isEnabled() + { + return m_enabled; + } + + + /** + * Returns the factory identifier + * + * @return A string containing a factory identifier or null + */ + public String getFactoryIdentifier() + { + return m_factory; + } + + + /** + * Returns the flag that defines the activation policy for the component. + * <p> + * This method may only be trusted after this instance has been validated + * by the {@link #validate( Logger logger )} call. Else it will either return the value + * of an explicitly set "immediate" attribute or return false if a service + * element or the factory attribute is set or true otherwise. This latter + * default value deduction may be unsafe while the descriptor has not been + * completely read. + * + * @return a boolean that defines the activation policy + */ + public boolean isImmediate() + { + // return explicit value if known + if ( m_immediate != null ) + { + return m_immediate.booleanValue(); + } + + // deduce default from service element and factory attribute presence + return m_service == null && m_factory == null; + } + + + /** + * Returns the name of the implementation class + * + * @return the name of the implementation class + */ + public String getImplementationClassName() + { + return m_implementationClassName; + } + + + /** + * Returns the configuration Policy + * + * @return the configuration policy + * @since 1.2.0 (DS 1.1) + */ + public String getConfigurationPolicy() + { + return m_configurationPolicy; + } + + + /** + * Returns the name of the activate method + * + * @return the name of the activate method + * @since 1.2.0 (DS 1.1) + */ + public String getActivate() + { + return m_activate; + } + + + /** + * Returns whether the activate method has been declared in the descriptor + * or not. + * + * @return whether the activate method has been declared in the descriptor + * or not. + * @since 1.2.0 (DS 1.1) + */ + public boolean isActivateDeclared() + { + return m_activateDeclared; + } + + + /** + * Returns the name of the deactivate method + * + * @return the name of the deactivate method + * @since 1.2.0 (DS 1.1) + */ + public String getDeactivate() + { + return m_deactivate; + } + + + /** + * Returns whether the deactivate method has been declared in the descriptor + * or not. + * + * @return whether the deactivate method has been declared in the descriptor + * or not. + * @since 1.2.0 (DS 1.1) + */ + public boolean isDeactivateDeclared() + { + return m_deactivateDeclared; + } + + + /** + * Returns the name of the modified method + * + * @return the name of the modified method + * @since 1.2.0 (DS 1.1) + */ + public String getModified() + { + return m_modified; + } + + + /** + * Returns the associated ServiceMetadata + * + * @return a ServiceMetadata object or null if the Component does not provide any service + */ + public ServiceMetadata getServiceMetadata() + { + return m_service; + } + + public Scope getServiceScope() + { + if (m_service == null) + { + return Scope.singleton; + } + return m_service.getScope(); + } + + + /** + * Returns the properties. + * + * @return the properties as a Dictionary + */ + public Map<String, Object> getProperties() + { + return m_properties; + } + + + /** + * Returns the list of property meta data. + * <b>Note: This method is intended for unit testing only</b> + * + * @return the list of property meta data. + */ + List<PropertyMetadata> getPropertyMetaData() + { + return m_propertyMetaData; + } + + + /** + * Returns the dependency descriptors + * + * @return a Collection of dependency descriptors + */ + public List<ReferenceMetadata> getDependencies() + { + return m_references; + } + + + /** + * Test to see if this service is a factory + * + * @return true if it is a factory, false otherwise + */ + public boolean isFactory() + { + return m_factory != null; + } + + + /** + * Returns <code>true</code> if the configuration policy is configured to + * {@link #CONFIGURATION_POLICY_REQUIRE}. + */ + public boolean isConfigurationRequired() + { + return CONFIGURATION_POLICY_REQUIRE.equals( m_configurationPolicy ); + } + + + /** + * Returns <code>true</code> if the configuration policy is configured to + * {@link #CONFIGURATION_POLICY_IGNORE}. + */ + public boolean isConfigurationIgnored() + { + return CONFIGURATION_POLICY_IGNORE.equals( m_configurationPolicy ); + } + + + /** + * Returns <code>true</code> if the configuration policy is configured to + * {@link #CONFIGURATION_POLICY_OPTIONAL}. + */ + public boolean isConfigurationOptional() + { + return CONFIGURATION_POLICY_OPTIONAL.equals( m_configurationPolicy ); + } + + + public boolean isConfigurableServiceProperties() { + return m_configurableServiceProperties; + } + + public boolean isPersistentFactoryComponent() { + return m_persistentFactoryComponent; + } + + public boolean isDeleteCallsModify() { + return m_deleteCallsModify; + } + + public boolean isObsoleteFactoryComponentFactory() { + return m_obsoleteFactoryComponentFactory == null ? false : m_obsoleteFactoryComponentFactory; + } + + public boolean isConfigureWithInterfaces() { + return m_configureWithInterfaces; + } + + public boolean isDelayedKeepInstances() { + return m_delayedKeepInstances; + } + + /** + * Method used to verify if the semantics of this metadata are correct + */ + public void validate( Logger logger ) + { + // nothing to do if already validated + if ( m_validated ) + { + return; + } + + // 112.10 The name of the component is required + if ( m_name == null ) + { + // 112.4.3 name is optional defaulting to implementation class name since DS 1.1 + if ( !m_dsVersion.isDS11() ) + { + throw new ComponentException( "The component name has not been set" ); + } + setName( getImplementationClassName() ); + } + + // 112.10 There must be one implementation element and the class atribute is required + if ( m_implementationClassName == null ) + { + throw validationFailure( "Implementation class name missing" ); + } + else if ( m_implementationClassName == IMPLEMENTATION_CLASS_DUPLICATE ) + { + throw validationFailure( "Implementation element must occur exactly once" ); + } + + // 112.4.3 configuration-policy (since DS 1.1) + if ( m_configurationPolicy == null ) + { + // default if not specified or pre DS 1.1 + m_configurationPolicy = CONFIGURATION_POLICY_OPTIONAL; + } + else if ( !m_dsVersion.isDS11() ) + { + throw validationFailure( "configuration-policy declaration requires DS 1.1 or later namespace " ); + } + else if ( !CONFIGURATION_POLICY_VALID.contains( m_configurationPolicy ) ) + { + throw validationFailure( "configuration-policy must be one of " + CONFIGURATION_POLICY_VALID ); + } + + // 112.5.8 activate can be specified (since DS 1.1) + if ( m_activate == null ) + { + // default if not specified or pre DS 1.1 + m_activate = "activate"; + } + else if ( !m_dsVersion.isDS11() ) + { + throw validationFailure( "activate method declaration requires DS 1.1 or later namespace " ); + } + + // 112.5.12 deactivate can be specified (since DS 1.1) + if ( m_deactivate == null ) + { + // default if not specified or pre DS 1.1 + m_deactivate = "deactivate"; + } + else if ( !m_dsVersion.isDS11() ) + { + throw validationFailure( "deactivate method declaration requires DS 1.1 or later namespace " ); + } + + // 112.??.?? modified can be specified (since DS 1.1) + if ( m_modified != null && !m_dsVersion.isDS11() ) + { + throw validationFailure( "modified method declaration requires DS 1.1 or later namespace " ); + } + + // 112.4.4 configuration-pid can be specified since DS 1.2 + if ( m_configurationPid == null ) + { + m_configurationPid = Collections.singletonList( getName() ); + } + else + { + if ( !m_dsVersion.isDS12() ) + { + throw validationFailure( "configuration-pid attribute requires DS 1.2 or later namespace " ); + } + if (m_configurationPid.isEmpty()) + { + throw validationFailure( "configuration-pid nust not be empty string " ); + } + if (m_configurationPid.size() > 1 && !m_dsVersion.isDS13()) + { + throw validationFailure( "multiple configuration-pid requires DS 1.3 or later namespace " ); + } + for (int i = 0; i < m_configurationPid.size(); i++) + { + if ("$".equals( m_configurationPid.get(i))) + { + if (!m_dsVersion.isDS13()) + { + throw validationFailure( "Use of '$' configuration-pid wildcard requires DS 1.3 or later namespace " ); + } + m_configurationPid.set( i, getName() ); + } + } + if ( new HashSet<String>( m_configurationPid ).size() != m_configurationPid.size()) + { + throw validationFailure( "Duplicate pids not allowed: " + m_configurationPid ); + } + } + + // Next check if the properties are valid (and extract property values) + for ( PropertyMetadata propMeta: m_propertyMetaData ) + { + propMeta.validate( this ); + m_properties.put( propMeta.getName(), propMeta.getValue() ); + } + m_propertyMetaData.clear(); + + // Check that the provided services are valid too + if ( m_service == SERVICE_DUPLICATE ) + { + throw validationFailure( "Service element must occur at most once" ); + } + else if ( m_service != null ) + { + m_service.validate( this ); + } + + // Check that the references are ok + Set<String> refs = new HashSet<String>(); + for ( ReferenceMetadata refMeta: m_references ) + { + refMeta.validate( this, logger ); + + // flag duplicates + if ( !refs.add( refMeta.getName() ) ) + { + throw validationFailure( "Detected duplicate reference name: ''" + refMeta.getName() + "''" ); + } + } + + // verify value of immediate attribute if set + if ( m_immediate != null ) + { + if ( isImmediate() ) + { + // FELIX-593: 112.4.3 clarification, immediate is false for factory + if ( isFactory() ) + { + throw validationFailure( "Factory cannot be immediate" ); + } + } + else + { + // 112.2.3 A delayed component specifies a service, is not specified to be a factory component + // and does not have the immediate attribute of the component element set to true. + // FELIX-593: 112.4.3 clarification, immediate may be true for factory + if ( m_service == null && !isFactory() ) + { + throw validationFailure( "Delayed must provide a service or be a factory" ); + } + } + } + + // 112.4.6 The serviceFactory attribute (of a provided service) must not be true if + // the component is a factory component or an immediate component + if ( m_service != null ) + { + if ( (m_service.getScope() != ServiceMetadata.Scope.singleton) && ( isFactory() || isImmediate() ) ) + { + throw validationFailure( "factory or immediate must be scope singleton not " + m_service.getScope()); + } + } + + if (m_dsVersion == DSVersion.DS12Felix) + { + m_configurableServiceProperties = true; + } + if ( m_configurableServiceProperties && getServiceScope() != Scope.singleton ) + { + throw validationFailure( "configurable service properties only allowed with singleton scope" ); + } + if (m_dsVersion.isDS13()) + { + m_deleteCallsModify = true; //spec behavior as of 1.3 + } + if ( !m_dsVersion.isDS13() && m_configureWithInterfaces) + { + throw validationFailure("Configuration with interfaces or annotations only possible with version 1.3 or later"); + } + if (m_dsVersion.isDS13() && m_obsoleteFactoryComponentFactory != null) + { + throw validationFailure("Configuration of component factory instances through config admin factory pids supported only through the 1.2 namespace"); + } + if (m_persistentFactoryComponent && !isFactory()) + { + throw validationFailure("Only a factory component can be a persistent factory component"); + } + + + m_validated = true; + } + + + /** + * Returns a <code>ComponentException</code> for this compeonent with the + * given explanation for failure. + * + * @param reason The explanation for failing to validate this component. + */ + ComponentException validationFailure( String reason ) + { + return new ComponentException( "Component " + getName() + " validation failed: " + reason ); + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/DSVersion.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/DSVersion.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/DSVersion.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/DSVersion.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,58 @@ +/* + * 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.metadata; + +public enum DSVersion +{ + DSnone(-1), + DS10(0), + DS11(1), + DS11Felix(2), + DS12(3), + DS12Felix(4), + DS13(5); + + private final int version; + + DSVersion(int version) + { + this.version = version; + } + + public boolean isDS10() + { + return version >=DS10.version; + } + + public boolean isDS11() + { + return version >=DS11.version; + } + + public boolean isDS12() + { + return version >=DS12.version; + } + + public boolean isDS13() + { + return version >=DS13.version; + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/PropertyMetadata.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/PropertyMetadata.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/PropertyMetadata.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/metadata/PropertyMetadata.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,336 @@ +/* + * 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.metadata; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * A property descriptor that contains the information for properties + * defined in the descriptor + * + */ +public class PropertyMetadata { + + // Name of the property (required) + private String m_name; + + // Type of the property (optional) + private String m_type; + + // Value of the type (optional) + // - before validate: raw value from XML (String or String[]) + // - after validate: converted value provided to component + private Object m_value; + + // Flag that indicates if this PropertyMetadata has been validated and thus has become immutable + private boolean m_validated = false; + + /** + * Set the name + * + * @param name + */ + public void setName(String name) { + if (m_validated == true) { + return; + } + + m_name = name; + } + + + /** + * Set the type + * + * @param type + */ + public void setType(String type) { + if (m_validated == true) { + return; + } + m_type = type; + } + + /** + * Set the value + * + * @param value + */ + public void setValue(String value) { + if (m_validated == true) { + return; + } + m_value = value; + } + + /** + * Set multiple values as an array, where the values are contained in + * the string as one value per line. + * + * @param values + */ + public void setValues(String values) { + if (m_validated == true) { + return; + } + // splite th values + List<String> valueList = new ArrayList<String>(); + StringTokenizer tokener = new StringTokenizer(values, "\r\n"); + while (tokener.hasMoreTokens()) { + String value = tokener.nextToken().trim(); + if (value.length() > 0) { + valueList.add( value ); + } + } + m_value = valueList.toArray( new String[valueList.size()] ); + } + + /** + * Get the name of the property + * + * @return the name of the property + */ + public String getName() { + return m_name; + } + + /** + * Get the type of the property + * + * @return the type of the property + */ + public String getType() { + return m_type; + } + + /** + * Get the value of the property + * + * @return the value of the property as an Object + */ + public Object getValue() { + return m_value; + } + + /** + * Method used to verify if the semantics of this metadata are correct + */ + public void validate( ComponentMetadata componentMetadata ) + { + if ( m_name == null ) + { + throw componentMetadata.validationFailure( "Property name attribute is mandatory" ); + } + + // check character type name + if ( m_type == null ) + { + m_type = "String"; + } + else if ( componentMetadata.getDSVersion().isDS11() && m_type.equals( "Char" ) ) + { + throw componentMetadata + .validationFailure( "Illegal property type 'Char' used for DS 1.1 descriptor, use 'Character' instead" ); + } + else if ( !componentMetadata.getDSVersion().isDS11() && m_type.equals( "Character" ) ) + { + throw componentMetadata + .validationFailure( "Illegal property type 'Character' used for DS 1.0 descriptor, use 'Char' instead" ); + } + + // validate and covert value + if ( m_value != null ) + { + try + { + if ( m_value instanceof String ) + { + m_value = toType( ( String ) m_value ); + } + else + { + m_value = toTypeArray( ( String[] ) m_value ); + } + } + catch ( NumberFormatException nfe ) + { + throw componentMetadata.validationFailure( getName() + ": Cannot convert property value to " + + getType() ); + } + catch ( IllegalArgumentException e ) + { + throw componentMetadata.validationFailure( getName() + ": " + e.getMessage() ); + } + } + + m_validated = true; + } + + + /** + * @throws IllegalArgumentException if the property type is not valid + * according to the spec + * @throws NumberFormatException if the string value cannot be converted + * to the numeric type indicated by the property type + */ + private Object toType( String value ) + { + // 112.4.5 Parsing of the value is done by the valueOf(String) method (P. 291) + // Should the type accept lowercase too? + if ( m_type.equals( "String" ) ) + { + return value; + } + else if ( m_type.equals( "Long" ) ) + { + return Long.valueOf( value ); + } + else if ( m_type.equals( "Double" ) ) + { + return Double.valueOf( value ); + } + else if ( m_type.equals( "Float" ) ) + { + return Float.valueOf( value ); + } + else if ( m_type.equals( "Integer" ) ) + { + return Integer.valueOf( value ); + } + else if ( m_type.equals( "Byte" ) ) + { + return Byte.valueOf( value ); + } + else if ( m_type.equals( "Char" ) || m_type.equals( "Character" ) ) + { + // DS 1.1 changes the "Char" type to "Character", here we support both + // For Character types, the conversion is handled by Integer.valueOf method. + // (since valueOf is defined in terms of parseInt we directly call + // parseInt to prevent unneeded Object creation) + return Character.valueOf( ( char ) Integer.parseInt( value ) ); + } + else if ( m_type.equals( "Boolean" ) ) + { + return Boolean.valueOf( value ); + } + else if ( m_type.equals( "Short" ) ) + { + return Short.valueOf( value ); + } + else + { + throw new IllegalArgumentException( "Undefined property type '" + m_type + "'" ); + } + } + + + /** + * @throws IllegalArgumentException if the property type is not valid + * according to the spec + * @throws NumberFormatException if the string value cannot be converted + * to the numeric type indicated by the property type + */ + private Object toTypeArray( String[] valueList ) + { + // 112.4.5 Except for String objects, the result will be translated to an array of primitive types. + if ( m_type.equals( "String" ) ) + { + return valueList; + } + else if ( m_type.equals( "Double" ) ) + { + double[] array = new double[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Double.parseDouble( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Float" ) ) + { + float[] array = new float[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Float.parseFloat( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Long" ) ) + { + long[] array = new long[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Long.parseLong( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Integer" ) ) + { + int[] array = new int[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Integer.parseInt( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Short" ) ) + { + short[] array = new short[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Short.parseShort( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Byte" ) ) + { + byte[] array = new byte[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Byte.parseByte( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Char" ) || m_type.equals( "Character" ) ) + { + // DS 1.1 changes the "Char" type to "Character", here we support both + char[] array = new char[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = ( char ) Integer.parseInt( valueList[i] ); + } + return array; + } + else if ( m_type.equals( "Boolean" ) ) + { + boolean[] array = new boolean[valueList.length]; + for ( int i = 0; i < array.length; i++ ) + { + array[i] = Boolean.valueOf( valueList[i] ); + } + return array; + } + else + { + throw new IllegalArgumentException( "Undefined property type '" + m_type + "'" ); + } + } +}
