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 }