1 // 2 // Copyright (c) ZeroC, Inc. All rights reserved. 3 // 4 5 package com.zeroc.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<>(); 28 try 29 { 30 for(PluginInfo p : _plugins) 31 { 32 try 33 { 34 p.plugin.initialize(); 35 } 36 catch(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<>(); 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 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 PluginManagerI(Communicator communicator, com.zeroc.IceInternal.Instance instance)166 public PluginManagerI(Communicator communicator, com.zeroc.IceInternal.Instance instance) 167 { 168 _communicator = communicator; 169 _instance = instance; 170 _initialized = false; 171 } 172 loadPlugins(String[] cmdArgs)173 public String[] loadPlugins(String[] cmdArgs) 174 { 175 assert(_communicator != null); 176 177 // 178 // Load and initialize the plug-ins defined in the property set 179 // with the prefix "Ice.Plugin.". These properties should 180 // have the following format: 181 // 182 // Ice.Plugin.name[.<language>]=entry_point [args] 183 // 184 // If the Ice.PluginLoadOrder property is defined, load the 185 // specified plug-ins in the specified order, then load any 186 // remaining plug-ins. 187 // 188 final String prefix = "Ice.Plugin."; 189 Properties properties = _communicator.getProperties(); 190 java.util.Map<String, String> plugins = properties.getPropertiesForPrefix(prefix); 191 192 final String[] loadOrder = properties.getPropertyAsList("Ice.PluginLoadOrder"); 193 for(String name : loadOrder) 194 { 195 if(findPlugin(name) != null) 196 { 197 PluginInitializationException ex = new PluginInitializationException(); 198 ex.reason = "plug-in `" + name + "' already loaded"; 199 throw ex; 200 } 201 202 String key = "Ice.Plugin." + name + ".java"; 203 boolean hasKey = plugins.containsKey(key); 204 if(hasKey) 205 { 206 plugins.remove("Ice.Plugin." + name); 207 } 208 else 209 { 210 key = "Ice.Plugin." + name; 211 hasKey = plugins.containsKey(key); 212 } 213 214 if(hasKey) 215 { 216 final String value = plugins.get(key); 217 cmdArgs = loadPlugin(name, value, cmdArgs); 218 plugins.remove(key); 219 } 220 else 221 { 222 PluginInitializationException ex = new PluginInitializationException(); 223 ex.reason = "plug-in `" + name + "' not defined"; 224 throw ex; 225 } 226 } 227 228 // 229 // Load any remaining plug-ins that weren't specified in PluginLoadOrder. 230 // 231 while(!plugins.isEmpty()) 232 { 233 java.util.Iterator<java.util.Map.Entry<String, String> > p = plugins.entrySet().iterator(); 234 java.util.Map.Entry<String, String> entry = p.next(); 235 236 String name = entry.getKey().substring(prefix.length()); 237 238 int dotPos = name.lastIndexOf('.'); 239 if(dotPos != -1) 240 { 241 String suffix = name.substring(dotPos + 1); 242 if(suffix.equals("cpp") || suffix.equals("clr")) 243 { 244 // 245 // Ignored 246 // 247 p.remove(); 248 } 249 else if(suffix.equals("java")) 250 { 251 name = name.substring(0, dotPos); 252 cmdArgs = loadPlugin(name, entry.getValue(), cmdArgs); 253 p.remove(); 254 255 // 256 // Don't want to load this one if it's there! 257 // 258 plugins.remove("Ice.Plugin." + name); 259 } 260 else 261 { 262 // 263 // Name is just a regular name that happens to contain a dot 264 // 265 dotPos = -1; 266 } 267 } 268 269 if(dotPos == -1) 270 { 271 // 272 // Is there a .java entry? 273 // 274 String value = entry.getValue(); 275 p.remove(); 276 277 String javaValue = plugins.remove("Ice.Plugin." + name + ".java"); 278 if(javaValue != null) 279 { 280 value = javaValue; 281 } 282 283 cmdArgs = loadPlugin(name, value, cmdArgs); 284 } 285 } 286 287 return cmdArgs; 288 } 289 loadPlugin(String name, String pluginSpec, String[] cmdArgs)290 private String[] loadPlugin(String name, String pluginSpec, String[] cmdArgs) 291 { 292 assert(_communicator != null); 293 294 // 295 // We support the following formats: 296 // 297 // <class-name> [args] 298 // <jar-file>:<class-name> [args] 299 // <class-dir>:<class-name> [args] 300 // "<path with spaces>":<class-name> [args] 301 // "<path with spaces>:<class-name>" [args] 302 // 303 304 String[] args; 305 try 306 { 307 args = com.zeroc.IceUtilInternal.Options.split(pluginSpec); 308 } 309 catch(com.zeroc.IceUtilInternal.Options.BadQuote ex) 310 { 311 throw new PluginInitializationException("invalid arguments for plug-in `" + name + "':\n" + 312 ex.getMessage()); 313 } 314 315 assert(args.length > 0); 316 317 final String entryPoint = args[0]; 318 319 final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); 320 boolean absolutePath = false; 321 322 // 323 // Find first ':' that isn't part of the file path. 324 // 325 int pos = entryPoint.indexOf(':'); 326 if(isWindows) 327 { 328 final String driveLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 329 if(pos == 1 && entryPoint.length() > 2 && driveLetters.indexOf(entryPoint.charAt(0)) != -1 && 330 (entryPoint.charAt(2) == '\\' || entryPoint.charAt(2) == '/')) 331 { 332 absolutePath = true; 333 pos = entryPoint.indexOf(':', pos + 1); 334 } 335 if(!absolutePath) 336 { 337 absolutePath = entryPoint.startsWith("\\\\"); 338 } 339 } 340 else 341 { 342 absolutePath = entryPoint.startsWith("/"); 343 } 344 345 if((pos == -1 && absolutePath) || (pos != -1 && entryPoint.length() <= pos + 1)) 346 { 347 // 348 // Class name is missing. 349 // 350 throw new PluginInitializationException("invalid entry point for plug-in `" + name + "':\n" + entryPoint); 351 } 352 353 // 354 // Extract the JAR file or subdirectory, if any. 355 // 356 String classDir = null; // Path name of JAR file or subdirectory. 357 String className; 358 359 if(pos == -1) 360 { 361 className = entryPoint; 362 } 363 else 364 { 365 classDir = entryPoint.substring(0, pos).trim(); 366 className = entryPoint.substring(pos + 1).trim(); 367 } 368 369 // 370 // Shift the arguments. 371 // 372 String[] tmp = new String[args.length - 1]; 373 System.arraycopy(args, 1, tmp, 0, args.length - 1); 374 args = tmp; 375 376 // 377 // Convert command-line options into properties. First we 378 // convert the options from the plug-in configuration, then 379 // we convert the options from the application command-line. 380 // 381 Properties properties = _communicator.getProperties(); 382 args = properties.parseCommandLineOptions(name, args); 383 cmdArgs = properties.parseCommandLineOptions(name, cmdArgs); 384 385 // 386 // Instantiate the class. 387 // 388 PluginFactory pluginFactory = null; 389 try 390 { 391 Class<?> c = null; 392 393 // 394 // Use a class loader if the user specified a JAR file or class directory. 395 // 396 if(classDir != null) 397 { 398 try 399 { 400 if(!absolutePath) 401 { 402 classDir = new java.io.File(System.getProperty("user.dir") + java.io.File.separator + 403 classDir).getCanonicalPath(); 404 } 405 406 if(!classDir.endsWith(java.io.File.separator) && !classDir.toLowerCase().endsWith(".jar")) 407 { 408 classDir += java.io.File.separator; 409 } 410 classDir = URLEncoder.encode(classDir, "UTF-8"); 411 412 // 413 // Reuse an existing class loader if we have already loaded a plug-in with 414 // the same value for classDir, otherwise create a new one. 415 // 416 ClassLoader cl = null; 417 418 if(_classLoaders == null) 419 { 420 _classLoaders = new java.util.HashMap<>(); 421 } 422 else 423 { 424 cl = _classLoaders.get(classDir); 425 } 426 427 if(cl == null) 428 { 429 final java.net.URL[] url = new java.net.URL[] { new java.net.URL("file", null, classDir) }; 430 431 // 432 // Use the custom class loader (if any) as the parent. 433 // 434 if(_instance.initializationData().classLoader != null) 435 { 436 cl = new java.net.URLClassLoader(url, _instance.initializationData().classLoader); 437 } 438 else 439 { 440 cl = new java.net.URLClassLoader(url); 441 } 442 443 _classLoaders.put(classDir, cl); 444 } 445 446 c = cl.loadClass(className); 447 } 448 catch(java.net.MalformedURLException ex) 449 { 450 throw new PluginInitializationException("invalid entry point format `" + pluginSpec + "'", ex); 451 } 452 catch(java.io.IOException ex) 453 { 454 throw new PluginInitializationException("invalid path in entry point `" + pluginSpec + "'", ex); 455 } 456 catch(java.lang.ClassNotFoundException ex) 457 { 458 // Ignored 459 } 460 } 461 else 462 { 463 c = com.zeroc.IceInternal.Util.getInstance(_communicator).findClass(className); 464 } 465 466 if(c == null) 467 { 468 throw new PluginInitializationException("class " + className + " not found"); 469 } 470 471 java.lang.Object obj = c.getDeclaredConstructor().newInstance(); 472 try 473 { 474 pluginFactory = (PluginFactory)obj; 475 } 476 catch(ClassCastException ex) 477 { 478 throw new PluginInitializationException("class " + className + " does not implement PluginFactory", 479 ex); 480 } 481 } 482 catch(NoSuchMethodException ex) 483 { 484 throw new PluginInitializationException("unable to instantiate class " + className, ex); 485 } 486 catch(java.lang.reflect.InvocationTargetException ex) 487 { 488 throw new PluginInitializationException("unable to instantiate class " + className, ex); 489 } 490 catch(IllegalAccessException ex) 491 { 492 throw new PluginInitializationException("unable to access default constructor in class " + className, ex); 493 } 494 catch(InstantiationException ex) 495 { 496 throw new PluginInitializationException("unable to instantiate class " + className, ex); 497 } 498 499 // 500 // Invoke the factory. 501 // 502 Plugin plugin = null; 503 try 504 { 505 plugin = pluginFactory.create(_communicator, name, args); 506 } 507 catch(PluginInitializationException ex) 508 { 509 throw ex; 510 } 511 catch(Throwable ex) 512 { 513 throw new PluginInitializationException("exception in factory " + className, ex); 514 } 515 516 if(plugin == null) 517 { 518 throw new PluginInitializationException("failure in factory " + className); 519 } 520 521 PluginInfo info = new PluginInfo(); 522 info.name = name; 523 info.plugin = plugin; 524 _plugins.add(info); 525 526 return cmdArgs; 527 } 528 findPlugin(String name)529 private Plugin 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 com.zeroc.IceInternal.Instance _instance; 549 private java.util.List<PluginInfo> _plugins = new java.util.ArrayList<>(); 550 private boolean _initialized; 551 private java.util.Map<String, ClassLoader> _classLoaders; 552 } 553