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