The API contract specifies that the effects of a Registration are
removed on close(). Unfortunately the routed RPC case is more funny, as
it has sub-registrations, for each added path -- and we have no mention
of the fact that the user should be removing them.
Hence we need to handle the user expecting us to remove any and all
paths which have been registered.
Change-Id: Iedf451c2a481d648173819f895cccb9e63c54d99
Signed-off-by: Robert Varga <rovarga@cisco.com>
package org.opendaylight.controller.sal.binding.codegen.impl;
import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.setRoutingTable;
package org.opendaylight.controller.sal.binding.codegen.impl;
import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.setRoutingTable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
public class RpcRouterCodegenInstance<T extends RpcService> implements //
RpcRouter<T>, RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
public class RpcRouterCodegenInstance<T extends RpcService> implements //
RpcRouter<T>, RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
return new DefaultRpcImplementationRegistration(service);
}
return new DefaultRpcImplementationRegistration(service);
}
- private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
+ private final class RoutedRpcRegistrationImpl extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
+ /*
+ * FIXME: retaining this collection is not completely efficient. We really should be storing
+ * a reference to this registration, as a particular listener may be registered multiple
+ * times -- and then this goes kaboom in various aspects.
+ */
+ @GuardedBy("this")
+ private final Collection<Class<? extends BaseIdentity>> contexts = new ArrayList<>(1);
public RoutedRpcRegistrationImpl(final T instance) {
super(instance);
public RoutedRpcRegistrationImpl(final T instance) {
super(instance);
- public void registerPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ public synchronized void registerPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ if (isClosed()) {
+ LOG.debug("Closed registration of {} ignoring new path {}", getInstance(), path);
+ return;
+ }
+
routingTables.get(context).updateRoute(path, getInstance());
routingTables.get(context).updateRoute(path, getInstance());
- public void unregisterPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ public synchronized void unregisterPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ if (isClosed()) {
+ LOG.debug("Closed unregistration of {} ignoring new path {}", getInstance(), path);
+ return;
+ }
+
routingTables.get(context).removeRoute(path, getInstance());
routingTables.get(context).removeRoute(path, getInstance());
+ contexts.remove(context);
@Override
public void registerInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
registerPath(context, instance);
}
@Override
public void registerInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
registerPath(context, instance);
}
@Override
public void unregisterInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
unregisterPath(context, instance);
}
@Override
@Override
public void unregisterInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> instance) {
unregisterPath(context, instance);
}
@Override
- protected void removeRegistration() {
-
+ protected synchronized void removeRegistration() {
+ for (Class<? extends BaseIdentity> ctx : contexts) {
+ routingTables.get(ctx).removeAllReferences(getInstance());
+ }
+ contexts.clear();
- private class DefaultRpcImplementationRegistration extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
+ private final class DefaultRpcImplementationRegistration extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
protected DefaultRpcImplementationRegistration(final T instance) {
protected DefaultRpcImplementationRegistration(final T instance) {
package org.opendaylight.controller.sal.binding.codegen.impl;
import java.util.Collections;
package org.opendaylight.controller.sal.binding.codegen.impl;
import java.util.Collections;
+import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-class RpcRoutingTableImpl<C extends BaseIdentity, S extends RpcService> //
-implements //
+final class RpcRoutingTableImpl<C extends BaseIdentity, S extends RpcService> implements
Mutable, //
RpcRoutingTable<C, S>, //
RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
Mutable, //
RpcRoutingTable<C, S>, //
RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
private RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> listener;
private S defaultRoute;
private RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> listener;
private S defaultRoute;
- public RpcRoutingTableImpl(String routerName,Class<C> contextType, Class<S> serviceType) {
+ public RpcRoutingTableImpl(final String routerName,final Class<C> contextType, final Class<S> serviceType) {
super();
this.routerName = routerName;
this.serviceType = serviceType;
super();
this.routerName = routerName;
this.serviceType = serviceType;
- public void setDefaultRoute(S target) {
+ public void setDefaultRoute(final S target) {
@Override
public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
@Override
public <L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
return new SingletonListenerRegistration<L>(listener);
}
return new SingletonListenerRegistration<L>(listener);
}
@Override
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
- public void updateRoute(InstanceIdentifier<?> path, S service) {
+ public void updateRoute(final InstanceIdentifier<?> path, final S service) {
S previous = this.routes.put(path, service);
LOGGER.debug("Route {} updated to {} in routing table {}",path,service,this);
S previous = this.routes.put(path, service);
LOGGER.debug("Route {} updated to {} in routing table {}",path,service,this);
@Override
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
- public void removeRoute(InstanceIdentifier<?> path) {
+ public void removeRoute(final InstanceIdentifier<?> path) {
S previous = this.routes.remove(path);
LOGGER.debug("Route {} to {} removed in routing table {}",path,previous,this);
@SuppressWarnings("rawtypes")
S previous = this.routes.remove(path);
LOGGER.debug("Route {} to {} removed in routing table {}",path,previous,this);
@SuppressWarnings("rawtypes")
- public void removeRoute(InstanceIdentifier<?> path, S service) {
+ void removeRoute(final InstanceIdentifier<?> path, final S service) {
@SuppressWarnings("rawtypes")
RouteChangeListener listenerCapture = listener;
if (routes.remove(path, service) && listenerCapture != null) {
@SuppressWarnings("rawtypes")
RouteChangeListener listenerCapture = listener;
if (routes.remove(path, service) && listenerCapture != null) {
- public S getRoute(InstanceIdentifier<?> nodeInstance) {
+ public S getRoute(final InstanceIdentifier<?> nodeInstance) {
S route = routes.get(nodeInstance);
if (route != null) {
return route;
S route = routes.get(nodeInstance);
if (route != null) {
return route;
return unmodifiableRoutes;
}
return unmodifiableRoutes;
}
- protected void removeAllReferences(S service) {
-
+ void removeAllReferences(final S service) {
+ // FIXME: replace this via properly-synchronized BiMap (or something)
+ final Iterator<S> it = routes.values().iterator();
+ while (it.hasNext()) {
+ final S s = it.next();
+ if (service.equals(s)) {
+ it.remove();
+ }
+ }
@Override
public String toString() {
return "RpcRoutingTableImpl [router=" + routerName + ", service=" + serviceType.getSimpleName() + ", context="
+ contextType.getSimpleName() + "]";
}
@Override
public String toString() {
return "RpcRoutingTableImpl [router=" + routerName + ", service=" + serviceType.getSimpleName() + ", context="
+ contextType.getSimpleName() + "]";
}
private class SingletonListenerRegistration<L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> extends
AbstractObjectRegistration<L>
implements ListenerRegistration<L> {
private class SingletonListenerRegistration<L extends RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>>> extends
AbstractObjectRegistration<L>
implements ListenerRegistration<L> {
- public SingletonListenerRegistration(L instance) {
+ public SingletonListenerRegistration(final L instance) {
super(instance);
listener = instance;
}
super(instance);
listener = instance;
}