001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden.plugin;
018:
019: import java.io.File;
020: import java.util.HashSet;
021: import java.util.List;
022: import java.util.Set;
023:
024: import org.apache.log4j.Logger;
025: import org.kuali.rice.config.Config;
026: import org.kuali.rice.core.Core;
027:
028: import edu.iu.uis.eden.plugin.PluginUtils.PluginZipFileFilter;
029: import edu.iu.uis.eden.util.ClassLoaderUtils;
030:
031: /**
032: * Checks for plugins added to or removed from the configured plugin directories.
033: *
034: * @author ewestfal
035: */
036: public class HotDeployer implements Runnable {
037: private static final Logger LOG = Logger
038: .getLogger(HotDeployer.class);
039:
040: private PluginRegistry registry;
041: private File sharedPluginDirectory;
042: private List<String> pluginDirectories;
043:
044: public HotDeployer(PluginRegistry registry,
045: File sharedPluginDirectory, List<String> pluginDirectories) {
046: this .registry = registry;
047: this .sharedPluginDirectory = sharedPluginDirectory;
048: this .pluginDirectories = pluginDirectories;
049: }
050:
051: public synchronized void run() {
052: try {
053: LOG.debug("Checking for added and removed plugins...");
054: Set<PluginEnvironment> removedPlugins = getRemovedPlugins();
055: for (PluginEnvironment pluginContext : removedPlugins) {
056: LOG.info("Detected a removed plugin '"
057: + pluginContext.getPlugin().getName()
058: + "', shutting down plugin.");
059: try {
060: pluginContext.unload();
061: registry.removePluginEnvironment(pluginContext
062: .getPlugin().getName());
063: } catch (Exception e) {
064: LOG.error(
065: "Failed to unload plugin '"
066: + pluginContext.getPlugin()
067: .getName() + "'", e);
068: }
069: }
070: Set<PluginEnvironment> addedPlugins = getAddedPlugins();
071: for (PluginEnvironment pluginContext : addedPlugins) {
072: try {
073: LOG
074: .info("Detected a new plugin. Loading plugin...");
075: pluginContext.load();
076: LOG.info("...plugin '"
077: + pluginContext.getPlugin().getName()
078: + "' loaded.");
079: registry.addPluginEnvironment(pluginContext);
080: } catch (Exception e) {
081: LOG
082: .warn("Failed to load plugin '"
083: + pluginContext.getPlugin()
084: .getName() + "'");
085: }
086: }
087: } catch (Exception e) {
088: LOG.warn("Failed to check for hot deploy.", e);
089: }
090: //
091: //
092: // for (Iterator iterator = addedPluginDirs.iterator(); iterator.hasNext();) {
093: // File pluginDir = (File) iterator.next();
094: // LOG.info("Detected a new plugin. Waiting for plugin in '" + pluginDir + "' to be ready...");
095: // if (PluginUtils.waitUntilPluginIsReady(pluginDir)) {
096: // LOG.info("Adding new plugin in '" + pluginDir + "'");
097: // Plugin plugin = new Plugin(pluginDir, sharedPluginDirectory);
098: // plugin.setPluginRegistry(registry);
099: // plugin.setParentClassLoader(registry.getInstitutionPlugin().getClassLoader());
100: // registry.addPlugin(plugin);
101: // } else {
102: // LOG.warn("It appears plugin in '" + pluginDir + "' is being modified. Waiting until next poll interval.");
103: // }
104: // }
105: }
106:
107: protected Set<PluginEnvironment> getRemovedPlugins() {
108: Set<PluginEnvironment> removedPlugins = new HashSet<PluginEnvironment>();
109: for (PluginEnvironment environment : registry
110: .getPluginEnvironments()) {
111: if (environment.getLoader().isRemoved()) {
112: removedPlugins.add(environment);
113: }
114: }
115: // for (Iterator iterator = registry.getPlugins().iterator(); iterator.hasNext();) {
116: // Plugin plugin = (Plugin) iterator.next();
117: // if (!plugin.getPluginDirectory().exists() || !plugin.getPluginDirectory().isDirectory()) {
118: // removedPlugins.add(plugin);
119: // }
120: // }
121: return removedPlugins;
122: }
123:
124: protected Set<PluginEnvironment> getAddedPlugins() throws Exception {
125: Set<PluginEnvironment> addedPlugins = new HashSet<PluginEnvironment>();
126: Set<File> newPluginZipFiles = new HashSet<File>();
127: // for now, this implementation should watch the plugin directories for more plugins
128: // TODO somehow the code which checks for new plugins and which loads plugins initially needs to be
129: // consolidated, maybe with some sort of set of PluginLocators? or something along those lines?
130: for (String pluginDirName : pluginDirectories) {
131: File pluginDir = new File(pluginDirName);
132: if (pluginDir.exists() && pluginDir.isDirectory()) {
133: File[] pluginDirFiles = pluginDir
134: .listFiles(new PluginZipFileFilter());
135: for (File pluginZip : pluginDirFiles) {
136: int indexOf = pluginZip.getName().lastIndexOf(
137: ".zip");
138: String pluginName = pluginZip.getName().substring(
139: 0, indexOf);
140: if (PluginUtils.isInstitutionalPlugin(pluginName)) {
141: continue;
142: }
143: // check to see if this plugin has already been loaded
144: List<PluginEnvironment> currentEnvironments = registry
145: .getPluginEnvironments();
146: boolean pluginExists = false;
147: for (PluginEnvironment environment : currentEnvironments) {
148: if (environment.getPlugin().getName()
149: .getLocalPart().equals(pluginName)) {
150: pluginExists = true;
151: break;
152: }
153: }
154: if (!pluginExists) {
155: // make sure the plugin's not in the process of being copied
156: long lastModified1 = pluginZip.lastModified();
157: Thread.sleep(100);
158: long lastModified2 = pluginZip.lastModified();
159: if (lastModified1 == lastModified2) {
160: newPluginZipFiles.add(pluginZip);
161: } else {
162: LOG
163: .warn("Detected that the plugin zip is still being modified, holding off on hot deploy: "
164: + pluginZip
165: .getAbsolutePath());
166: }
167: }
168: }
169: }
170: }
171:
172: // TODO this currently couldn't handle an institutional plugin being "added", should it be able to?!?
173: ClassLoader parentClassLoader = ClassLoaderUtils
174: .getDefaultClassLoader();
175: Config parentConfig = Core.getCurrentContextConfig();
176: Plugin institutionalPlugin = registry.getInstitutionalPlugin();
177: if (institutionalPlugin != null) {
178: parentClassLoader = institutionalPlugin.getClassLoader();
179: parentConfig = institutionalPlugin.getConfig();
180: }
181: for (File newPluginZipFile : newPluginZipFiles) {
182: PluginLoader loader = new ZipFilePluginLoader(
183: newPluginZipFile, sharedPluginDirectory,
184: parentClassLoader, parentConfig, false);
185: PluginEnvironment environment = new PluginEnvironment(
186: loader, registry);
187: addedPlugins.add(environment);
188: }
189: return addedPlugins;
190: }
191:
192: }
|