/* * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hawkular.alerts.actions.standalone; import static org.hawkular.alerts.actions.standalone.ServiceNames.Service.ACTIONS_SERVICE; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.naming.InitialContext; import javax.naming.NamingException; import org.hawkular.alerts.actions.api.ActionPluginListener; import org.hawkular.alerts.actions.api.ActionPluginSender; import org.hawkular.alerts.actions.api.Plugin; import org.hawkular.alerts.actions.api.Sender; import org.hawkular.alerts.api.services.ActionsService; import org.jboss.vfs.VirtualFile; /** * Helper class to find the classes annotated with ActionPlugin and instantiate them. * * @author Lucas Ponce */ public class ActionPlugins { private final MsgLogger msgLog = MsgLogger.LOGGER; private ActionsService actions; private static ActionPlugins instance; private Map<String, ActionPluginListener> plugins; private Map<String, ActionPluginSender> senders; public static synchronized Map<String, ActionPluginListener> getPlugins() { if (instance == null) { instance = new ActionPlugins(); } return Collections.unmodifiableMap(instance.plugins); } public static synchronized Map<String, ActionPluginSender> getSenders() { if (instance == null) { instance = new ActionPlugins(); } return Collections.unmodifiableMap(instance.senders); } private ActionPlugins() { try { plugins = new HashMap<>(); senders = new HashMap<>(); init(); List<URL> webInfUrls = getWebInfUrls(); for (URL webInfUrl : webInfUrls) { List<Class> pluginClasses = findAnnotationInJar(webInfUrl, Plugin.class); for (Class pluginClass : pluginClasses) { Annotation actionPlugin = pluginClass.getDeclaredAnnotation(Plugin.class); if (actionPlugin instanceof Plugin) { String name = ((Plugin) actionPlugin).name(); Object newInstance = pluginClass.newInstance(); if (newInstance instanceof ActionPluginListener) { ActionPluginListener pluginInstance = (ActionPluginListener)newInstance; injectActionPluginSender(name, pluginInstance); plugins.put(name, pluginInstance); } else { throw new IllegalStateException("Plugin [" + name + "] is not instance of " + "ActionPluginListener"); } } } } } catch (Exception e) { throw new IllegalStateException(e); } } private List<URL> getWebInfUrls() throws Exception { Enumeration<URL> allUrls = Thread.currentThread().getContextClassLoader().getResources(""); List<URL> webInfUrls = new ArrayList<>(); while (allUrls != null && allUrls.hasMoreElements()) { URL url = allUrls.nextElement(); String sUrl = url.toExternalForm(); int indexPrefix = sUrl.indexOf("hawkular-alerts-actions-"); if (indexPrefix > 0) { int indexSuffix = sUrl.indexOf("-plugin", indexPrefix); if (indexSuffix > 0) { msgLog.debugf("Scanning %s", url); webInfUrls.add(url); } } } return webInfUrls; } private List<Class> findAnnotationInJar(URL url, Class annotation) throws Exception { if (url == null || annotation == null) { throw new IllegalArgumentException("url or annotation must be not null"); } List<Class> plugins = new ArrayList<>(); URLConnection conn = url.openConnection(); Object content = conn.getContent(); if (content instanceof VirtualFile) { VirtualFile root = (VirtualFile)content; List<VirtualFile> children = root.getChildrenRecursively(); for (VirtualFile vf : children) { String vfName = vf.toURI().toString(); msgLog.debugf("Searching %s", vfName); if (vfName.endsWith(".class")) { int startName = vfName.indexOf(".jar/") + 5; int stopName = vfName.indexOf(".class"); String className = vfName.substring(startName, stopName).replace("/", "."); msgLog.debugf("Loading %s", className); Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className); if (clazz.isAnnotationPresent(annotation)) { plugins.add(clazz); } } } } return plugins; } /* Search and inject ActionPluginSender inside ActionPluginListener */ private void injectActionPluginSender(String actionPlugin, ActionPluginListener pluginInstance) throws Exception { if (pluginInstance == null) { throw new IllegalArgumentException("pluginInstance must be not null"); } Field[] fields = pluginInstance.getClass().getDeclaredFields(); Field sender = null; for (Field field : fields) { if (field.isAnnotationPresent(Sender.class) && field.getType().isAssignableFrom(ActionPluginSender.class)) { sender = field; break; } } if (sender != null) { ActionPluginSender standaloneSender = new StandaloneActionPluginSender(actions); sender.setAccessible(true); sender.set(pluginInstance, standaloneSender); senders.put(actionPlugin, standaloneSender); } } private void init() { if (actions == null) { try { InitialContext ctx = new InitialContext(); actions = (ActionsService)ctx.lookup(ServiceNames.getServiceName(ACTIONS_SERVICE)); } catch (NamingException e) { msgLog.error("Cannot access to JNDI context", e); } } } }