| /* |
| * 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 static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString; |
| |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| 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.Map.Entry; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader; |
| import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter; |
| 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 |
| 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; |
| |
| // 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; |
| |
| // 112.4.4 Implementation Element (required) |
| private String m_implementationClassName; |
| |
| // 112.5.8 activate can be specified (since DS 1.1) |
| private String m_activate; |
| |
| // 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; |
| |
| // 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; |
| |
| // 112.4.3 configuration-policy (since DS 1.1) |
| private String m_configurationPolicy; |
| |
| // 112.4.4 configuration-pid (since DS 1.2) |
| private List<String> m_configurationPid; |
| |
| // activation fields (since DS 1.4) |
| private List<String> m_activationFields; |
| |
| // Associated properties (0..*) |
| private final Map<String, Object> m_properties = new HashMap<>(); |
| |
| // Associated factory properties (0..*) |
| private final Map<String, Object> m_factoryProperties = new HashMap<>(); |
| |
| // 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 final List<PropertyMetadata> m_propertyMetaData = new ArrayList<>(); |
| |
| // List of Property metadata - used while building the meta data |
| // while validating the properties contained in the PropertyMetadata |
| // instances are copied to the m_factoryProperties Dictionary while this |
| // list will be cleared |
| private final List<PropertyMetadata> m_factoryPropertyMetaData = new ArrayList<>(); |
| |
| // Provided services (0..1) |
| private ServiceMetadata m_service; |
| |
| // List of service references, (required services 0..*) |
| private final List<ReferenceMetadata> m_references = new ArrayList<>(); |
| |
| private boolean m_configurableServiceProperties; |
| private boolean m_persistentFactoryComponent; |
| private boolean m_deleteCallsModify; |
| private Boolean m_obsoleteFactoryComponentFactory; |
| private boolean m_configureWithInterfaces; |
| private boolean m_delayedKeepInstances; |
| |
| private String m_init; |
| |
| // 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<>(); |
| CONFIGURATION_POLICY_VALID.add( CONFIGURATION_POLICY_IGNORE ); |
| CONFIGURATION_POLICY_VALID.add( CONFIGURATION_POLICY_OPTIONAL ); |
| CONFIGURATION_POLICY_VALID.add( CONFIGURATION_POLICY_REQUIRE ); |
| } |
| |
| |
| public ComponentMetadata( final 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<>( 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 add a factory property to the instance |
| * |
| * @param newProperty a property metadata object |
| */ |
| public void addFactoryProperty( PropertyMetadata newProperty ) |
| { |
| if ( m_validated ) |
| { |
| return; |
| } |
| if ( newProperty == null ) |
| { |
| throw new IllegalArgumentException( "Cannot add a null property" ); |
| } |
| m_factoryPropertyMetaData.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; |
| } |
| |
| |
| |
| public void setActivationFields( final String[] fields ) |
| { |
| if ( !m_validated ) |
| { |
| this.m_activationFields = new ArrayList<>( Arrays.asList( fields ) ); |
| } |
| } |
| |
| public void setInit( final String value ) |
| { |
| if ( !m_validated ) |
| { |
| this.m_init = value; |
| } |
| } |
| |
| /////////////////////////////////////////// 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( )} 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 number of constructor parameters (0 is default) |
| * @return The number of constructor parameters |
| * @since 2.1.0 (DS 1.4) |
| */ |
| public int getNumberOfConstructorParameters() |
| { |
| // validate() ensures this is a valid integer |
| return m_init == null ? 0 : Integer.valueOf(m_init); |
| } |
| |
| /** |
| * Returns the names of the activation fields |
| * |
| * @return the list of activation fields or {@code null} |
| * @since 2.1.0 (DS 1.4) |
| */ |
| public List<String> getActivationFields() |
| { |
| return m_activationFields; |
| } |
| |
| |
| /** |
| * 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 factory properties. |
| * |
| * @return the factory properties as a Dictionary |
| */ |
| public Map<String, Object> getFactoryProperties() |
| { |
| return m_factoryProperties; |
| } |
| |
| |
| /** |
| * 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 list of factory property meta data. |
| * <b>Note: This method is intended for unit testing only</b> |
| * |
| * @return the list of property meta data. |
| */ |
| List<PropertyMetadata> getFactoryPropertyMetaData() |
| { |
| return m_factoryPropertyMetaData; |
| } |
| |
| |
| /** |
| * 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( ) |
| { |
| // 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<>( 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(); |
| |
| // Next check if the factory properties are valid (and extract property values) |
| if ( !m_dsVersion.isDS14() && !m_factoryPropertyMetaData.isEmpty() ) |
| { |
| throw validationFailure( "Use of factory properties requires DS 1.4 or later namespace " ); |
| } |
| if ( m_dsVersion.isDS14() && isFactory() ) |
| { |
| for ( PropertyMetadata propMeta: m_factoryPropertyMetaData ) |
| { |
| propMeta.validate( this ); |
| m_factoryProperties.put( propMeta.getName(), propMeta.getValue() ); |
| } |
| } |
| // if this is not a factory, these props are ignored, so nothing else to do |
| m_factoryPropertyMetaData.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<>(); |
| for ( ReferenceMetadata refMeta: m_references ) |
| { |
| refMeta.validate( this ); |
| |
| // 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()); |
| } |
| } |
| |
| // activation fields require DS 1.4 |
| if ( m_activationFields != null && !m_dsVersion.isDS14() ) |
| { |
| throw validationFailure( "Activation fields require version 1.4 or later"); |
| } |
| |
| // constructor injection requires DS 1.4 |
| if ( this.m_init != null ) |
| { |
| if ( !m_dsVersion.isDS14() ) |
| { |
| throw validationFailure( "Constructor injection requires version 1.4 or later"); |
| } |
| int constructorParameters = 0; |
| try |
| { |
| constructorParameters = Integer.valueOf(m_init); |
| if ( constructorParameters < 0) |
| { |
| throw validationFailure( "Init parameter must have non negative value: " + m_init); |
| } |
| } |
| catch ( final NumberFormatException nfe) |
| { |
| throw validationFailure( "Init parameter is not a number: " + m_init); |
| } |
| } |
| |
| 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 component 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 ); |
| } |
| |
| public void collectStrings(Set<String> strings) |
| { |
| addString(m_dsVersion.toString(), strings); |
| addString(m_activate, strings); |
| if (m_activationFields != null) |
| { |
| for (String s : m_activationFields) |
| { |
| addString(s, strings); |
| } |
| } |
| if (m_configurationPid != null) |
| { |
| for (String s : m_configurationPid) |
| { |
| addString(s, strings); |
| } |
| } |
| addString(m_configurationPolicy, strings); |
| addString(m_deactivate, strings); |
| addString(m_factory, strings); |
| addString(m_implementationClassName, strings); |
| addString(m_init, strings); |
| addString(m_modified, strings); |
| addString(m_name, strings); |
| for (Entry<String, Object> entry : m_factoryProperties.entrySet()) |
| { |
| collectStrings(entry, strings); |
| } |
| for (Entry<String, Object> entry : m_properties.entrySet()) |
| { |
| collectStrings(entry, strings); |
| } |
| for (ReferenceMetadata rMeta : m_references) |
| { |
| rMeta.collectStrings(strings); |
| } |
| if (m_service != null) |
| { |
| m_service.collectStrings(strings); |
| } |
| } |
| |
| private void collectStrings(Entry<String, Object> entry, Set<String> strings) |
| { |
| addString(entry.getKey(), strings); |
| Object v = entry.getValue(); |
| if (v instanceof String) |
| { |
| addString((String) v, strings); |
| } |
| else if (v instanceof String[]) |
| { |
| for (String s : (String[]) v) |
| { |
| addString(s, strings); |
| } |
| } |
| } |
| |
| public void store(DataOutputStream out, MetaDataWriter metaDataWriter) |
| throws IOException |
| { |
| metaDataWriter.writeString(m_dsVersion.toString(), out); |
| metaDataWriter.writeString(m_activate, out); |
| out.writeBoolean(m_activationFields != null); |
| if (m_activationFields != null) |
| { |
| out.writeInt(m_activationFields.size()); |
| for (String s : m_activationFields) |
| { |
| metaDataWriter.writeString(s, out); |
| } |
| } |
| out.writeBoolean(m_configurationPid != null); |
| if (m_configurationPid != null) |
| { |
| out.writeInt(m_configurationPid.size()); |
| for (String s : m_configurationPid) |
| { |
| metaDataWriter.writeString(s, out); |
| } |
| } |
| metaDataWriter.writeString(m_configurationPolicy, out); |
| metaDataWriter.writeString(m_deactivate, out); |
| metaDataWriter.writeString(m_factory, out); |
| metaDataWriter.writeString(m_implementationClassName, out); |
| metaDataWriter.writeString(m_init, out); |
| metaDataWriter.writeString(m_modified, out); |
| metaDataWriter.writeString(m_name, out); |
| out.writeInt(m_factoryProperties.size()); |
| for (Entry<String, Object> prop : m_factoryProperties.entrySet()) |
| { |
| metaDataWriter.writeString(prop.getKey(), out); |
| storePropertyValue(prop.getValue(), out, metaDataWriter); |
| } |
| out.writeInt(m_properties.size()); |
| for (Entry<String, Object> prop : m_properties.entrySet()) |
| { |
| metaDataWriter.writeString(prop.getKey(), out); |
| storePropertyValue(prop.getValue(), out, metaDataWriter); |
| } |
| out.writeInt(m_references.size()); |
| for (ReferenceMetadata rMeta : m_references) |
| { |
| rMeta.store(out, metaDataWriter); |
| } |
| out.writeBoolean(m_service != null); |
| if (m_service != null) |
| { |
| m_service.store(out, metaDataWriter); |
| } |
| out.writeBoolean(m_activateDeclared); |
| out.writeBoolean(m_configurableServiceProperties); |
| out.writeBoolean(m_configureWithInterfaces); |
| out.writeBoolean(m_deactivateDeclared); |
| out.writeBoolean(m_delayedKeepInstances); |
| out.writeBoolean(m_deleteCallsModify); |
| out.writeBoolean(m_enabled); |
| out.writeBoolean(m_immediate != null); |
| if (m_immediate != null) |
| { |
| out.writeBoolean(m_immediate.booleanValue()); |
| } |
| } |
| |
| public static ComponentMetadata load(DataInputStream in, |
| MetaDataReader metaDataReader) |
| throws IOException |
| { |
| ComponentMetadata result = new ComponentMetadata( |
| DSVersion.valueOf(metaDataReader.readString(in))); |
| result.m_activate = metaDataReader.readString(in); |
| if (in.readBoolean()) |
| { |
| int size = in.readInt(); |
| String[] activationFields = new String[size]; |
| for (int i = 0; i < size; i++) |
| { |
| activationFields[i] = metaDataReader.readString(in); |
| } |
| result.setActivationFields(activationFields); |
| } |
| if (in.readBoolean()) |
| { |
| int size = in.readInt(); |
| String[] configPids = new String[size]; |
| for (int i = 0; i < size; i++) |
| { |
| configPids[i] = metaDataReader.readString(in); |
| } |
| result.setConfigurationPid(configPids); |
| } |
| result.m_configurationPolicy = metaDataReader.readString(in); |
| result.m_deactivate = metaDataReader.readString(in); |
| result.m_factory = metaDataReader.readString(in); |
| result.m_implementationClassName = metaDataReader.readString(in); |
| result.m_init = metaDataReader.readString(in); |
| result.m_modified = metaDataReader.readString(in); |
| result.m_name = metaDataReader.readString(in); |
| int numFProps = in.readInt(); |
| for (int i = 0; i < numFProps; i++) |
| { |
| result.m_factoryProperties.put(metaDataReader.readString(in), |
| loadPropertyValue(in, metaDataReader)); |
| } |
| int numProps = in.readInt(); |
| for (int i = 0; i < numProps; i++) |
| { |
| result.m_properties.put(metaDataReader.readString(in), |
| loadPropertyValue(in, metaDataReader)); |
| } |
| int numRefs = in.readInt(); |
| for (int i = 0; i < numRefs; i++) |
| { |
| result.addDependency(ReferenceMetadata.load(in, metaDataReader)); |
| } |
| if (in.readBoolean()) |
| { |
| result.m_service = ServiceMetadata.load(in, metaDataReader); |
| } |
| result.m_activateDeclared = in.readBoolean(); |
| result.m_configurableServiceProperties = in.readBoolean(); |
| result.m_configureWithInterfaces = in.readBoolean(); |
| result.m_deactivateDeclared = in.readBoolean(); |
| result.m_delayedKeepInstances = in.readBoolean(); |
| result.m_deleteCallsModify = in.readBoolean(); |
| result.m_enabled = in.readBoolean(); |
| if (in.readBoolean()) |
| { |
| result.m_immediate = in.readBoolean(); |
| } |
| // we only store valid metadata |
| result.m_validated = true; |
| return result; |
| } |
| |
| private static final byte TypeString = 1; |
| private static final byte TypeLong = 2; |
| private static final byte TypeDouble = 3; |
| private static final byte TypeFloat = 4; |
| private static final byte TypeInteger = 5; |
| private static final byte TypeByte = 6; |
| private static final byte TypeChar = 7; |
| private static final byte TypeBoolean = 8; |
| private static final byte TypeShort = 9; |
| |
| static Object loadPropertyValue(DataInputStream in, MetaDataReader metaDataReader) |
| throws IOException |
| { |
| boolean isArray = in.readBoolean(); |
| byte valueType = in.readByte(); |
| switch (valueType) |
| { |
| case TypeBoolean: |
| if (isArray) |
| { |
| boolean[] vArray = new boolean[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readBoolean(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Boolean.valueOf(in.readBoolean()); |
| } |
| case TypeByte: |
| if (isArray) |
| { |
| byte[] vArray = new byte[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readByte(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Byte.valueOf(in.readByte()); |
| } |
| case TypeChar: |
| if (isArray) |
| { |
| char[] vArray = new char[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readChar(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Character.valueOf(in.readChar()); |
| } |
| case TypeDouble: |
| if (isArray) |
| { |
| double[] vArray = new double[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readDouble(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Double.valueOf(in.readDouble()); |
| } |
| case TypeFloat: |
| if (isArray) |
| { |
| float[] vArray = new float[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readFloat(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Float.valueOf(in.readFloat()); |
| } |
| case TypeInteger: |
| if (isArray) |
| { |
| int[] vArray = new int[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readInt(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Integer.valueOf(in.readInt()); |
| } |
| case TypeLong: |
| if (isArray) |
| { |
| long[] vArray = new long[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readLong(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Long.valueOf(in.readLong()); |
| } |
| case TypeShort: |
| if (isArray) |
| { |
| short[] vArray = new short[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = in.readShort(); |
| } |
| return vArray; |
| } |
| else |
| { |
| return Short.valueOf(in.readShort()); |
| } |
| case TypeString: |
| if (isArray) |
| { |
| String[] vArray = new String[in.readInt()]; |
| for (int i = 0; i < vArray.length; i++) |
| { |
| vArray[i] = metaDataReader.readString(in); |
| } |
| return vArray; |
| } |
| else |
| { |
| return metaDataReader.readString(in); |
| } |
| } |
| return null; |
| } |
| |
| void storePropertyValue(Object value, DataOutputStream out, |
| MetaDataWriter metaDataWriter) throws IOException |
| { |
| if (value == null) |
| { |
| // handle null value as a string |
| out.writeBoolean(false); |
| out.writeByte(TypeString); |
| metaDataWriter.writeString(null, out); |
| return; |
| } |
| Class<?> arrayType = value.getClass().getComponentType(); |
| boolean isArray = arrayType != null; |
| out.writeBoolean(isArray); |
| byte valueType = getType(arrayType == null ? value.getClass() : arrayType); |
| out.writeByte(valueType); |
| switch (valueType) |
| { |
| case TypeBoolean: |
| if (isArray) |
| { |
| boolean[] vArray = (boolean[]) value; |
| out.writeInt(vArray.length); |
| for (boolean v : vArray) |
| { |
| out.writeBoolean(v); |
| } |
| } |
| else |
| { |
| out.writeBoolean((boolean) value); |
| } |
| break; |
| case TypeByte: |
| if (isArray) |
| { |
| byte[] vArray = (byte[]) value; |
| out.writeInt(vArray.length); |
| for (byte v : vArray) |
| { |
| out.writeByte(v); |
| } |
| } |
| else |
| { |
| out.writeByte((byte) value); |
| } |
| break; |
| case TypeChar: |
| if (isArray) |
| { |
| char[] vArray = (char[]) value; |
| out.writeInt(vArray.length); |
| for (char v : vArray) |
| { |
| out.writeChar(v); |
| } |
| } |
| else |
| { |
| out.writeChar((char) value); |
| } |
| break; |
| case TypeDouble: |
| if (isArray) |
| { |
| double[] vArray = (double[]) value; |
| out.writeInt(vArray.length); |
| for (double v : vArray) |
| { |
| out.writeDouble(v); |
| } |
| } |
| else |
| { |
| out.writeDouble((double) value); |
| } |
| break; |
| case TypeFloat: |
| if (isArray) |
| { |
| float[] vArray = (float[]) value; |
| out.writeInt(vArray.length); |
| for (float v : vArray) |
| { |
| out.writeFloat(v); |
| } |
| } |
| else |
| { |
| out.writeFloat((float) value); |
| } |
| break; |
| case TypeInteger: |
| if (isArray) |
| { |
| int[] vArray = (int[]) value; |
| out.writeInt(vArray.length); |
| for (int v : vArray) |
| { |
| out.writeInt(v); |
| } |
| } |
| else |
| { |
| out.writeInt((int) value); |
| } |
| break; |
| case TypeLong: |
| if (isArray) |
| { |
| long[] vArray = (long[]) value; |
| out.writeInt(vArray.length); |
| for (long v : vArray) |
| { |
| out.writeLong(v); |
| } |
| } |
| else |
| { |
| out.writeLong((long) value); |
| } |
| break; |
| case TypeShort: |
| if (isArray) |
| { |
| short[] vArray = (short[]) value; |
| out.writeInt(vArray.length); |
| for (short v : vArray) |
| { |
| out.writeShort(v); |
| } |
| } |
| else |
| { |
| out.writeShort((short) value); |
| } |
| break; |
| case TypeString: |
| if (isArray) |
| { |
| String[] vArray = (String[]) value; |
| out.writeInt(vArray.length); |
| for (String v : vArray) |
| { |
| metaDataWriter.writeString(v, out); |
| } |
| |
| } |
| else |
| { |
| metaDataWriter.writeString((String) value, out); |
| } |
| break; |
| } |
| } |
| |
| private byte getType(Class<?> typeClass) |
| { |
| if (Boolean.class.equals(typeClass) || boolean.class.equals(typeClass)) |
| { |
| return TypeBoolean; |
| } |
| if (Byte.class.equals(typeClass) || byte.class.equals(typeClass)) |
| { |
| return TypeByte; |
| } |
| if (Character.class.equals(typeClass) || char.class.equals(typeClass)) |
| { |
| return TypeChar; |
| } |
| if (Double.class.equals(typeClass) || double.class.equals(typeClass)) |
| { |
| return TypeDouble; |
| } |
| if (Float.class.equals(typeClass) || float.class.equals(typeClass)) |
| { |
| return TypeFloat; |
| } |
| if (Integer.class.equals(typeClass) || int.class.equals(typeClass)) |
| { |
| return TypeInteger; |
| } |
| if (Long.class.equals(typeClass) || long.class.equals(typeClass)) |
| { |
| return TypeLong; |
| } |
| if (Short.class.equals(typeClass) || short.class.equals(typeClass)) |
| { |
| return TypeShort; |
| } |
| if (String.class.equals(typeClass)) |
| { |
| return TypeString; |
| } |
| throw new IllegalArgumentException("Unsupported type: " + typeClass); |
| |
| } |
| |
| } |