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