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 */ 019package org.crsh.plugin; 020 021import org.crsh.util.Utils; 022import org.crsh.vfs.FS; 023import org.crsh.vfs.Resource; 024 025import java.io.InputStream; 026import java.util.*; 027import java.util.concurrent.ExecutorService; 028import java.util.concurrent.Executors; 029import java.util.concurrent.ScheduledExecutorService; 030import java.util.concurrent.ScheduledFuture; 031import java.util.concurrent.ScheduledThreadPoolExecutor; 032import java.util.concurrent.TimeUnit; 033import java.util.logging.Level; 034import java.util.logging.Logger; 035 036public 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}