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 * Returns a context property or null if it cannot be found.
192 *
193 * @param desc the property descriptor
194 * @param <T> the property parameter type
195 * @return the property value
196 * @throws NullPointerException if the descriptor argument is null
197 */
198 public <T> T getProperty(PropertyDescriptor<T> desc) throws NullPointerException {
199 return propertyManager.getProperty(desc);
200 }
201
202 /**
203 * Returns a context property or null if it cannot be found.
204 *
205 * @param propertyName the name of the property
206 * @param type the property type
207 * @param <T> the property parameter type
208 * @return the property value
209 * @throws NullPointerException if the descriptor argument is null
210 */
211 public <T> T getProperty(String propertyName, Class<T> type) throws NullPointerException {
212 return propertyManager.getProperty(propertyName, type);
213 }
214
215 /**
216 * Set a context property to a new value. If the provided value is null, then the property is removed.
217 *
218 * @param desc the property descriptor
219 * @param value the property value
220 * @param <T> the property parameter type
221 * @throws NullPointerException if the descriptor argument is null
222 */
223 public <T> void setProperty(PropertyDescriptor<T> desc, T value) throws NullPointerException {
224 propertyManager.setProperty(desc, value);
225 }
226
227 /**
228 * Set a context property to a new value. If the provided value is null, then the property is removed.
229 *
230 * @param desc the property descriptor
231 * @param value the property value
232 * @param <T> the property parameter type
233 * @throws NullPointerException if the descriptor argument is null
234 * @throws IllegalArgumentException if the string value cannot be converted to the property type
235 */
236 public <T> void setProperty(PropertyDescriptor<T> desc, String value) throws NullPointerException, IllegalArgumentException {
237 propertyManager.setProperty(desc, value);
238 }
239
240 /**
241 * Load a resource from the context.
242 *
243 * @param resourceId the resource id
244 * @param resourceKind the resource kind
245 * @return the resource or null if it cannot be found
246 */
247 public Resource loadResource(String resourceId, ResourceKind resourceKind) {
248 return resourceManager.loadResource(resourceId, resourceKind);
249 }
250
251 /**
252 * List the resources id for a specific resource kind.
253 *
254 * @param kind the resource kind
255 * @return the resource ids
256 */
257 public List<String> listResourceId(ResourceKind kind) {
258 return resourceManager.listResourceId(kind);
259 }
260
261 /**
262 * Returns the classloader associated with this context.
263 *
264 * @return the class loader
265 */
266 public ClassLoader getLoader() {
267 return loader;
268 }
269
270 public Iterable<CRaSHPlugin<?>> getPlugins() {
271 return manager.getPlugins();
272 }
273
274 /**
275 * Returns the plugins associated with this context.
276 *
277 * @param pluginType the plugin type
278 * @param <T> the plugin generic type
279 * @return the plugins
280 */
281 public <T> Iterable<T> getPlugins(Class<T> pluginType) {
282 return manager.getPlugins(pluginType);
283 }
284
285 /**
286 * Returns the first plugin associated with this context implementing the specified type.
287 *
288 * @param pluginType the plugin type
289 * @param <T> the plugin generic type
290 * @return the plugins
291 */
292 public <T> T getPlugin(Class<T> pluginType) {
293 Iterator<T> plugins = manager.getPlugins(pluginType).iterator();
294 return plugins.hasNext() ? plugins.next() : null;
295 }
296
297 /**
298 * Refresh the fs system view. This is normally triggered by the periodic job but it can be manually
299 * invoked to trigger explicit refreshes.
300 */
301 public void refresh() {
302 resourceManager.refresh();
303 }
304
305 synchronized void start() {
306 if (!started) {
307
308 // Start refresh
309 Integer refreshRate = getProperty(PropertyDescriptor.VFS_REFRESH_PERIOD);
310 TimeUnit timeUnit = getProperty(PropertyDescriptor.VFS_REFRESH_UNIT);
311 if (refreshRate != null && refreshRate > 0) {
312 TimeUnit tu = timeUnit != null ? timeUnit : TimeUnit.SECONDS;
313 scannerFuture = scanner.scheduleWithFixedDelay(new Runnable() {
314 public void run() {
315 refresh();
316 }
317 }, 0, refreshRate, tu);
318 }
319
320 // Init plugins
321 manager.getPlugins(Object.class);
322
323 //
324 started = true;
325 } else {
326 log.log(Level.WARNING, "Attempt to double start");
327 }
328 }
329
330 synchronized void stop() {
331
332 //
333 if (started) {
334
335 // Shutdown manager
336 manager.shutdown();
337
338 // Shutdown scanner
339 if (scannerFuture != null) {
340 scannerFuture.cancel(true);
341 }
342
343 //
344 scanner.shutdownNow();
345
346 // Shutdown executor
347 executor.shutdownNow();
348 } else {
349 log.log(Level.WARNING, "Attempt to stop when stopped");
350 }
351 }
352 }