1 // 2 // Copyright (c) ZeroC, Inc. All rights reserved. 3 // 4 5 package Ice; 6 7 import java.net.URLEncoder; 8 9 public final class PluginManagerI implements PluginManager 10 { 11 private static String _kindOfObject = "plugin"; 12 13 @Override 14 public synchronized void initializePlugins()15 initializePlugins() 16 { 17 if(_initialized) 18 { 19 InitializationException ex = new InitializationException(); 20 ex.reason = "plug-ins already initialized"; 21 throw ex; 22 } 23 24 // 25 // Invoke initialize() on the plug-ins, in the order they were loaded. 26 // 27 java.util.List<Plugin> initializedPlugins = new java.util.ArrayList<Plugin>(); 28 try 29 { 30 for(PluginInfo p : _plugins) 31 { 32 try 33 { 34 p.plugin.initialize(); 35 } 36 catch(Ice.PluginInitializationException ex) 37 { 38 throw ex; 39 } 40 catch(RuntimeException ex) 41 { 42 PluginInitializationException e = new PluginInitializationException(); 43 e.reason = "plugin `" + p.name + "' initialization failed"; 44 e.initCause(ex); 45 throw e; 46 } 47 initializedPlugins.add(p.plugin); 48 } 49 } 50 catch(RuntimeException ex) 51 { 52 // 53 // Destroy the plug-ins that have been successfully initialized, in the 54 // reverse order. 55 // 56 java.util.ListIterator<Plugin> i = initializedPlugins.listIterator(initializedPlugins.size()); 57 while(i.hasPrevious()) 58 { 59 Plugin p = i.previous(); 60 try 61 { 62 p.destroy(); 63 } 64 catch(RuntimeException e) 65 { 66 // Ignore. 67 } 68 } 69 throw ex; 70 } 71 72 _initialized = true; 73 } 74 75 @Override 76 public synchronized String[] getPlugins()77 getPlugins() 78 { 79 java.util.ArrayList<String> names = new java.util.ArrayList<String>(); 80 for(PluginInfo p : _plugins) 81 { 82 names.add(p.name); 83 } 84 return names.toArray(new String[0]); 85 } 86 87 @Override 88 public synchronized Plugin getPlugin(String name)89 getPlugin(String name) 90 { 91 if(_communicator == null) 92 { 93 throw new CommunicatorDestroyedException(); 94 } 95 96 Plugin p = findPlugin(name); 97 if(p != null) 98 { 99 return p; 100 } 101 102 NotRegisteredException ex = new NotRegisteredException(); 103 ex.id = name; 104 ex.kindOfObject = _kindOfObject; 105 throw ex; 106 } 107 108 @Override 109 public synchronized void addPlugin(String name, Plugin plugin)110 addPlugin(String name, Plugin plugin) 111 { 112 if(_communicator == null) 113 { 114 throw new CommunicatorDestroyedException(); 115 } 116 117 if(findPlugin(name) != null) 118 { 119 AlreadyRegisteredException ex = new AlreadyRegisteredException(); 120 ex.id = name; 121 ex.kindOfObject = _kindOfObject; 122 throw ex; 123 } 124 125 PluginInfo info = new PluginInfo(); 126 info.name = name; 127 info.plugin = plugin; 128 _plugins.add(info); 129 } 130 131 @Override 132 public synchronized void destroy()133 destroy() 134 { 135 if(_communicator != null) 136 { 137 if(_initialized) 138 { 139 java.util.ListIterator<PluginInfo> i = _plugins.listIterator(_plugins.size()); 140 while(i.hasPrevious()) 141 { 142 PluginInfo p = i.previous(); 143 try 144 { 145 p.plugin.destroy(); 146 } 147 catch(RuntimeException ex) 148 { 149 Ice.Util.getProcessLogger().warning("unexpected exception raised by plug-in `" + 150 p.name + "' destruction:\n" + ex.toString()); 151 } 152 } 153 } 154 155 _communicator = null; 156 } 157 158 _plugins.clear(); 159 160 if(_classLoaders != null) 161 { 162 _classLoaders.clear(); 163 } 164 } 165 166 public PluginManagerI(Communicator communicator, IceInternal.Instance instance)167 PluginManagerI(Communicator communicator, IceInternal.Instance instance) 168 { 169 _communicator = communicator; 170 _instance = instance; 171 _initialized = false; 172 } 173 174 public void loadPlugins(StringSeqHolder cmdArgs)175 loadPlugins(StringSeqHolder cmdArgs) 176 { 177 assert(_communicator != null); 178 179 // 180 // Load and initialize the plug-ins defined in the property set 181 // with the prefix "Ice.Plugin.". These properties should 182 // have the following format: 183 // 184 // Ice.Plugin.name[.<language>]=entry_point [args] 185 // 186 // If the Ice.PluginLoadOrder property is defined, load the 187 // specified plug-ins in the specified order, then load any 188 // remaining plug-ins. 189 // 190 final String prefix = "Ice.Plugin."; 191 Properties properties = _communicator.getProperties(); 192 java.util.Map<String, String> plugins = properties.getPropertiesForPrefix(prefix); 193 194 final String[] loadOrder = properties.getPropertyAsList("Ice.PluginLoadOrder"); 195 for(String name : loadOrder) 196 { 197 if(findPlugin(name) != null) 198 { 199 PluginInitializationException ex = new PluginInitializationException(); 200 ex.reason = "plug-in `" + name + "' already loaded"; 201 throw ex; 202 } 203 204 String key = "Ice.Plugin." + name + ".java"; 205 boolean hasKey = plugins.containsKey(key); 206 if(hasKey) 207 { 208 plugins.remove("Ice.Plugin." + name); 209 } 210 else 211 { 212 key = "Ice.Plugin." + name; 213 hasKey = plugins.containsKey(key); 214 } 215 216 if(hasKey) 217 { 218 final String value = plugins.get(key); 219 loadPlugin(name, value, cmdArgs); 220 plugins.remove(key); 221 } 222 else 223 { 224 PluginInitializationException ex = new PluginInitializationException(); 225 ex.reason = "plug-in `" + name + "' not defined"; 226 throw ex; 227 } 228 } 229 230 // 231 // Load any remaining plug-ins that weren't specified in PluginLoadOrder. 232 // 233 while(!plugins.isEmpty()) 234 { 235 java.util.Iterator<java.util.Map.Entry<String, String> > p = plugins.entrySet().iterator(); 236 java.util.Map.Entry<String, String> entry = p.next(); 237 238 String name = entry.getKey().substring(prefix.length()); 239 240 int dotPos = name.lastIndexOf('.'); 241 if(dotPos != -1) 242 { 243 String suffix = name.substring(dotPos + 1); 244 if(suffix.equals("cpp") || suffix.equals("clr")) 245 { 246 // 247 // Ignored 248 // 249 p.remove(); 250 } 251 else if(suffix.equals("java")) 252 { 253 name = name.substring(0, dotPos); 254 loadPlugin(name, entry.getValue(), cmdArgs); 255 p.remove(); 256 257 // 258 // Don't want to load this one if it's there! 259 // 260 plugins.remove("Ice.Plugin." + name); 261 } 262 else 263 { 264 // 265 // Name is just a regular name that happens to contain a dot 266 // 267 dotPos = -1; 268 } 269 } 270 271 if(dotPos == -1) 272 { 273 // 274 // Is there a .java entry? 275 // 276 String value = entry.getValue(); 277 p.remove(); 278 279 String javaValue = plugins.remove("Ice.Plugin." + name + ".java"); 280 if(javaValue != null) 281 { 282 value = javaValue; 283 } 284 285 loadPlugin(name, value, cmdArgs); 286 } 287 } 288 } 289 290 private void loadPlugin(String name, String pluginSpec, StringSeqHolder cmdArgs)291 loadPlugin(String name, String pluginSpec, StringSeqHolder cmdArgs) 292 { 293 assert(_communicator != null); 294 295 // 296 // We support the following formats: 297 // 298 // <class-name> [args] 299 // <jar-file>:<class-name> [args] 300 // <class-dir>:<class-name> [args] 301 // "<path with spaces>":<class-name> [args] 302 // "<path with spaces>:<class-name>" [args] 303 // 304 305 String[] args; 306 try 307 { 308 args = IceUtilInternal.Options.split(pluginSpec); 309 } 310 catch(IceUtilInternal.Options.BadQuote ex) 311 { 312 throw new PluginInitializationException("invalid arguments for plug-in `" + name + "':\n" + 313 ex.getMessage()); 314 } 315 316 assert(args.length > 0); 317 318 final String entryPoint = args[0]; 319 320 final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); 321 boolean absolutePath = false; 322 323 // 324 // Find first ':' that isn't part of the file path. 325 // 326 int pos = entryPoint.indexOf(':'); 327 if(isWindows) 328 { 329 final String driveLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 330 if(pos == 1 && entryPoint.length() > 2 && driveLetters.indexOf(entryPoint.charAt(0)) != -1 && 331 (entryPoint.charAt(2) == '\\' || entryPoint.charAt(2) == '/')) 332 { 333 absolutePath = true; 334 pos = entryPoint.indexOf(':', pos + 1); 335 } 336 if(!absolutePath) 337 { 338 absolutePath = entryPoint.startsWith("\\\\"); 339 } 340 } 341 else 342 { 343 absolutePath = entryPoint.startsWith("/"); 344 } 345 346 if((pos == -1 && absolutePath) || (pos != -1 && entryPoint.length() <= pos + 1)) 347 { 348 // 349 // Class name is missing. 350 // 351 throw new PluginInitializationException("invalid entry point for plug-in `" + name + "':\n" + entryPoint); 352 } 353 354 // 355 // Extract the JAR file or subdirectory, if any. 356 // 357 String classDir = null; // Path name of JAR file or subdirectory. 358 String className; 359 360 if(pos == -1) 361 { 362 className = entryPoint; 363 } 364 else 365 { 366 classDir = entryPoint.substring(0, pos).trim(); 367 className = entryPoint.substring(pos + 1).trim(); 368 } 369 370 // 371 // Shift the arguments. 372 // 373 String[] tmp = new String[args.length - 1]; 374 System.arraycopy(args, 1, tmp, 0, args.length - 1); 375 args = tmp; 376 377 // 378 // Convert command-line options into properties. First we 379 // convert the options from the plug-in configuration, then 380 // we convert the options from the application command-line. 381 // 382 Properties properties = _communicator.getProperties(); 383 args = properties.parseCommandLineOptions(name, args); 384 cmdArgs.value = properties.parseCommandLineOptions(name, cmdArgs.value); 385 386 // 387 // Instantiate the class. 388 // 389 PluginFactory pluginFactory = null; 390 try 391 { 392 Class<?> c = null; 393 394 // 395 // Use a class loader if the user specified a JAR file or class directory. 396 // 397 if(classDir != null) 398 { 399 try 400 { 401 if(!absolutePath) 402 { 403 classDir = new java.io.File(System.getProperty("user.dir") + java.io.File.separator + 404 classDir).getCanonicalPath(); 405 } 406 407 if(!classDir.endsWith(java.io.File.separator) && !classDir.toLowerCase().endsWith(".jar")) 408 { 409 classDir += java.io.File.separator; 410 } 411 classDir = URLEncoder.encode(classDir, "UTF-8"); 412 413 // 414 // Reuse an existing class loader if we have already loaded a plug-in with 415 // the same value for classDir, otherwise create a new one. 416 // 417 ClassLoader cl = null; 418 419 if(_classLoaders == null) 420 { 421 _classLoaders = new java.util.HashMap<String, ClassLoader>(); 422 } 423 else 424 { 425 cl = _classLoaders.get(classDir); 426 } 427 428 if(cl == null) 429 { 430 final java.net.URL[] url = new java.net.URL[] { new java.net.URL("file", null, classDir) }; 431 432 // 433 // Use the custom class loader (if any) as the parent. 434 // 435 if(_instance.initializationData().classLoader != null) 436 { 437 cl = new java.net.URLClassLoader(url, _instance.initializationData().classLoader); 438 } 439 else 440 { 441 cl = new java.net.URLClassLoader(url); 442 } 443 444 _classLoaders.put(classDir, cl); 445 } 446 447 c = cl.loadClass(className); 448 } 449 catch(java.net.MalformedURLException ex) 450 { 451 throw new PluginInitializationException("invalid entry point format `" + pluginSpec + "'", ex); 452 } 453 catch(java.io.IOException ex) 454 { 455 throw new PluginInitializationException("invalid path in entry point `" + pluginSpec + "'", ex); 456 } 457 catch(java.lang.ClassNotFoundException ex) 458 { 459 // Ignored 460 } 461 } 462 else 463 { 464 c = IceInternal.Util.getInstance(_communicator).findClass(className); 465 } 466 467 if(c == null) 468 { 469 throw new PluginInitializationException("class " + className + " not found"); 470 } 471 472 java.lang.Object obj = c.getDeclaredConstructor().newInstance(); 473 try 474 { 475 pluginFactory = (PluginFactory)obj; 476 } 477 catch(ClassCastException ex) 478 { 479 throw new PluginInitializationException("class " + className + " does not implement Ice.PluginFactory", 480 ex); 481 } 482 } 483 catch(NoSuchMethodException ex) 484 { 485 throw new PluginInitializationException("unable to instantiate class " + className, ex); 486 } 487 catch(java.lang.reflect.InvocationTargetException ex) 488 { 489 throw new PluginInitializationException("unable to instantiate class " + className, ex); 490 } 491 catch(IllegalAccessException ex) 492 { 493 throw new PluginInitializationException("unable to access default constructor in class " + className, ex); 494 } 495 catch(InstantiationException ex) 496 { 497 throw new PluginInitializationException("unable to instantiate class " + className, ex); 498 } 499 500 // 501 // Invoke the factory. 502 // 503 Plugin plugin = null; 504 try 505 { 506 plugin = pluginFactory.create(_communicator, name, args); 507 } 508 catch(PluginInitializationException ex) 509 { 510 throw ex; 511 } 512 catch(Throwable ex) 513 { 514 throw new PluginInitializationException("exception in factory " + className, ex); 515 } 516 517 if(plugin == null) 518 { 519 throw new PluginInitializationException("failure in factory " + className); 520 } 521 522 PluginInfo info = new PluginInfo(); 523 info.name = name; 524 info.plugin = plugin; 525 _plugins.add(info); 526 } 527 528 private Plugin findPlugin(String name)529 findPlugin(String name) 530 { 531 for(PluginInfo p : _plugins) 532 { 533 if(name.equals(p.name)) 534 { 535 return p.plugin; 536 } 537 } 538 return null; 539 } 540 541 static class PluginInfo 542 { 543 String name; 544 Plugin plugin; 545 } 546 547 private Communicator _communicator; 548 private IceInternal.Instance _instance; 549 private java.util.List<PluginInfo> _plugins = new java.util.ArrayList<PluginInfo>(); 550 private boolean _initialized; 551 private java.util.Map<String, ClassLoader> _classLoaders; 552 } 553