package org.dcache.util.cli; import com.google.common.collect.Maps; import com.google.common.reflect.TypeToken; import java.io.Serializable; import java.lang.reflect.Constructor; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import dmg.util.command.Command; import static java.util.Arrays.asList; /** * CommandScanner for commands implemented as non-static inner * classes implementing Callable and annotated with @Command. */ public class AnnotatedCommandScanner implements CommandScanner { private static final TypeToken<Callable<? extends Serializable>> EXPECTED_TYPE = new TypeToken<Callable<? extends Serializable>>() {}; /** * Verifies that clazz implements Callable<? extends Serializable> and casts it * to that type. * * @param clazz The clazz of the command object * @return clazz cast to Callable<? extends Serializable> */ @SuppressWarnings("unchecked") private Class<? extends Callable<? extends Serializable>> cast(Class<?> clazz) { if (EXPECTED_TYPE.isSupertypeOf(clazz)) { return (Class<? extends Callable<? extends Serializable>>) clazz.asSubclass(Callable.class); } throw new RuntimeException("This is a bug. Please notify support@dcache.org (" + clazz + " does not implement Callable<? extends Serializable>)."); } @Override public Map<List<String>, ? extends CommandExecutor> scan(Object obj) { Map<List<String>, AnnotatedCommandExecutor> commands = Maps.newHashMap(); Class<?> containerClass = obj.getClass(); while (containerClass != null) { Class<?>[] classes = containerClass.getDeclaredClasses(); for (Class<?> commandClass : classes) { Command command = commandClass.getAnnotation(Command.class); if (command != null && !commandClass.isInterface() && Callable.class.isAssignableFrom(commandClass)) { try { Constructor<? extends Callable<? extends Serializable>> constructor = cast(commandClass).getDeclaredConstructor(commandClass.getDeclaringClass()); constructor.setAccessible(true); commands.put(asList(command.name().split(" ")), new AnnotatedCommandExecutor(obj, command, constructor)); } catch (NoSuchMethodException e) { throw new RuntimeException("This is a bug. Please notify support@dcache.org.", e); } } } containerClass = containerClass.getSuperclass(); } return commands; } }