import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Sets;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URI;
/**
* Checks if method is RPC invocation
*
- *
- *
* @param possibleMethod
* Method to check
* @return true if method is RPC invocation, false otherwise.
public static boolean isRpcMethod(final Method possibleMethod) {
return possibleMethod != null && RpcService.class.isAssignableFrom(possibleMethod.getDeclaringClass())
&& Future.class.isAssignableFrom(possibleMethod.getReturnType())
- && possibleMethod.getParameterTypes().length <= 1;
+ // length <= 2: it seemed to be impossible to get correct RpcMethodInvoker because of
+ // resolveRpcInputClass() check.While RpcMethodInvoker counts with one argument for
+ // non input type and two arguments for input type, resolveRpcInputClass() counting
+ // with zero for non input and one for input type
+ && possibleMethod.getParameterTypes().length <= 2;
}
/**
*/
@SuppressWarnings("rawtypes")
public static Optional<Class<?>> resolveRpcOutputClass(final Method targetMethod) {
- checkState(isRpcMethod(targetMethod), "Supplied method is not Rpc invocation method");
+ checkState(isRpcMethod(targetMethod), "Supplied method is not a RPC invocation method");
Type futureType = targetMethod.getGenericReturnType();
Type rpcResultType = ClassLoaderUtils.getFirstGenericParameter(futureType);
Type rpcResultArgument = ClassLoaderUtils.getFirstGenericParameter(rpcResultType);
if (rpcResultArgument instanceof Class && !Void.class.equals(rpcResultArgument)) {
- return Optional.<Class<?>> of((Class) rpcResultArgument);
+ return Optional.of((Class) rpcResultArgument);
}
return Optional.absent();
}
* method to scan
* @return Optional.absent() if rpc has no input, Rpc input type otherwise.
*/
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("rawtypes")
public static Optional<Class<? extends DataContainer>> resolveRpcInputClass(final Method targetMethod) {
- @SuppressWarnings("rawtypes")
- Class[] types = targetMethod.getParameterTypes();
- if (types.length == 0) {
- return Optional.absent();
- }
- if (types.length == 1) {
- return Optional.<Class<? extends DataContainer>> of(types[0]);
+ for (Class clazz : targetMethod.getParameterTypes()) {
+ if (DataContainer.class.isAssignableFrom(clazz)) {
+ return Optional.of(clazz);
+ }
}
- throw new IllegalArgumentException("Method has 2 or more arguments.");
+ return Optional.absent();
}
public static QName getQName(final Class<? extends BaseIdentity> context) {
checkArgument(cls != null);
String packageName = getModelRootPackageName(cls.getPackage());
final String potentialClassName = getModuleInfoClassName(packageName);
- return ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Callable<YangModuleInfo>() {
- @Override
- public YangModuleInfo call() throws ClassNotFoundException, IllegalAccessException,
- IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
- Class<?> moduleInfoClass = Thread.currentThread().getContextClassLoader().loadClass(potentialClassName);
- return (YangModuleInfo) moduleInfoClass.getMethod("getInstance").invoke(null);
- }
- });
+ return ClassLoaderUtils.withClassLoader(cls.getClassLoader(), (Callable<YangModuleInfo>) () -> {
+ Class<?> moduleInfoClass = Thread.currentThread().getContextClassLoader().loadClass(potentialClassName);
+ return (YangModuleInfo) moduleInfoClass.getMethod("getInstance").invoke(null);
+ });
}
public static String getModuleInfoClassName(final String packageName) {
if (DataContainer.class.isAssignableFrom(cls) || Augmentation.class.isAssignableFrom(cls)) {
return true;
}
- return (cls.getName().startsWith(BindingMapping.PACKAGE_PREFIX));
+ return cls.getName().startsWith(BindingMapping.PACKAGE_PREFIX);
}
/**
* @return Set of {@link YangModuleInfo} available for supplied classloader.
*/
public static ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader loader) {
- Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.<YangModuleInfo> builder();
+ Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.builder();
ServiceLoader<YangModelBindingProvider> serviceLoader = ServiceLoader.load(YangModelBindingProvider.class,
loader);
for (YangModelBindingProvider bindingProvider : serviceLoader) {
@SuppressWarnings("unchecked")
private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method) {
- if (method.getName().equals("getClass") || !method.getName().startsWith("get")
+ if ("getClass".equals(method.getName()) || !method.getName().startsWith("get")
|| method.getParameterTypes().length > 0) {
return Optional.absent();
}
@SuppressWarnings("rawtypes")
Class returnType = method.getReturnType();
if (DataContainer.class.isAssignableFrom(returnType)) {
- return Optional.<Class<? extends DataContainer>> of(returnType);
+ return Optional.of(returnType);
} else if (List.class.isAssignableFrom(returnType)) {
try {
return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
- new Callable<Optional<Class<? extends DataContainer>>>() {
- @SuppressWarnings("rawtypes")
- @Override
- public Optional<Class<? extends DataContainer>> call() {
- Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
- .getGenericReturnType());
- if (listResult instanceof Class
- && DataContainer.class.isAssignableFrom((Class) listResult)) {
- return Optional.<Class<? extends DataContainer>> of((Class) listResult);
- }
- return Optional.absent();
+ (Callable<Optional<Class<? extends DataContainer>>>) () -> {
+ Type listResult = ClassLoaderUtils.getFirstGenericParameter(method.getGenericReturnType());
+ if (listResult instanceof Class
+ && DataContainer.class.isAssignableFrom((Class) listResult)) {
+ return Optional.of((Class) listResult);
}
-
+ return Optional.absent();
});
} catch (Exception e) {
/*
private static class ClassToQNameLoader extends CacheLoader<Class<?>, Optional<QName>> {
@Override
- public Optional<QName> load(final Class<?> key) throws Exception {
+ public Optional<QName> load(@SuppressWarnings("NullableProblems") final Class<?> key) throws Exception {
return resolveQNameNoCache(key);
}
- }
- /**
- *
- * Tries to resolve QName for supplied class.
- *
- * Looks up for static field with name from constant
- * {@link BindingMapping#QNAME_STATIC_FIELD_NAME} and returns value if
- * present.
- *
- * If field is not present uses {@link #computeQName(Class)} to compute
- * QName for missing types.
- *
- * @param key
- * @return
- */
- private static Optional<QName> resolveQNameNoCache(final Class<?> key) {
- try {
- Field field = key.getField(BindingMapping.QNAME_STATIC_FIELD_NAME);
- Object obj = field.get(null);
- if (obj instanceof QName) {
- return Optional.of((QName) obj);
- }
+ /**
+ *
+ * Tries to resolve QName for supplied class.
+ *
+ * Looks up for static field with name from constant {@link BindingMapping#QNAME_STATIC_FIELD_NAME} and returns
+ * value if present.
+ *
+ * If field is not present uses {@link #computeQName(Class)} to compute QName for missing types.
+ *
+ * @param key
+ * @return
+ */
+ private static Optional<QName> resolveQNameNoCache(final Class<?> key) {
+ try {
+ Field field = key.getField(BindingMapping.QNAME_STATIC_FIELD_NAME);
+ Object obj = field.get(null);
+ if (obj instanceof QName) {
+ return Optional.of((QName) obj);
+ }
- } catch (NoSuchFieldException e) {
- return Optional.of(computeQName(key));
-
- } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
- /*
- *
- * It is safe to log this this exception on debug, since this method
- * should not fail. Only failures are possible if the runtime /
- * backing.
- */
- LOG.debug("Unexpected exception during extracting QName for {}", key, e);
- }
- return Optional.absent();
- }
+ } catch (NoSuchFieldException e) {
+ return Optional.of(computeQName(key));
- /**
- * Computes QName for supplied class
- *
- * Namespace and revision are same as {@link YangModuleInfo} associated with
- * supplied class.
- * <p>
- * If class is
- * <ul>
- * <li>rpc input: local name is "input".
- * <li>rpc output: local name is "output".
- * <li>augmentation: local name is "module name".
- * </ul>
- *
- * There is also fallback, if it is not possible to compute QName using
- * following algorithm returns module QName.
- *
- * FIXME: Extend this algorithm to also provide QName for YANG modeled
- * simple types.
- *
- * @throws IllegalStateException
- * If YangModuleInfo could not be resolved
- * @throws IllegalArgumentException
- * If supplied class was not derived from YANG model.
- *
- */
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private static QName computeQName(final Class key) {
- if (isBindingClass(key)) {
- YangModuleInfo moduleInfo;
- try {
- moduleInfo = getModuleInfo(key);
- } catch (Exception e) {
- throw new IllegalStateException("Unable to get QName for " + key + ". YangModuleInfo was not found.", e);
+ } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ /*
+ *
+ * It is safe to log this this exception on debug, since this method
+ * should not fail. Only failures are possible if the runtime /
+ * backing.
+ */
+ LOG.debug("Unexpected exception during extracting QName for {}", key, e);
}
- final QName module = getModuleQName(moduleInfo).intern();
- if (Augmentation.class.isAssignableFrom(key)) {
- return module;
- } else if (isRpcType(key)) {
- final String className = key.getSimpleName();
- if (className.endsWith(BindingMapping.RPC_OUTPUT_SUFFIX)) {
- return QName.create(module, "output").intern();
- } else {
- return QName.create(module, "input").intern();
+ return Optional.absent();
+ }
+
+ /**
+ * Computes QName for supplied class
+ *
+ * Namespace and revision are same as {@link YangModuleInfo} associated with supplied class.
+ * <p>
+ * If class is
+ * <ul>
+ * <li>rpc input: local name is "input".
+ * <li>rpc output: local name is "output".
+ * <li>augmentation: local name is "module name".
+ * </ul>
+ *
+ * There is also fallback, if it is not possible to compute QName using following algorithm returns module
+ * QName.
+ *
+ * FIXME: Extend this algorithm to also provide QName for YANG modeled simple types.
+ *
+ * @throws IllegalStateException If YangModuleInfo could not be resolved
+ * @throws IllegalArgumentException If supplied class was not derived from YANG model.
+ *
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private static QName computeQName(final Class key) {
+ if (isBindingClass(key)) {
+ YangModuleInfo moduleInfo;
+ try {
+ moduleInfo = getModuleInfo(key);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to get QName for " + key
+ + ". YangModuleInfo was not found.", e);
+ }
+ final QName module = getModuleQName(moduleInfo).intern();
+ if (Augmentation.class.isAssignableFrom(key)) {
+ return module;
+ } else if (isRpcType(key)) {
+ final String className = key.getSimpleName();
+ if (className.endsWith(BindingMapping.RPC_OUTPUT_SUFFIX)) {
+ return QName.create(module, "output").intern();
+ } else {
+ return QName.create(module, "input").intern();
+ }
}
+ /*
+ * Fallback for Binding types which do not have QNAME field
+ */
+ return module;
+ } else {
+ throw new IllegalArgumentException("Supplied class " + key + "is not derived from YANG.");
}
- /*
- * Fallback for Binding types which do not have QNAME field
- */
- return module;
- } else {
- throw new IllegalArgumentException("Supplied class " + key + "is not derived from YANG.");
}
+
}
/**
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.binding.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Future;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class AbstractMappedRpcInvokerTest {
+
+ @Test
+ public void invokeRpcTest() throws Exception {
+ final Method methodWithoutInput =
+ TestRpcService.class.getDeclaredMethod("methodWithoutInput", RpcService.class);
+ final Method methodWithInput =
+ TestRpcService.class.getDeclaredMethod("methodWithInput", RpcService.class, DataObject.class);
+
+ methodWithInput.setAccessible(true);
+ methodWithoutInput.setAccessible(true);
+
+ final RpcService rpcService = new TestRpcService();
+
+ final RpcServiceInvoker testRpcInvoker =
+ new TestRpcInvokerImpl(ImmutableMap.of(
+ "tstWithoutInput", methodWithoutInput,
+ "tstWithInput", methodWithInput));
+
+ final Field testInvokerMapField = testRpcInvoker.getClass().getSuperclass().getDeclaredField("map");
+ testInvokerMapField.setAccessible(true);
+ final Map<String, RpcMethodInvoker> testInvokerMap =
+ (Map<String, RpcMethodInvoker>) testInvokerMapField.get(testRpcInvoker);
+
+ assertTrue(testInvokerMap.get("tstWithInput") instanceof RpcMethodInvokerWithInput);
+ assertTrue(testInvokerMap.get("tstWithoutInput") instanceof RpcMethodInvokerWithoutInput);
+
+ final Crate crateWithoutInput =
+ (Crate) testRpcInvoker.invokeRpc(rpcService, QName.create("tstWithoutInput"), null).get();
+ assertEquals(TestRpcService.methodWithoutInput(rpcService).get().getRpcService(),
+ crateWithoutInput.getRpcService());
+ assertFalse(crateWithoutInput.getDataObject().isPresent());
+
+ final DataObject dataObject = mock(DataObject.class);
+ final Crate crateWithInput =
+ (Crate) testRpcInvoker.invokeRpc(rpcService, QName.create("tstWithInput"), dataObject).get();
+ assertEquals(TestRpcService.methodWithInput(rpcService, dataObject).get().getRpcService(),
+ crateWithInput.getRpcService());
+ assertTrue(crateWithInput.getDataObject().isPresent());
+ assertEquals(dataObject, crateWithInput.getDataObject().get());
+ }
+
+ private class TestRpcInvokerImpl extends AbstractMappedRpcInvoker<String> {
+
+ TestRpcInvokerImpl(Map<String, Method> map) {
+ super(map);
+ }
+
+ @Override
+ protected String qnameToKey(QName qname) {
+ return qname.toString();
+ }
+ }
+
+ static class Crate {
+ private final RpcService rpcService;
+ private final ThreadLocal<Optional<DataObject>> dataObject;
+
+ Crate(@Nonnull RpcService rpcService, @Nullable DataObject dataObject) {
+ this.rpcService = rpcService;
+ this.dataObject = new ThreadLocal<Optional<DataObject>>() {
+ @Override
+ protected Optional<DataObject> initialValue() {
+ return dataObject == null ? Optional.empty() : Optional.of(dataObject);
+ }
+ };
+ }
+
+ RpcService getRpcService() {
+ return this.rpcService;
+ }
+
+ Optional<DataObject> getDataObject() {
+ return this.dataObject.get();
+ }
+ }
+
+ static class TestRpcService implements RpcService {
+ static Future<Crate> methodWithoutInput(RpcService testArgument) {
+ return Futures.immediateFuture(new Crate(testArgument, null));
+ }
+
+ static Future<Crate> methodWithInput(RpcService testArgument, DataObject testArgument2) {
+ return Futures.immediateFuture(new Crate(testArgument, testArgument2));
+ }
+ }
+}
+