View Javadoc

1   /**
2    * Copyright 2005-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  package org.kuali.rice.kew.plugin;
17  
18  import org.kuali.rice.core.api.config.CoreConfigHelper;
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.resourceloader.ResourceLoader;
22  import org.kuali.rice.core.api.util.ClassLoaderUtils;
23  import org.kuali.rice.kew.plugin.PluginUtils.PluginZipFileFilter;
24  
25  import javax.xml.namespace.QName;
26  import java.io.File;
27  import java.util.ArrayList;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.TreeMap;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.ScheduledExecutorService;
35  import java.util.concurrent.ScheduledFuture;
36  import java.util.concurrent.ThreadFactory;
37  import java.util.concurrent.TimeUnit;
38  
39  /**
40   * A PluginRegistry implementation which loads plugins from the file system on the server.
41   *
42   * @author Kuali Rice Team (rice.collab@kuali.org)
43   */
44  public class ServerPluginRegistry extends BasePluginRegistry {
45  
46  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ServerPluginRegistry.class);
47  
48  	private List<String> pluginDirectories = new ArrayList<String>();
49  	private File sharedPluginDirectory;
50  	private Reloader reloader;
51  	private HotDeployer hotDeployer;
52  
53  	private ScheduledExecutorService scheduledExecutor;
54  	private ScheduledFuture<?> reloaderFuture;
55  	private ScheduledFuture<?> hotDeployerFuture;
56  
57  
58  	public ServerPluginRegistry() {
59  		super(new QName(CoreConfigHelper.getApplicationId(), ResourceLoader.PLUGIN_REGISTRY_LOADER_NAME));
60  	}
61  
62  	public void start() throws Exception {
63  		LOG.info("Starting server Plugin Registry...");
64  		scheduledExecutor = Executors.newScheduledThreadPool(2, new KEWThreadFactory());
65  		sharedPluginDirectory = loadSharedPlugin();
66  		reloader = new Reloader();
67  		hotDeployer = new HotDeployer(PluginUtils.getPluginRegistry(), sharedPluginDirectory, pluginDirectories);
68  		loadPlugins(sharedPluginDirectory);
69  		// TODO make the delay configurable
70  		this.reloaderFuture = scheduledExecutor.scheduleWithFixedDelay(reloader, 5, 5, TimeUnit.SECONDS);
71  		this.hotDeployerFuture = scheduledExecutor.scheduleWithFixedDelay(hotDeployer, 5, 5, TimeUnit.SECONDS);
72  		super.start();
73  		LOG.info("...server Plugin Registry successfully started.");
74  	}
75  
76  	public void stop() throws Exception {
77  		LOG.info("Stopping server Plugin Registry...");
78  		stopReloader();
79  		stopHotDeployer();
80  		reloader = null;
81  		hotDeployer = null;
82  
83  		if (scheduledExecutor != null) {
84  			scheduledExecutor.shutdownNow();
85  			scheduledExecutor = null;
86  		}
87  		super.stop();
88  		LOG.info("...server Plugin Registry successfully stopped.");
89  	}
90  
91  	protected void stopReloader() {
92  		if (reloaderFuture != null) {
93  			if (!reloaderFuture.cancel(true)) {
94  				LOG.warn("Failed to cancel the plugin reloader.");
95  			}
96  			reloaderFuture = null;
97  		}
98  	}
99  
100 	protected void stopHotDeployer() {
101 		if (hotDeployerFuture != null) {
102 			if (!hotDeployerFuture.cancel(true)) {
103 				LOG.warn("Failed to cancel the hot deployer.");
104 			}
105 			hotDeployerFuture = null;
106 		}
107 	}
108 
109 	protected void loadPlugins(File sharedPluginDirectory) {
110         Map<String, File> pluginLocations = new TreeMap<String, File>(new PluginNameComparator());
111 		PluginZipFileFilter pluginFilter = new PluginZipFileFilter();
112         //PluginDirectoryFilter pluginFilter = new PluginDirectoryFilter(sharedPluginDirectory);
113         Set<File> visitedFiles = new HashSet<File>();
114         for (String pluginDir : pluginDirectories) {
115             LOG.info("Reading plugins from " + pluginDir);
116             File file = new File(pluginDir);
117             if (visitedFiles.contains(file)) {
118                 LOG.info("Skipping visited directory: " + pluginDir);
119                 continue;
120             }
121             visitedFiles.add(file);
122             if (!file.exists() || !file.isDirectory()) {
123                 LOG.warn(file.getAbsoluteFile()+" is not a valid plugin directory.");
124                 continue;
125             }
126             File[] pluginZips = file.listFiles(pluginFilter);
127             for (int i = 0; i < pluginZips.length; i++) {
128                 File pluginZip = pluginZips[i];
129                 int indexOf = pluginZip.getName().lastIndexOf(".zip");
130                 String pluginName = pluginZip.getName().substring(0, indexOf);
131                 if (pluginLocations.containsKey(pluginName)) {
132                 	LOG.warn("There already exists an installed plugin with the name '"+ pluginName + "', ignoring plugin " + pluginZip.getAbsolutePath());
133                 	continue;
134                 }
135                 pluginLocations.put(pluginName, pluginZip);
136             }
137         }
138         for (String pluginName : pluginLocations.keySet()) {
139         	File pluginZipFile = pluginLocations.get(pluginName);
140         	try {
141         		LOG.info("Loading plugin '" + pluginName + "'");
142         		ClassLoader parentClassLoader = ClassLoaderUtils.getDefaultClassLoader();
143         		Config parentConfig = ConfigContext.getCurrentContextConfig();
144         		ZipFilePluginLoader loader = new ZipFilePluginLoader(pluginZipFile,
145         				sharedPluginDirectory,
146         				parentClassLoader,
147         				parentConfig);
148         		PluginEnvironment environment = new PluginEnvironment(loader, this);
149         		try {
150         		    environment.load();
151         		} finally {
152         		    // regardless of whether the plugin loads or not, let's add it to the environment
153         		    addPluginEnvironment(environment);
154         		}        		
155         	} catch (Exception e) {
156         		LOG.error("Failed to read workflow plugin '"+pluginName+"'", e);
157         	}
158         }
159     }
160 
161 	@Override
162 	public void addPluginEnvironment(PluginEnvironment pluginEnvironment) {
163 		super.addPluginEnvironment(pluginEnvironment);
164 		reloader.addReloadable(pluginEnvironment);
165 	}
166 
167 	@Override
168 	public PluginEnvironment removePluginEnvironment(String pluginName) {
169 		PluginEnvironment environment = super.removePluginEnvironment(pluginName);
170 		reloader.removeReloadable(environment);
171 		return environment;
172 	}
173 
174 	public File loadSharedPlugin() {
175 		return PluginUtils.findSharedDirectory(pluginDirectories);
176 	}
177 
178 	public void setPluginDirectories(List<String> pluginDirectories) {
179 		this.pluginDirectories = pluginDirectories;
180 	}
181 
182 	public void setSharedPluginDirectory(File sharedPluginDirectory) {
183 		this.sharedPluginDirectory = sharedPluginDirectory;
184 	}
185 
186 	protected HotDeployer getHotDeployer() {
187 		return hotDeployer;
188 	}
189 
190 	protected Reloader getReloader() {
191 		return reloader;
192 	}
193 	
194 	private static class KEWThreadFactory implements ThreadFactory {
195 		
196 		private ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();
197 		
198 		public Thread newThread(Runnable runnable) {
199 			Thread thread = defaultThreadFactory.newThread(runnable);
200 			thread.setName("ServerPluginRegistry-" + thread.getName());
201 			return thread;
202 	    }
203 	}
204 
205 }