001 /*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019 package org.crsh.plugin;
020
021 import org.crsh.util.Utils;
022 import org.crsh.vfs.FS;
023 import org.crsh.vfs.Resource;
024
025 import java.io.InputStream;
026 import java.util.*;
027 import java.util.concurrent.ExecutorService;
028 import java.util.concurrent.Executors;
029 import java.util.concurrent.ScheduledExecutorService;
030 import java.util.concurrent.ScheduledFuture;
031 import java.util.concurrent.ScheduledThreadPoolExecutor;
032 import java.util.concurrent.TimeUnit;
033 import java.util.logging.Level;
034 import java.util.logging.Logger;
035
036 public final class PluginContext {
037
038 /** . */
039 private static final Logger log = Logger.getLogger(PluginContext.class.getName());
040
041 /** . */
042 final PluginManager manager;
043
044 /** . */
045 private final ClassLoader loader;
046
047 /** . */
048 private final String version;
049
050 /** . */
051 private final ScheduledExecutorService scanner;
052
053 /** . */
054 private final Map<String, Object> attributes;
055
056 /** The shared executor. */
057 private final ExecutorService executor;
058
059 /** . */
060 private boolean started;
061
062 /** . */
063 private ScheduledFuture scannerFuture;
064
065 /** . */
066 private final ResourceManager resourceManager;
067
068 /** . */
069 private final PropertyManager propertyManager;
070
071 /**
072 * Create a new plugin context with preconfigured executor and scanner, this is equivalent to invoking:
073 *
074 * <code><pre>new PluginContext(
075 * Executors.newFixedThreadPool(20),
076 * new ScheduledThreadPoolExecutor(1),
077 * discovery,
078 * attributes,
079 * cmdFS,
080 * confFS,
081 * loader);</pre></code>
082 *
083 * @param discovery the plugin discovery
084 * @param cmdFS the command file system
085 * @param attributes the attributes
086 * @param confFS the conf file system
087 * @param loader the loader
088 * @throws NullPointerException if any parameter argument is null
089 */
090 public PluginContext(
091 PluginDiscovery discovery,
092 Map<String, Object> attributes,
093 FS cmdFS,
094 FS confFS,
095 ClassLoader loader) throws NullPointerException {
096 this(
097 Executors.newFixedThreadPool(20),
098 new ScheduledThreadPoolExecutor(1),
099 discovery,
100 attributes,
101 cmdFS,
102 confFS,
103 loader);
104 }
105
106 /**
107 * Create a new plugin context.
108 *
109 * @param executor the executor for executing asynchronous jobs
110 * @param scanner the background scanner for scanning commands
111 * @param discovery the plugin discovery
112 * @param cmdFS the command file system
113 * @param attributes the attributes
114 * @param confFS the conf file system
115 * @param loader the loader
116 * @throws NullPointerException if any parameter argument is null
117 */
118 public PluginContext(
119 ExecutorService executor,
120 ScheduledExecutorService scanner,
121 PluginDiscovery discovery,
122 Map<String, Object> attributes,
123 FS cmdFS,
124 FS confFS,
125 ClassLoader loader) throws NullPointerException {
126 if (executor == null) {
127 throw new NullPointerException("No null executor accepted");
128 }
129 if (scanner == null) {
130 throw new NullPointerException("No null scanner accepted");
131 }
132 if (discovery == null) {
133 throw new NullPointerException("No null plugin discovery accepted");
134 }
135 if (confFS == null) {
136 throw new NullPointerException("No null configuration file system accepted");
137 }
138 if (cmdFS == null) {
139 throw new NullPointerException("No null command file system accepted");
140 }
141 if (loader == null) {
142 throw new NullPointerException("No null loader accepted");
143 }
144 if (attributes == null) {
145 throw new NullPointerException("No null attributes accepted");
146 }
147
148 //
149 String version = null;
150 try {
151 Properties props = new Properties();
152 InputStream in = getClass().getClassLoader().getResourceAsStream("META-INF/maven/org.crashub/crash.shell/pom.properties");
153 if (in != null) {
154 props.load(in);
155 version = props.getProperty("version");
156 }
157 } catch (Exception e) {
158 log.log(Level.SEVERE, "Could not load maven properties", e);
159 }
160
161 //
162 if (version == null) {
163 log.log(Level.WARNING, "No version found will use unknown value instead");
164 version = "unknown";
165 }
166
167 //
168 this.loader = loader;
169 this.attributes = attributes;
170 this.version = version;
171 this.started = false;
172 this.manager = new PluginManager(this, discovery);
173 this.executor = executor;
174 this.scanner = scanner;
175 this.resourceManager = new ResourceManager(cmdFS, confFS);
176 this.propertyManager = new PropertyManager();
177 }
178
179 public String getVersion() {
180 return version;
181 }
182
183 public Map<String, Object> getAttributes() {
184 return attributes;
185 }
186
187 public ExecutorService getExecutor() {
188 return executor;
189 }
190
191 /**
192 * @return the property manager
193 */
194 public PropertyManager getPropertyManager() {
195 return propertyManager;
196 }
197
198 /**
199 * Returns a context property or null if it cannot be found.
200 *
201 * @param desc the property descriptor
202 * @param <T> the property parameter type
203 * @return the property value
204 * @throws NullPointerException if the descriptor argument is null
205 */
206 public <T> T getProperty(PropertyDescriptor<T> desc) throws NullPointerException {
207 return propertyManager.resolvePropertyValue(desc);
208 }
209
210 /**
211 * Returns a context property or null if it cannot be found.
212 *
213 * @param propertyName the name of the property
214 * @param type the property type
215 * @param <T> the property parameter type
216 * @return the property value
217 * @throws NullPointerException if the descriptor argument is null
218 */
219 public <T> T getProperty(String propertyName, Class<T> type) throws NullPointerException {
220 return propertyManager.resolvePropertyValue(propertyName, type);
221 }
222
223 /**
224 * Set a context property to a new value. If the provided value is null, then the property is removed.
225 *
226 * @param desc the property descriptor
227 * @param value the property value
228 * @param <T> the property parameter type
229 * @throws NullPointerException if the descriptor argument is null
230 */
231 public <T> void setProperty(PropertyDescriptor<T> desc, T value) throws NullPointerException {
232 propertyManager.setProperty(desc, value);
233 }
234
235 /**
236 * Set a context property to a new value. If the provided value is null, then the property is removed.
237 *
238 * @param desc the property descriptor
239 * @param value the property value
240 * @param <T> the property parameter type
241 * @throws NullPointerException if the descriptor argument is null
242 * @throws IllegalArgumentException if the string value cannot be converted to the property type
243 */
244 public <T> void setProperty(PropertyDescriptor<T> desc, String value) throws NullPointerException, IllegalArgumentException {
245 propertyManager.parseProperty(desc, value);
246 }
247
248 /**
249 * Load a resource from the context.
250 *
251 * @param resourceId the resource id
252 * @param resourceKind the resource kind
253 * @return the resource or null if it cannot be found
254 */
255 public Resource loadResource(String resourceId, ResourceKind resourceKind) {
256 return Utils.first(resourceManager.loadResource(resourceId, resourceKind));
257 }
258
259 /**
260 * Load a resource from the context.
261 *
262 * @param resourceId the resource id
263 * @param resourceKind the resource kind
264 * @return the resource or null if it cannot be found
265 */
266 public Iterable<Resource> loadResources(String resourceId, ResourceKind resourceKind) {
267 return resourceManager.loadResource(resourceId, resourceKind);
268 }
269
270 /**
271 * List the resources id for a specific resource kind.
272 *
273 * @param kind the resource kind
274 * @return the resource ids
275 */
276 public Iterable<String> listResources(ResourceKind kind) {
277 return resourceManager.listResourceId(kind);
278 }
279
280 /**
281 * Returns the classloader associated with this context.
282 *
283 * @return the class loader
284 */
285 public ClassLoader getLoader() {
286 return loader;
287 }
288
289 public Iterable<CRaSHPlugin<?>> getPlugins() {
290 return manager.getPlugins();
291 }
292
293 /**
294 * Returns the plugins associated with this context.
295 *
296 * @param pluginType the plugin type
297 * @param <T> the plugin generic type
298 * @return the plugins
299 */
300 public <T> Iterable<T> getPlugins(Class<T> pluginType) {
301 return manager.getPlugins(pluginType);
302 }
303
304 /**
305 * Returns the first plugin associated with this context implementing the specified type.
306 *
307 * @param pluginType the plugin type
308 * @param <T> the plugin generic type
309 * @return the plugins
310 */
311 public <T> T getPlugin(Class<T> pluginType) {
312 Iterator<T> plugins = manager.getPlugins(pluginType).iterator();
313 return plugins.hasNext() ? plugins.next() : null;
314 }
315
316 /**
317 * Refresh the fs system view. This is normally triggered by the periodic job but it can be manually
318 * invoked to trigger explicit refreshes.
319 */
320 public void refresh() {
321 resourceManager.refresh();
322 }
323
324 synchronized void start() {
325 if (!started) {
326
327 // Start refresh
328 Integer refreshRate = getProperty(PropertyDescriptor.VFS_REFRESH_PERIOD);
329 TimeUnit timeUnit = getProperty(PropertyDescriptor.VFS_REFRESH_UNIT);
330 if (refreshRate != null && refreshRate > 0) {
331 TimeUnit tu = timeUnit != null ? timeUnit : TimeUnit.SECONDS;
332 scannerFuture = scanner.scheduleWithFixedDelay(new Runnable() {
333 public void run() {
334 refresh();
335 }
336 }, 0, refreshRate, tu);
337 }
338
339 // Init plugins
340 manager.getPlugins(Object.class);
341
342 //
343 started = true;
344 } else {
345 log.log(Level.WARNING, "Attempt to double start");
346 }
347 }
348
349 synchronized void stop() {
350
351 //
352 if (started) {
353
354 // Shutdown manager
355 manager.shutdown();
356
357 // Shutdown scanner
358 if (scannerFuture != null) {
359 scannerFuture.cancel(true);
360 }
361
362 //
363 scanner.shutdownNow();
364
365 // Shutdown executor
366 executor.shutdownNow();
367 } else {
368 log.log(Level.WARNING, "Attempt to stop when stopped");
369 }
370 }
371 }