Prevent registration of duplicate CommandInfo instances.
diff --git a/activation-api-1.1/pom.xml b/activation-api-1.1/pom.xml
index 25a79a3..77794fb 100644
--- a/activation-api-1.1/pom.xml
+++ b/activation-api-1.1/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.servicemix.specs</groupId>
         <artifactId>specs-pom</artifactId>
-        <version>1-SNAPSHOT</version>
+        <version>2</version>
         <relativePath>../specs-pom/pom.xml</relativePath>
     </parent>
 
@@ -38,14 +38,13 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/servicemix-specs.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/servicemix-specs.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=servicemix-specs.git</url>
-        <tag>HEAD</tag>
     </scm>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.servicemix.specs</groupId>
             <artifactId>org.apache.servicemix.specs.locator</artifactId>
-            <version>2.10-SNAPSHOT</version>
+            <version>2.10</version>
         </dependency>
         <dependency>
             <groupId>org.apache.geronimo.specs</groupId>
@@ -58,6 +57,18 @@
             <version>1.4.0</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>3.3.3</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -131,6 +142,22 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>3.0.0-M4</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                        <configuration>
+                            <argLine>-Djava.endorsed.dirs=${project.build.directory},${maven.dependency.org.osgi.core.jar.path}</argLine>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 
diff --git a/activation-api-1.1/src/main/java/javax/activation/MailcapCommandMap.java b/activation-api-1.1/src/main/java/javax/activation/MailcapCommandMap.java
index a028635..492cbb0 100644
--- a/activation-api-1.1/src/main/java/javax/activation/MailcapCommandMap.java
+++ b/activation-api-1.1/src/main/java/javax/activation/MailcapCommandMap.java
@@ -28,7 +28,6 @@
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URL;
-import java.security.Security;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -277,11 +276,21 @@
                     cmdList = new ArrayList();
                     allCommands.put(mimeType, cmdList);
                 }
-                cmdList.add(info);
+                addUnique(cmdList, info);
             }
         }
     }
 
+    private void addUnique(List commands, CommandInfo newCommand) {
+        for (Iterator i = commands.iterator(); i.hasNext();) {
+            CommandInfo info = (CommandInfo)i.next();
+            if (info.getCommandName().equals(newCommand.getCommandName())
+                    && info.getCommandClass().equals(newCommand.getCommandClass())) {
+                return;
+            }
+        }
+        commands.add(newCommand);
+    }
 
     /**
      * Add a command to a target command list (preferred or fallback).
diff --git a/activation-api-1.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java b/activation-api-1.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java
index fc2643f..30ff5df 100644
--- a/activation-api-1.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java
+++ b/activation-api-1.1/src/main/java/org/apache/servicemix/specs/activation/Activator.java
@@ -99,6 +99,7 @@
                     commandMap.addMailcap(line, mailcap.bundle);
                 }
             }
+            commandMap.addMailcap("", null);
             CommandMap.setDefaultCommandMap(commandMap);
         } finally {
             Thread.currentThread().setContextClassLoader(tccl);
diff --git a/activation-api-1.1/src/test/java/javax/activation/ITBugDuplicatedMaincapRegistrations.java b/activation-api-1.1/src/test/java/javax/activation/ITBugDuplicatedMaincapRegistrations.java
new file mode 100644
index 0000000..4bb4749
--- /dev/null
+++ b/activation-api-1.1/src/test/java/javax/activation/ITBugDuplicatedMaincapRegistrations.java
@@ -0,0 +1,49 @@
+/*
+ * 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 javax.activation;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+
+/**
+ * SM-4383. Filter out duplicate registrations of a command.
+ */
+public class ITBugDuplicatedMaincapRegistrations {
+
+    private static final String MIMETYPE = "example/sm-4383";
+
+    private static final String MAILCAP = MIMETYPE+";; x-java-content-handler=some.value";
+
+    @Test
+    public void testFilterDuplicates() {
+        MailcapCommandMap mc = new MailcapCommandMap();
+
+        assertEquals(0, mc.getAllCommands(MIMETYPE).length);
+
+        mc.addMailcap(MAILCAP);
+        mc.addMailcap(MAILCAP);
+        assertEquals(1, mc.getAllCommands(MIMETYPE).length);
+
+        mc.addMailcap(MAILCAP+".alt");
+        assertEquals(2, mc.getAllCommands(MIMETYPE).length);
+	}
+
+}
diff --git a/activation-api-1.1/src/test/java/org/apache/servicemix/specs/activation/ITClearCurrentBundle.java b/activation-api-1.1/src/test/java/org/apache/servicemix/specs/activation/ITClearCurrentBundle.java
new file mode 100644
index 0000000..7cf6f49
--- /dev/null
+++ b/activation-api-1.1/src/test/java/org/apache/servicemix/specs/activation/ITClearCurrentBundle.java
@@ -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.servicemix.specs.activation;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.net.URL;
+
+import javax.activation.CommandMap;
+import javax.activation.DataContentHandler;
+
+import org.junit.After;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.framework.Bundle;
+
+/**
+ * After Activator.rebuildCommandMap the currentBundle of OsgiMailcapCommandMap should be null, so that calls
+ * to addMailcap do not register with the last started bundle.
+ */
+public class ITClearCurrentBundle {
+
+	@After
+	public void tearDown() {
+		CommandMap.setDefaultCommandMap(null);
+	}
+
+	@Test
+	public void testClearCurrentBundle() throws Exception {
+		Activator activator = new Activator();
+		Bundle bundle = createBundle(ITClearCurrentBundle.class.getResource("/mailcap.example"));
+		
+		activator.register(bundle);
+		
+		assertTrue(CommandMap.getDefaultCommandMap() instanceof OsgiMailcapCommandMap);
+		assertNullCurrentBundle((OsgiMailcapCommandMap) CommandMap.getDefaultCommandMap());
+	}
+
+	private void assertNullCurrentBundle(OsgiMailcapCommandMap commandMap) throws Exception {
+		Field field = OsgiMailcapCommandMap.class.getDeclaredField("currentBundle");
+		boolean oldAccessible = field.isAccessible();
+		try {
+			field.setAccessible(true);
+			assertNull(field.get(commandMap));
+		}
+		finally {
+			field.setAccessible(oldAccessible);
+		}
+	}
+
+	private Bundle createBundle(URL mailcapResource) throws Exception {
+		Bundle result = Mockito.mock(Bundle.class);
+		Mockito.when(result.loadClass("javax.activation.DataContentHandler")).thenReturn(DataContentHandler.class);
+		Mockito.when(result.getResource("/META-INF/mailcap")).thenReturn(mailcapResource);
+		return result;
+	}
+}
diff --git a/activation-api-1.1/src/test/resources/mailcap.example b/activation-api-1.1/src/test/resources/mailcap.example
new file mode 100644
index 0000000..3ec1b0d
--- /dev/null
+++ b/activation-api-1.1/src/test/resources/mailcap.example
@@ -0,0 +1,2 @@
+# Example mailcap
+example/sm-4383;; x-java-content-handler=some.value