package com.temenos.interaction.core.command; /* * #%L * interaction-core * %% * Copyright (C) 2012 - 2015 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.reflections.Reflections; import org.reflections.util.ConfigurationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import com.temenos.interaction.core.command.annotation.InteractionCommandImpl; /** * Implementation of {@link CommandController} based on Annotation. Using * Reflection will scan the classes in JAR's & Packages for required annotation. * Resolution is based on the name attribute of the annotation. * * @author hmanchala * @author trojanbug */ public class AnnotationBasedCommandController implements CommandController { private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationBasedCommandController.class); protected Map<String, InteractionCommand> cache = new HashMap<String, InteractionCommand>(); private ClassLoader classloader = null; private Collection<String> packagesToScan = null; private Collection<URL> jarsToScan = null; private Reflections reflectionsHelper = null; private boolean initialized = false; public AnnotationBasedCommandController() { this(Thread.currentThread().getContextClassLoader()); } public AnnotationBasedCommandController(ClassLoader classloader) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("AnnotationBasedCommandController created with classloader {}", classloader != null ? classloader.getClass().getCanonicalName() : "NULL"); } setClassloader(classloader); } /** * @param name * @return If the command is already in the cache it is returned else using * reflection scan the JARs/Packages and get the required annotated classes * which have implemented the interface, and return the object by calling * BeanUtils.instantiate() method */ @Override public InteractionCommand fetchCommand(String name) { LOGGER.trace("AnnotationBasedCommandController requested finding command for name: {}", name); if (!initialized) { LOGGER.debug("AnnotationBasedCommandController store not yet initialized (or reintiialized) - requesting initialisation"); reinitialize(); } return cache.get(name); } protected synchronized void reinitialize() { LOGGER.debug("AnnotationBasedCommandController initializing."); ConfigurationBuilder config = new ConfigurationBuilder(); if (getJarsToScan() != null && (!jarsToScan.isEmpty())) { config.setUrls(getJarsToScan()); } if (getClassloader() != null) { config.addClassLoader(getClassloader()); } if (getPackagesToScan() != null && (!packagesToScan.isEmpty())) { config.forPackages(getPackagesToScan().toArray(new String[]{})); } reflectionsHelper = new Reflections(config); Set<Class<?>> annotatedClasses = reflectionsHelper.getTypesAnnotatedWith(InteractionCommandImpl.class); for (Class<?> annotatedClass : annotatedClasses) { String nameFound = annotatedClass.getAnnotation(InteractionCommandImpl.class).name(); Object newCommandAsObject = BeanUtils.instantiate(annotatedClass); if (newCommandAsObject instanceof InteractionCommand) { InteractionCommand newCommand = (InteractionCommand) newCommandAsObject; LOGGER.debug("AnnotationBasedCommandController adding {} class to cache uinder the name {}.", newCommand.getClass().getCanonicalName(), nameFound); cache.put(nameFound, newCommand); } else { LOGGER.warn("A class annotated with @InteractionCommandImpl is not an InteractionCommand - ignoring!"); } } initialized = true; } @Override public boolean isValidCommand(String name) { return fetchCommand(name) != null; } public ClassLoader getClassloader() { return classloader; } public final void setClassloader(ClassLoader classloader) { LOGGER.trace("AnnotationBasedCommandController {} setting classloader: {}", this, classloader); this.classloader = classloader; initialized = false; } public Collection<String> getPackagesToScan() { return packagesToScan; } public void setPackagesToScan(Collection<String> packagesToScan) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("AnnotationBasedCommandController {} setting packages to scan: {}", this, Arrays.toString(packagesToScan.toArray(new String[]{}))); } this.packagesToScan = packagesToScan; initialized = false; } public Collection<URL> getJarsToScan() { return jarsToScan; } public void setJarsToScan(Collection<URL> jarsToScan) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("AnnotationBasedCommandController {} setting JARs' URLs to scan: {}", this, Arrays.toString(jarsToScan.toArray())); } this.jarsToScan = jarsToScan; initialized = false; } }