View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.plugin;
17  
18  import org.apache.log4j.Logger;
19  import org.kuali.rice.core.api.config.property.Config;
20  import org.kuali.rice.core.api.config.property.ConfigContext;
21  import org.kuali.rice.core.api.util.ClassLoaderUtils;
22  import org.kuali.rice.kew.plugin.PluginUtils.PluginZipFileFilter;
23  
24  import java.io.File;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  
30  /**
31   * Checks for plugins added to or removed from the configured plugin directories.
32   *
33   * @author Kuali Rice Team (rice.collab@kuali.org)
34   */
35  public class HotDeployer implements Runnable {
36  	private static final Logger LOG = Logger.getLogger(HotDeployer.class);
37  
38  
39  	private PluginRegistry registry;
40  	private File sharedPluginDirectory;
41  	private List<String> pluginDirectories;
42  
43  	public HotDeployer(PluginRegistry registry, File sharedPluginDirectory, List<String> pluginDirectories) {
44  		this.registry = registry;
45  		this.sharedPluginDirectory = sharedPluginDirectory;
46  		this.pluginDirectories = pluginDirectories;
47  	}
48  
49  	public synchronized void run() {
50  		try {
51  			LOG.debug("Checking for added and removed plugins...");
52  			Set<PluginEnvironment> removedPlugins = getRemovedPlugins();
53  			for (PluginEnvironment pluginContext : removedPlugins) {
54  				LOG.info("Detected a removed plugin '" + pluginContext.getPluginName() + "', shutting down plugin.");
55  				try {
56  					pluginContext.unload();
57  					registry.removePluginEnvironment(pluginContext.getPluginName());
58  				} catch (Exception e) {
59  					LOG.error("Failed to unload plugin '" + pluginContext.getPluginName() + "'", e);
60  				}
61  			}
62  			Set<PluginEnvironment> addedPlugins = getAddedPlugins();
63  			for (PluginEnvironment pluginContext : addedPlugins) {
64  				try {
65  					LOG.info("Detected a new plugin.  Loading plugin...");
66  					pluginContext.load();
67  					LOG.info("...plugin '" + pluginContext.getPluginName() + "' loaded.");					
68  				} catch (Exception e) {
69  				    // see: KULRICE-1184
70  				    String pluginName= "<<unknown>>";
71  				    if (pluginContext.getPlugin() != null) {
72  				        pluginName = String.valueOf(pluginContext.getPluginName());
73  				    }
74  					LOG.error("Failed to load plugin '" + pluginName + "'", e);
75  				} finally {
76  				    // whether or not there is a load error, we want to add the environment to the registry, this prevents the registry
77  				    // continuously trying to hot deploy a bad plugin
78  				    registry.addPluginEnvironment(pluginContext);
79  				}
80  			}
81  		} catch (Exception e) {
82  			LOG.warn("Failed to check for hot deploy.", e);
83  		}
84  	}
85  
86  	protected Set<PluginEnvironment> getRemovedPlugins() {
87  		Set<PluginEnvironment> removedPlugins = new HashSet<PluginEnvironment>();
88  		for (PluginEnvironment environment : registry.getPluginEnvironments()) {
89  			if (environment.getLoader().isRemoved()) {
90  				removedPlugins.add(environment);
91  			}
92  		}
93  		return removedPlugins;
94  	}
95  
96  	protected Set<PluginEnvironment> getAddedPlugins() throws Exception {
97  		Set<PluginEnvironment> addedPlugins = new HashSet<PluginEnvironment>();
98  		Set<File> newPluginZipFiles = new HashSet<File>();
99  		// 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 }