001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kew.plugin;
017
018import org.apache.log4j.Logger;
019import org.kuali.rice.core.api.config.property.Config;
020import org.kuali.rice.core.api.config.property.ConfigContext;
021import org.kuali.rice.core.api.util.ClassLoaderUtils;
022import org.kuali.rice.kew.plugin.PluginUtils.PluginZipFileFilter;
023
024import java.io.File;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Set;
028
029
030/**
031 * Checks for plugins added to or removed from the configured plugin directories.
032 *
033 * @author Kuali Rice Team (rice.collab@kuali.org)
034 */
035public class HotDeployer implements Runnable {
036        private static final Logger LOG = Logger.getLogger(HotDeployer.class);
037
038
039        private PluginRegistry registry;
040        private File sharedPluginDirectory;
041        private List<String> pluginDirectories;
042
043        public HotDeployer(PluginRegistry registry, File sharedPluginDirectory, List<String> pluginDirectories) {
044                this.registry = registry;
045                this.sharedPluginDirectory = sharedPluginDirectory;
046                this.pluginDirectories = pluginDirectories;
047        }
048
049        public synchronized void run() {
050                try {
051                        LOG.debug("Checking for added and removed plugins...");
052                        Set<PluginEnvironment> removedPlugins = getRemovedPlugins();
053                        for (PluginEnvironment pluginContext : removedPlugins) {
054                                LOG.info("Detected a removed plugin '" + pluginContext.getPluginName() + "', shutting down plugin.");
055                                try {
056                                        pluginContext.unload();
057                                        registry.removePluginEnvironment(pluginContext.getPluginName());
058                                } catch (Exception e) {
059                                        LOG.error("Failed to unload plugin '" + pluginContext.getPluginName() + "'", e);
060                                }
061                        }
062                        Set<PluginEnvironment> addedPlugins = getAddedPlugins();
063                        for (PluginEnvironment pluginContext : addedPlugins) {
064                                try {
065                                        LOG.info("Detected a new plugin.  Loading plugin...");
066                                        pluginContext.load();
067                                        LOG.info("...plugin '" + pluginContext.getPluginName() + "' loaded.");                                  
068                                } catch (Exception e) {
069                                    // see: KULRICE-1184
070                                    String pluginName= "<<unknown>>";
071                                    if (pluginContext.getPlugin() != null) {
072                                        pluginName = String.valueOf(pluginContext.getPluginName());
073                                    }
074                                        LOG.error("Failed to load plugin '" + pluginName + "'", e);
075                                } finally {
076                                    // whether or not there is a load error, we want to add the environment to the registry, this prevents the registry
077                                    // continuously trying to hot deploy a bad plugin
078                                    registry.addPluginEnvironment(pluginContext);
079                                }
080                        }
081                } catch (Exception e) {
082                        LOG.warn("Failed to check for hot deploy.", e);
083                }
084        }
085
086        protected Set<PluginEnvironment> getRemovedPlugins() {
087                Set<PluginEnvironment> removedPlugins = new HashSet<PluginEnvironment>();
088                for (PluginEnvironment environment : registry.getPluginEnvironments()) {
089                        if (environment.getLoader().isRemoved()) {
090                                removedPlugins.add(environment);
091                        }
092                }
093                return removedPlugins;
094        }
095
096        protected Set<PluginEnvironment> getAddedPlugins() throws Exception {
097                Set<PluginEnvironment> addedPlugins = new HashSet<PluginEnvironment>();
098                Set<File> newPluginZipFiles = new HashSet<File>();
099                // for now, this implementation should watch the plugin directories for more plugins
100                // TODO somehow the code which checks for new plugins and which loads plugins initially needs to be
101                // consolidated, maybe with some sort of set of PluginLocators? or something along those lines?
102                for (String pluginDirName : pluginDirectories) {
103                        File pluginDir = new File(pluginDirName);
104                        if (pluginDir.exists() && pluginDir.isDirectory()) {
105                                File[] pluginDirFiles = pluginDir.listFiles(new PluginZipFileFilter());
106                                for (File pluginZip : pluginDirFiles) {
107                                        int indexOf = pluginZip.getName().lastIndexOf(".zip");
108                                        String pluginName = pluginZip.getName().substring(0, indexOf);
109                                        // check to see if this plugin environment has already been loaded
110                                        List<PluginEnvironment> currentEnvironments = registry.getPluginEnvironments();
111                                        boolean pluginExists = false;
112                                        for (PluginEnvironment environment : currentEnvironments) {
113                                                if (environment.getPluginName().equals(pluginName)) {
114                                                        pluginExists = true;
115                                                        break;
116                                                }
117                                        }
118                                        if (!pluginExists) {
119                                                // make sure the plugin's not in the process of being copied
120                                                long lastModified1 = pluginZip.lastModified();
121                                                Thread.sleep(100);
122                                                long lastModified2 = pluginZip.lastModified();
123                                                if (lastModified1 == lastModified2) {
124                                                        newPluginZipFiles.add(pluginZip);
125                                                } else {
126                                                        LOG.warn("Detected that the plugin zip is still being modified, holding off on hot deploy: " + pluginZip.getAbsolutePath());
127                                                }
128                                        }
129                                }
130                        }
131                }
132
133                ClassLoader parentClassLoader = ClassLoaderUtils.getDefaultClassLoader();
134                Config parentConfig = ConfigContext.getCurrentContextConfig();
135                for (File newPluginZipFile : newPluginZipFiles) {
136                        PluginLoader loader = new ZipFilePluginLoader(newPluginZipFile, sharedPluginDirectory, parentClassLoader, parentConfig);
137                        PluginEnvironment environment = new PluginEnvironment(loader, registry);
138                        addedPlugins.add(environment);
139                }
140                return addedPlugins;
141        }
142
143}