View Javadoc

1   /*
2    * Copyright 2006-2011 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  
17  package org.kuali.rice.kew.plugin;
18  
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.core.api.config.property.Config;
21  import org.kuali.rice.core.api.config.property.ConfigContext;
22  import org.kuali.rice.core.api.util.ClassLoaderUtils;
23  import org.kuali.rice.kew.plugin.PluginUtils.PluginZipFileFilter;
24  
25  import java.io.File;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  
30  
31  /**
32   * Checks for plugins added to or removed from the configured plugin directories.
33   *
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   */
36  public class HotDeployer implements Runnable {
37  	private static final Logger LOG = Logger.getLogger(HotDeployer.class);
38  
39  
40  	private PluginRegistry registry;
41  	private File sharedPluginDirectory;
42  	private List<String> pluginDirectories;
43  
44  	public HotDeployer(PluginRegistry registry, File sharedPluginDirectory, List<String> pluginDirectories) {
45  		this.registry = registry;
46  		this.sharedPluginDirectory = sharedPluginDirectory;
47  		this.pluginDirectories = pluginDirectories;
48  	}
49  
50  	public synchronized void run() {
51  		try {
52  			LOG.debug("Checking for added and removed plugins...");
53  			Set<PluginEnvironment> removedPlugins = getRemovedPlugins();
54  			for (PluginEnvironment pluginContext : removedPlugins) {
55  				LOG.info("Detected a removed plugin '" + pluginContext.getPluginName() + "', shutting down plugin.");
56  				try {
57  					pluginContext.unload();
58  					registry.removePluginEnvironment(pluginContext.getPluginName());
59  				} catch (Exception e) {
60  					LOG.error("Failed to unload plugin '" + pluginContext.getPluginName() + "'", e);
61  				}
62  			}
63  			Set<PluginEnvironment> addedPlugins = getAddedPlugins();
64  			for (PluginEnvironment pluginContext : addedPlugins) {
65  				try {
66  					LOG.info("Detected a new plugin.  Loading plugin...");
67  					pluginContext.load();
68  					LOG.info("...plugin '" + pluginContext.getPluginName() + "' loaded.");					
69  				} catch (Exception e) {
70  				    // see: KULRICE-1184
71  				    String pluginName= "<<unknown>>";
72  				    if (pluginContext.getPlugin() != null) {
73  				        pluginName = String.valueOf(pluginContext.getPluginName());
74  				    }
75  					LOG.error("Failed to load plugin '" + pluginName + "'", e);
76  				} finally {
77  				    // whether or not there is a load error, we want to add the environment to the registry, this prevents the registry
78  				    // continuously trying to hot deploy a bad plugin
79  				    registry.addPluginEnvironment(pluginContext);
80  				}
81  			}
82  		} catch (Exception e) {
83  			LOG.warn("Failed to check for hot deploy.", e);
84  		}
85  	}
86  
87  	protected Set<PluginEnvironment> getRemovedPlugins() {
88  		Set<PluginEnvironment> removedPlugins = new HashSet<PluginEnvironment>();
89  		for (PluginEnvironment environment : registry.getPluginEnvironments()) {
90  			if (environment.getLoader().isRemoved()) {
91  				removedPlugins.add(environment);
92  			}
93  		}
94  		return removedPlugins;
95  	}
96  
97  	protected Set<PluginEnvironment> getAddedPlugins() throws Exception {
98  		Set<PluginEnvironment> addedPlugins = new HashSet<PluginEnvironment>();
99  		Set<File> newPluginZipFiles = new HashSet<File>();
100 		// for now, this implementation should watch the plugin directories for more plugins
101 		// TODO somehow the code which checks for new plugins and which loads plugins initially needs to be
102 		// consolidated, maybe with some sort of set of PluginLocators? or something along those lines?
103 		for (String pluginDirName : pluginDirectories) {
104 			File pluginDir = new File(pluginDirName);
105 			if (pluginDir.exists() && pluginDir.isDirectory()) {
106 				File[] pluginDirFiles = pluginDir.listFiles(new PluginZipFileFilter());
107 				for (File pluginZip : pluginDirFiles) {
108 					int indexOf = pluginZip.getName().lastIndexOf(".zip");
109 					String pluginName = pluginZip.getName().substring(0, indexOf);
110 					// check to see if this plugin environment has already been loaded
111 					List<PluginEnvironment> currentEnvironments = registry.getPluginEnvironments();
112 					boolean pluginExists = false;
113 					for (PluginEnvironment environment : currentEnvironments) {
114 						if (environment.getPluginName().equals(pluginName)) {
115 							pluginExists = true;
116 							break;
117 						}
118 					}
119 					if (!pluginExists) {
120 						// make sure the plugin's not in the process of being copied
121 						long lastModified1 = pluginZip.lastModified();
122 						Thread.sleep(100);
123 						long lastModified2 = pluginZip.lastModified();
124 						if (lastModified1 == lastModified2) {
125 							newPluginZipFiles.add(pluginZip);
126 						} else {
127 							LOG.warn("Detected that the plugin zip is still being modified, holding off on hot deploy: " + pluginZip.getAbsolutePath());
128 						}
129 					}
130 				}
131 			}
132 		}
133 
134 		ClassLoader parentClassLoader = ClassLoaderUtils.getDefaultClassLoader();
135 		Config parentConfig = ConfigContext.getCurrentContextConfig();
136 		for (File newPluginZipFile : newPluginZipFiles) {
137 			PluginLoader loader = new ZipFilePluginLoader(newPluginZipFile, sharedPluginDirectory, parentClassLoader, parentConfig);
138 			PluginEnvironment environment = new PluginEnvironment(loader, registry);
139 			addedPlugins.add(environment);
140 		}
141 		return addedPlugins;
142 	}
143 
144 }