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