import org.eclipse.xtext.xbase.lib.Extension;
import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.NotificationListener;
import org.opendaylight.yangtools.yang.binding.RpcService;
protected abstract <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType);
protected abstract <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, RpcServiceMetadata metadata);
- private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException {
+ private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException, RpcIsNotRoutedException {
final RpcServiceMetadata metadata = new RpcServiceMetadata();
for (CtMethod method : iface.getMethods()) {
- if (iface.equals(method.getDeclaringClass()) && method.getParameterTypes().length == 1) {
+ if (isRpcMethodWithInput(iface, method)) {
final RpcMetadata routingPair = getRpcMetadata(method);
if (routingPair != null) {
metadata.addContext(routingPair.getContext());
* remains to be investigated.
*/
Thread.currentThread().getContextClassLoader().loadClass(routingPair.getInputType().getName());
+ } else {
+ throw new RpcIsNotRoutedException("RPC " + method.getName() + " from "+ iface.getName() +" is not routed");
}
}
}
return metadata;
}
+
+ private boolean isRpcMethodWithInput(final CtClass iface, final CtMethod method) throws NotFoundException {
+ if(iface.equals(method.getDeclaringClass())
+ && method.getParameterTypes().length == 1) {
+ final CtClass onlyArg = method.getParameterTypes()[0];
+ if(onlyArg.isInterface() && onlyArg.getName().endsWith(BindingMapping.RPC_INPUT_SUFFIX)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private RpcMetadata getRpcMetadata(final CtMethod method) throws NotFoundException {
final CtClass inputClass = method.getParameterTypes()[0];
return rpcMethodMetadata(inputClass, inputClass, method.getName());
}
@Override
- public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) {
+ public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) throws RpcIsNotRoutedException {
final RpcServiceMetadata metadata = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), new Supplier<RpcServiceMetadata>() {
@Override
public RpcServiceMetadata get() {
import static com.google.common.base.Preconditions.checkState;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
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.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper;
import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Throwables;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
public class RpcProviderRegistryImpl implements RpcProviderRegistry, RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
private RuntimeCodeGenerator rpcFactory = SingletonHolder.RPC_GENERATOR_IMPL;
}
});
- private final Map<Class<? extends RpcService>, RpcRouter<?>> rpcRouters = new WeakHashMap<>();
+ private final Cache<Class<? extends RpcService>, RpcRouter<?>> rpcRouters = CacheBuilder.newBuilder().weakKeys()
+ .build();
+
private final ListenerRegistry<RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> routeChangeListeners = ListenerRegistry
.create();
private final ListenerRegistry<RouterInstantiationListener> routerInstantiationListener = ListenerRegistry.create();
@Override
public final <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation)
throws IllegalStateException {
- @SuppressWarnings("unchecked")
- RpcRouter<T> potentialRouter = (RpcRouter<T>) rpcRouters.get(type);
- if (potentialRouter != null) {
+
+ // FIXME: This should be well documented - addRpcImplementation for
+ // routed RPCs
+ try {
+ // Note: If RPC is really global, expected count of registrations
+ // of this method is really low.
+ RpcRouter<T> potentialRouter = getRpcRouter(type);
checkState(potentialRouter.getDefaultService() == null,
- "Default service for routed RPC already registered.");
+ "Default service for routed RPC already registered.");
return potentialRouter.registerDefaultService(implementation);
+ } catch (RpcIsNotRoutedException e) {
+ // NOOP - we could safely continue, since RPC is not routed
+ // so we fallback to global routing.
+ LOG.debug("RPC is not routed. Using global registration.",e);
}
T publicProxy = getRpcService(type);
RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy);
return (T) publicProxies.getUnchecked(type);
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
+
public <T extends RpcService> RpcRouter<T> getRpcRouter(final Class<T> type) {
- RpcRouter<?> potentialRouter = rpcRouters.get(type);
- if (potentialRouter != null) {
- return (RpcRouter<T>) potentialRouter;
- }
- synchronized (this) {
- /**
- * Potential Router could be instantiated by other thread while we
- * were waiting for the lock.
- */
- potentialRouter = rpcRouters.get(type);
- if (potentialRouter != null) {
- return (RpcRouter<T>) potentialRouter;
+ try {
+ final AtomicBoolean created = new AtomicBoolean(false);
+ @SuppressWarnings( "unchecked")
+ // LoadingCache is unsuitable for RpcRouter since we need to distinguish
+ // first creation of RPC Router, so that is why
+ // we are using normal cache with load API and shared AtomicBoolean
+ // for this call, which will be set to true if router was created.
+ RpcRouter<T> router = (RpcRouter<T>) rpcRouters.get(type,new Callable<RpcRouter<?>>() {
+
+ @Override
+ public org.opendaylight.controller.sal.binding.api.rpc.RpcRouter<?> call() {
+ RpcRouter<?> router = rpcFactory.getRouterFor(type, name);
+ router.registerRouteChangeListener(new RouteChangeForwarder<T>(type));
+ LOG.debug("Registering router {} as global implementation of {} in {}", router, type.getSimpleName(), this);
+ RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy());
+ created.set(true);
+ return router;
+ }
+ });
+ if(created.get()) {
+ notifyListenersRoutedCreated(router);
}
- RpcRouter<T> router = rpcFactory.getRouterFor(type, name);
- router.registerRouteChangeListener(new RouteChangeForwarder(type));
- LOG.debug("Registering router {} as global implementation of {} in {}", router, type.getSimpleName(), this);
- RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy());
- rpcRouters.put(type, router);
- notifyListenersRoutedCreated(router);
return router;
+ } catch (ExecutionException | UncheckedExecutionException e) {
+ // We rethrow Runtime Exceptions which were wrapped by
+ // Execution Exceptions
+ // otherwise we throw IllegalStateException with original
+ Throwables.propagateIfPossible(e.getCause());
+ throw new IllegalStateException("Could not load RPC Router for "+type.getName(),e);
}
}
final RouterInstantiationListener listener) {
ListenerRegistration<RouterInstantiationListener> reg = routerInstantiationListener.register(listener);
try {
- for (RpcRouter<?> router : rpcRouters.values()) {
+ for (RpcRouter<?> router : rpcRouters.asMap().values()) {
listener.onRpcRouterCreated(router);
}
} catch (Exception e) {
try {
listener.getInstance().onRouteChange(toPublish);
} catch (Exception e) {
- e.printStackTrace();
+ LOG.error("Unhandled exception during invoking listener",listener.getInstance(),e);
}
}
}
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
assertNotNull(regTwo);
}
+ @Test
+ public void routedRpcRegisteredUsingGlobalAsDefaultInstance() throws Exception {
+ OpendaylightTestRoutedRpcService def = Mockito.mock(OpendaylightTestRoutedRpcService.class);
+ rpcRegistry.addRpcImplementation(OpendaylightTestRoutedRpcService.class, def);
+ RpcRouter<OpendaylightTestRoutedRpcService> router = rpcRegistry.getRpcRouter(OpendaylightTestRoutedRpcService.class);
+ assertEquals(def, router.getDefaultService());
+ }
+
+ @Test
+ public void nonRoutedRegisteredAsRouted() {
+ OpendaylightTestRpcServiceService one = Mockito.mock(OpendaylightTestRpcServiceService.class);
+ try {
+ rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRpcServiceService.class, one);
+ fail("RpcIsNotRoutedException should be thrown");
+ } catch (RpcIsNotRoutedException e) {
+ assertNotNull(e.getMessage());
+ } catch (Exception e) {
+ fail("RpcIsNotRoutedException should be thrown");
+ }
+
+ }
+
@Test
public void testRpcRouterInstance() throws Exception {
OpendaylightTestRoutedRpcService def = Mockito.mock(OpendaylightTestRoutedRpcService.class);