Index: src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java (revision 1709960) +++ src/main/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboard.java (working copy) @@ -42,12 +42,17 @@ import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * OSGi-based whiteboard implementation. */ public class OsgiWhiteboard implements Whiteboard { + private static final Logger log = LoggerFactory + .getLogger(OsgiWhiteboard.class); + private final BundleContext context; public OsgiWhiteboard(@Nonnull BundleContext context) { @@ -56,7 +61,7 @@ @Override public Registration register( - Class type, T service, Map properties) { + final Class type, final T service, Map properties) { checkNotNull(type); checkNotNull(service); checkNotNull(properties); @@ -72,7 +77,12 @@ return new Registration() { @Override public void unregister() { - registration.unregister(); + try { + registration.unregister(); + } catch (IllegalStateException ex) { + log.warn("Error unregistering service: {} of type {}", + service, type.getName(), ex); + } } }; } Index: src/test/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboardTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboardTest.java (revision 0) +++ src/test/java/org/apache/jackrabbit/oak/osgi/OsgiWhiteboardTest.java (revision 0) @@ -0,0 +1,82 @@ +/* + * 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.jackrabbit.oak.osgi; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; + +import org.apache.jackrabbit.oak.spi.whiteboard.Registration; +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; + +public class OsgiWhiteboardTest { + + /** + * OAK-3409 + */ + @Test + public void testDoubleUnregister() { + BundleContext bundleContext = mock(BundleContext.class); + OsgiWhiteboard w = new OsgiWhiteboard(bundleContext); + + Runnable r = new Runnable() { + @Override + public void run() { + // + } + }; + + ServiceRegistration sr = new ServiceRegistration() { + + boolean isUnregistering = false; + + @Override + public void unregister() { + if (isUnregistering) { + throw new IllegalStateException( + "Service already unregistered."); + } + isUnregistering = true; + } + + @Override + public void setProperties(Dictionary properties) { + } + + @Override + public ServiceReference getReference() { + return null; + } + }; + + when( + bundleContext.registerService(Runnable.class.getName(), r, + new Hashtable())).thenReturn(sr); + Registration reg = w.register(Runnable.class, r, + new HashMap()); + reg.unregister(); + reg.unregister(); + } + +}