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