FELIX-1436 Ensure duplicate implementation and/or service elements render
the descriptor invalid. Include unit tests for this.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@799809 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java b/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
index cb24da7..173a6ac 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
@@ -49,6 +49,12 @@
     // set of valid configuration policy settings
     private static final Set 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 int m_namespaceCode;
@@ -186,7 +192,16 @@
         {
             return;
         }
-        m_implementationClassName = implementationClassName;
+
+        // set special flag value if implementation class is already set
+        if ( m_implementationClassName != null )
+        {
+            m_implementationClassName = IMPLEMENTATION_CLASS_DUPLICATE;
+        }
+        else
+        {
+            m_implementationClassName = implementationClassName;
+        }
     }
 
 
@@ -284,7 +299,16 @@
         {
             return;
         }
-        m_service = service;
+
+        // set special flag value if implementation class is already set
+        if ( m_service != null )
+        {
+            m_service = SERVICE_DUPLICATE;
+        }
+        else
+        {
+            m_service = service;
+        }
     }
 
 
@@ -550,6 +574,10 @@
         {
             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 )
@@ -614,7 +642,11 @@
         m_propertyMetaData.clear();
 
         // Check that the provided services are valid too
-        if ( m_service != null )
+        if ( m_service == SERVICE_DUPLICATE )
+        {
+            throw validationFailure( "Service element must occur at most once" );
+        }
+        else if ( m_service != null )
         {
             m_service.validate( this );
         }
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java b/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java
index 57f56d7..ba4b78a 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java
@@ -474,6 +474,72 @@
     }
 
 
+    public void test_duplicate_implementation_ds10()
+    {
+        final ComponentMetadata cm = createComponentMetadata( Boolean.TRUE, null );
+        cm.setImplementationClassName( "second.implementation.class" );
+        try
+        {
+            cm.validate( logger );
+            fail( "Expect validation failure for duplicate implementation element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
+    public void test_duplicate_implementation_ds11()
+    {
+        final ComponentMetadata cm = createComponentMetadata11( Boolean.TRUE, null );
+        cm.setImplementationClassName( "second.implementation.class" );
+        try
+        {
+            cm.validate( logger );
+            fail( "Expect validation failure for duplicate implementation element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
+    public void test_duplicate_service_ds10()
+    {
+        final ComponentMetadata cm = createComponentMetadata( Boolean.TRUE, null );
+        cm.setService( createServiceMetadata( Boolean.TRUE ) );
+        cm.setService( createServiceMetadata( Boolean.TRUE ) );
+        try
+        {
+            cm.validate( logger );
+            fail( "Expect validation failure for duplicate service element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
+    public void test_duplicate_service_ds11()
+    {
+        final ComponentMetadata cm = createComponentMetadata11( Boolean.TRUE, null );
+        cm.setService( createServiceMetadata( Boolean.TRUE ) );
+        cm.setService( createServiceMetadata( Boolean.TRUE ) );
+        try
+        {
+            cm.validate( logger );
+            fail( "Expect validation failure for duplicate service element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
     //---------- Helper methods
 
     // Creates DS 1.0 Component Metadata
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java b/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
index a206790..b659a54 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
@@ -198,6 +198,74 @@
     }
 
 
+    public void test_duplicate_implementation_class_10() throws Exception
+    {
+        final List metadataList10 = readMetadata( "/components_duplicate_implementation_10.xml" );
+        assertEquals( "Component Descriptors", 1, metadataList10.size() );
+        final ComponentMetadata cm10 = ( ComponentMetadata ) metadataList10.get( 0 );
+        try
+        {
+            cm10.validate( logger );
+            fail( "Expect validation failure for duplicate implementation element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
+    public void test_duplicate_implementation_class_11() throws Exception
+    {
+        final List metadataList11 = readMetadata( "/components_duplicate_implementation_11.xml" );
+        assertEquals( "Component Descriptors", 1, metadataList11.size() );
+        final ComponentMetadata cm11 = ( ComponentMetadata ) metadataList11.get( 0 );
+        try
+        {
+            cm11.validate( logger );
+            fail( "Expect validation failure for duplicate implementation element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
+    public void test_duplicate_service_10() throws Exception
+    {
+        final List metadataList10 = readMetadata( "/components_duplicate_service_10.xml" );
+        assertEquals( "Component Descriptors", 1, metadataList10.size() );
+        final ComponentMetadata cm10 = ( ComponentMetadata ) metadataList10.get( 0 );
+        try
+        {
+            cm10.validate( logger );
+            fail( "Expect validation failure for duplicate service element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
+    public void test_duplicate_service_11() throws Exception
+    {
+        final List metadataList11 = readMetadata( "/components_duplicate_service_11.xml" );
+        assertEquals( "Component Descriptors", 1, metadataList11.size() );
+        final ComponentMetadata cm11 = ( ComponentMetadata ) metadataList11.get( 0 );
+        try
+        {
+            cm11.validate( logger );
+            fail( "Expect validation failure for duplicate service element" );
+        }
+        catch ( ComponentException ce )
+        {
+            // expected
+        }
+    }
+
+
     //---------- helper
 
     private List readMetadata( String filename ) throws IOException, ComponentException, XmlPullParserException,
diff --git a/scr/src/test/resources/components_duplicate_implementation_10.xml b/scr/src/test/resources/components_duplicate_implementation_10.xml
new file mode 100644
index 0000000..2cfd478
--- /dev/null
+++ b/scr/src/test/resources/components_duplicate_implementation_10.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<components>
+    <scr:component name="components.duplicate.implementation.10"
+        xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+        <implementation class="components.duplicate.implementation.10.first" />
+        <implementation class="components.duplicate.implementation.10.second" />
+    </scr:component>
+</components>
diff --git a/scr/src/test/resources/components_duplicate_implementation_11.xml b/scr/src/test/resources/components_duplicate_implementation_11.xml
new file mode 100644
index 0000000..5f2cc38
--- /dev/null
+++ b/scr/src/test/resources/components_duplicate_implementation_11.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<components>
+    <scr:component name="components.duplicate.implementation.11"
+        xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+        <implementation class="components.duplicate.implementation.11.first" />
+        <implementation class="components.duplicate.implementation.11.second" />
+    </scr:component>
+</components>
diff --git a/scr/src/test/resources/components_duplicate_service_10.xml b/scr/src/test/resources/components_duplicate_service_10.xml
new file mode 100644
index 0000000..dab7772
--- /dev/null
+++ b/scr/src/test/resources/components_duplicate_service_10.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<components>
+    <scr:component name="components.duplicate.service.10"
+        xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
+        <implementation class="components.duplicate.implementation.10" />
+        <service>
+            <provide interface="if.service.first" />
+        </service>
+        <service>
+            <provide interface="if.service.second" />
+        </service>
+    </scr:component>
+</components>
diff --git a/scr/src/test/resources/components_duplicate_service_11.xml b/scr/src/test/resources/components_duplicate_service_11.xml
new file mode 100644
index 0000000..2356a11
--- /dev/null
+++ b/scr/src/test/resources/components_duplicate_service_11.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<components>
+    <scr:component name="components.duplicate.service.11"
+        xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+        <implementation class="components.duplicate.implementation.11" />
+        <service>
+            <provide interface="if.service.first" />
+        </service>
+        <service>
+            <provide interface="if.service.second" />
+        </service>
+    </scr:component>
+</components>