1 package com
.intellij
.ide
.plugins
;
3 import com
.intellij
.ide
.ClassloaderUtil
;
4 import com
.intellij
.ide
.IdeBundle
;
5 import com
.intellij
.ide
.plugins
.cl
.PluginClassLoader
;
6 import com
.intellij
.idea
.Main
;
7 import com
.intellij
.openapi
.application
.ApplicationManager
;
8 import com
.intellij
.openapi
.application
.PathManager
;
9 import com
.intellij
.openapi
.application
.impl
.PluginsFacade
;
10 import com
.intellij
.openapi
.diagnostic
.Logger
;
11 import com
.intellij
.openapi
.extensions
.Extensions
;
12 import com
.intellij
.openapi
.extensions
.LogProvider
;
13 import com
.intellij
.openapi
.extensions
.PluginId
;
14 import com
.intellij
.openapi
.util
.Comparing
;
15 import com
.intellij
.openapi
.util
.Condition
;
16 import com
.intellij
.openapi
.util
.io
.FileUtil
;
17 import com
.intellij
.openapi
.util
.text
.StringUtil
;
18 import com
.intellij
.util
.ArrayUtil
;
19 import com
.intellij
.util
.Function
;
20 import com
.intellij
.util
.graph
.CachingSemiGraph
;
21 import com
.intellij
.util
.graph
.DFSTBuilder
;
22 import com
.intellij
.util
.graph
.Graph
;
23 import com
.intellij
.util
.graph
.GraphGenerator
;
24 import com
.intellij
.util
.lang
.UrlClassLoader
;
25 import com
.intellij
.util
.xmlb
.XmlSerializationException
;
26 import gnu
.trove
.THashMap
;
27 import org
.jetbrains
.annotations
.NonNls
;
28 import org
.jetbrains
.annotations
.Nullable
;
29 import sun
.reflect
.Reflection
;
33 import java
.lang
.reflect
.InvocationTargetException
;
34 import java
.lang
.reflect
.Method
;
35 import java
.net
.MalformedURLException
;
37 import java
.net
.URLClassLoader
;
38 import java
.net
.URLDecoder
;
44 @SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"}) // No logger is loaded at this time so we have to use these.
45 public class PluginManager
{
47 @NonNls public static final String AREA_IDEA_PROJECT
= "IDEA_PROJECT";
48 @NonNls public static final String AREA_IDEA_MODULE
= "IDEA_MODULE";
49 @NonNls private static final String PROPERTY_PLUGIN_PATH
= "plugin.path";
50 private static final Object PLUGIN_CLASSES_LOCK
= new Object();
51 private static String myPluginError
= null;
52 @NonNls public static final String CORE_PLUGIN_ID
= "com.intellij";
54 @NonNls public static final String DISABLED_PLUGINS_FILENAME
= "disabled_plugins.txt";
56 private static List
<String
> ourDisabledPlugins
= null;
58 static final Object lock
= new Object();
60 private static String ourBuildNumber
;
61 @NonNls private static final String PLUGIN_XML
= "plugin.xml";
62 @NonNls private static final String META_INF
= "META-INF";
63 private static final Map
<PluginId
,Integer
> ourId2Index
= new THashMap
<PluginId
, Integer
>();
65 public static class Facade
extends PluginsFacade
{
66 public IdeaPluginDescriptor
getPlugin(PluginId id
) {
67 return PluginManager
.getPlugin(id
);
70 public IdeaPluginDescriptor
[] getPlugins() {
71 return PluginManager
.getPlugins();
75 private static IdeaPluginDescriptorImpl
[] ourPlugins
;
76 private static Map
<String
, PluginId
> ourPluginClasses
;
79 * do not call this method during bootstrap, should be called in a copy of PluginManager, loaded by IdeaClassLoader
81 public synchronized static IdeaPluginDescriptor
[] getPlugins() {
82 if (ourPlugins
== null) {
84 ClassloaderUtil
.clearJarURLCache();
90 * Called via reflection
92 @SuppressWarnings({"UnusedDeclaration"})
93 protected static void start(final String mainClass
, final String methodName
, final String
[] args
) {
95 //noinspection HardCodedStringLiteral
96 ThreadGroup threadGroup
= new ThreadGroup("Idea Thread Group") {
97 public void uncaughtException(Thread t
, Throwable e
) {
102 Runnable runnable
= new Runnable() {
105 ClassloaderUtil
.clearJarURLCache();
107 //noinspection AssignmentToStaticFieldFromInstanceMethod
108 PluginsFacade
.INSTANCE
= new PluginManager
.Facade();
110 Class aClass
= Class
.forName(mainClass
);
111 final Method method
= aClass
.getDeclaredMethod(methodName
, (ArrayUtil
.EMPTY_STRING_ARRAY
).getClass());
112 method
.setAccessible(true);
114 //noinspection RedundantArrayCreation
115 method
.invoke(null, new Object
[]{args
});
117 catch (Exception e
) {
119 getLogger().error("Error while accessing " + mainClass
+ "." + methodName
+ " with arguments: " + Arrays
.asList(args
), e
);
124 //noinspection HardCodedStringLiteral
125 new Thread(threadGroup
, runnable
, "Idea Main Thread").start();
127 catch (Exception e
) {
128 getLogger().error(e
);
132 private static void initializePlugins() {
133 configureExtensions();
135 final IdeaPluginDescriptorImpl
[] pluginDescriptors
= loadDescriptors();
137 final Class callerClass
= Reflection
.getCallerClass(1);
138 final ClassLoader parentLoader
= callerClass
.getClassLoader();
140 final List
<IdeaPluginDescriptorImpl
> result
= new ArrayList
<IdeaPluginDescriptorImpl
>();
141 for (IdeaPluginDescriptorImpl descriptor
: pluginDescriptors
) {
142 if (!shouldSkipPlugin(descriptor
, pluginDescriptors
)) {
143 result
.add(descriptor
);
145 descriptor
.setEnabled(false);
146 final List
<File
> classPath
= descriptor
.getClassPath();
147 final ClassLoader loader
=
148 createPluginClassLoader(classPath
.toArray(new File
[classPath
.size()]), new ClassLoader
[]{parentLoader
}, descriptor
);
149 descriptor
.setLoader(loader
, false);
153 prepareLoadingPluginsErrorMessage(filterBadPlugins(result
));
155 final Map
<PluginId
, IdeaPluginDescriptorImpl
> idToDescriptorMap
= new HashMap
<PluginId
, IdeaPluginDescriptorImpl
>();
156 for (final IdeaPluginDescriptorImpl descriptor
: result
) {
157 idToDescriptorMap
.put(descriptor
.getPluginId(), descriptor
);
160 final IdeaPluginDescriptor corePluginDescriptor
= idToDescriptorMap
.get(PluginId
.getId(CORE_PLUGIN_ID
));
161 assert corePluginDescriptor
!= null;
162 for (IdeaPluginDescriptorImpl descriptor
: result
) {
163 if (descriptor
!= corePluginDescriptor
) {
164 descriptor
.insertDependency(corePluginDescriptor
);
168 mergeOptionalConfigs(idToDescriptorMap
);
170 // sort descriptors according to plugin dependencies
171 Collections
.sort(result
, getPluginDescriptorComparator(idToDescriptorMap
));
173 for (int i
= 0; i
< result
.size(); i
++) {
174 ourId2Index
.put(result
.get(i
).getPluginId(), i
);
177 for (final IdeaPluginDescriptorImpl pluginDescriptor
: result
) {
178 if (pluginDescriptor
.getPluginId().getIdString().equals(CORE_PLUGIN_ID
) || pluginDescriptor
.isUseCoreClassLoader()) {
179 pluginDescriptor
.setLoader(parentLoader
, true);
182 final List
<File
> classPath
= pluginDescriptor
.getClassPath();
183 final PluginId
[] dependentPluginIds
= pluginDescriptor
.getDependentPluginIds();
184 final ClassLoader
[] parentLoaders
= getParentLoaders(idToDescriptorMap
, dependentPluginIds
);
186 final ClassLoader pluginClassLoader
= createPluginClassLoader(classPath
.toArray(new File
[classPath
.size()]),
187 parentLoaders
.length
> 0 ? parentLoaders
: new ClassLoader
[] {parentLoader
},
189 pluginDescriptor
.setLoader(pluginClassLoader
, true);
192 pluginDescriptor
.registerExtensions();
195 ourPlugins
= pluginDescriptors
;
198 public static int getPluginLoadingOrder(PluginId id
) {
199 return ourId2Index
.get(id
);
202 private static void mergeOptionalConfigs(Map
<PluginId
, IdeaPluginDescriptorImpl
> descriptors
) {
203 for (IdeaPluginDescriptorImpl descriptor
: descriptors
.values()) {
204 final Map
<PluginId
, IdeaPluginDescriptorImpl
> optionalDescriptors
= descriptor
.getOptionalDescriptors();
205 if (optionalDescriptors
!= null && !optionalDescriptors
.isEmpty()) {
206 for (Map
.Entry
<PluginId
, IdeaPluginDescriptorImpl
> entry
: optionalDescriptors
.entrySet()) {
207 if (descriptors
.containsKey(entry
.getKey())) {
208 descriptor
.mergeOptionalConfig(entry
.getValue());
215 private static void prepareLoadingPluginsErrorMessage(final String errorMessage
) {
216 if (errorMessage
!= null) {
217 if (!Main
.isHeadless()) {
218 if (myPluginError
== null) {
219 myPluginError
= errorMessage
;
222 myPluginError
+= "\n" + errorMessage
;
225 getLogger().error(errorMessage
);
230 private static void configureExtensions() {
231 Extensions
.setLogProvider(new IdeaLogProvider());
232 Extensions
.registerAreaClass(AREA_IDEA_PROJECT
, null);
233 Extensions
.registerAreaClass(AREA_IDEA_MODULE
, AREA_IDEA_PROJECT
);
236 private static boolean shouldLoadPlugins() {
238 // no plugins during bootstrap
239 Class
.forName("com.intellij.openapi.extensions.Extensions");
241 catch (ClassNotFoundException e
) {
244 //noinspection HardCodedStringLiteral
245 final String loadPlugins
= System
.getProperty("idea.load.plugins");
246 return loadPlugins
== null || Boolean
.TRUE
.toString().equals(loadPlugins
);
249 public static boolean shouldSkipPlugin(final IdeaPluginDescriptor descriptor
) {
250 if (descriptor
instanceof IdeaPluginDescriptorImpl
) {
251 IdeaPluginDescriptorImpl descriptorImpl
= (IdeaPluginDescriptorImpl
)descriptor
;
252 Boolean skipped
= descriptorImpl
.getSkipped();
253 if (skipped
!= null) {
254 return skipped
.booleanValue();
256 boolean result
= shouldSkipPlugin(descriptor
, ourPlugins
);
257 descriptorImpl
.setSkipped(result
);
260 return shouldSkipPlugin(descriptor
, ourPlugins
);
263 private static boolean shouldSkipPlugin(final IdeaPluginDescriptor descriptor
, IdeaPluginDescriptor
[] loaded
) {
264 final String idString
= descriptor
.getPluginId().getIdString();
265 if (idString
.equals(CORE_PLUGIN_ID
)) {
269 //noinspection HardCodedStringLiteral
270 final String pluginId
= System
.getProperty("idea.load.plugins.id");
271 if (pluginId
== null) {
272 if (descriptor
instanceof IdeaPluginDescriptorImpl
&& !((IdeaPluginDescriptorImpl
)descriptor
).isEnabled()) return true;
274 if (!shouldLoadPlugins()) return true;
278 //noinspection HardCodedStringLiteral
279 final String loadPluginCategory
= System
.getProperty("idea.load.plugins.category");
280 if (loadPluginCategory
!= null) {
281 shouldLoad
= loadPluginCategory
.equals(descriptor
.getCategory());
284 if (pluginId
!= null) {
285 shouldLoad
= pluginId
.equals(idString
);
287 Map
<PluginId
,IdeaPluginDescriptor
> map
= new HashMap
<PluginId
, IdeaPluginDescriptor
>();
288 for (final IdeaPluginDescriptor pluginDescriptor
: loaded
) {
289 map
.put(pluginDescriptor
.getPluginId(), pluginDescriptor
);
291 final IdeaPluginDescriptor descriptorFromProperty
= map
.get(PluginId
.getId(pluginId
));
292 shouldLoad
= descriptorFromProperty
!= null && isDependent(descriptorFromProperty
, descriptor
.getPluginId(), map
);
295 shouldLoad
= !getDisabledPlugins().contains(idString
);
297 if (shouldLoad
&& descriptor
instanceof IdeaPluginDescriptorImpl
) {
298 if (isIncompatible(descriptor
)) return true;
306 private static boolean isDependent(final IdeaPluginDescriptor descriptor
, final PluginId on
, Map
<PluginId
, IdeaPluginDescriptor
> map
) {
307 for (PluginId id
: descriptor
.getDependentPluginIds()) {
311 final IdeaPluginDescriptor depDescriptor
= map
.get(id
);
312 if (depDescriptor
!= null && isDependent(depDescriptor
, on
, map
)) {
319 public static boolean isIncompatible(final IdeaPluginDescriptor descriptor
) {
320 final String buildNumber
= getBuildNumber();
321 if (buildNumber
!= null) {
322 final String sinceBuild
= descriptor
.getSinceBuild();
324 Integer
.parseInt(sinceBuild
);
325 if (sinceBuild
.compareToIgnoreCase(buildNumber
) > 0) {
329 catch (NumberFormatException e
) {
330 //skip invalid numbers
333 final String untilBuild
= descriptor
.getUntilBuild();
335 Integer
.parseInt(untilBuild
);
336 if (untilBuild
.compareToIgnoreCase(buildNumber
) < 0) {
340 catch (NumberFormatException e
) {
341 //skip invalid numbers
347 private static Comparator
<IdeaPluginDescriptor
> getPluginDescriptorComparator(Map
<PluginId
, IdeaPluginDescriptorImpl
> idToDescriptorMap
) {
348 final Graph
<PluginId
> graph
= createPluginIdGraph(idToDescriptorMap
);
349 final DFSTBuilder
<PluginId
> builder
= new DFSTBuilder
<PluginId
>(graph
);
351 if (!builder.isAcyclic()) {
352 final Pair<String,String> circularDependency = builder.getCircularDependency();
353 throw new Exception("Cyclic dependencies between plugins are not allowed: \"" + circularDependency.getFirst() + "\" and \"" + circularDependency.getSecond() + "");
356 final Comparator
<PluginId
> idComparator
= builder
.comparator();
357 return new Comparator
<IdeaPluginDescriptor
>() {
358 public int compare(IdeaPluginDescriptor o1
, IdeaPluginDescriptor o2
) {
359 return idComparator
.compare(o1
.getPluginId(), o2
.getPluginId());
364 private static Graph
<PluginId
> createPluginIdGraph(final Map
<PluginId
, IdeaPluginDescriptorImpl
> idToDescriptorMap
) {
365 final PluginId
[] ids
= idToDescriptorMap
.keySet().toArray(new PluginId
[idToDescriptorMap
.size()]);
366 return GraphGenerator
.create(CachingSemiGraph
.create(new GraphGenerator
.SemiGraph
<PluginId
>() {
367 public Collection
<PluginId
> getNodes() {
368 return Arrays
.asList(ids
);
371 public Iterator
<PluginId
> getIn(PluginId pluginId
) {
372 final IdeaPluginDescriptor descriptor
= idToDescriptorMap
.get(pluginId
);
373 ArrayList
<PluginId
> plugins
= new ArrayList
<PluginId
>();
374 for(PluginId dependentPluginId
: descriptor
.getDependentPluginIds()) {
375 // check for missing optional dependency
376 if (idToDescriptorMap
.containsKey(dependentPluginId
)) {
377 plugins
.add(dependentPluginId
);
380 return plugins
.iterator();
385 private static ClassLoader
[] getParentLoaders(Map
<PluginId
, IdeaPluginDescriptorImpl
> idToDescriptorMap
, PluginId
[] pluginIds
) {
386 if (ApplicationManager
.getApplication().isUnitTestMode()) return new ClassLoader
[0];
387 final List
<ClassLoader
> classLoaders
= new ArrayList
<ClassLoader
>();
388 for (final PluginId id
: pluginIds
) {
389 IdeaPluginDescriptor pluginDescriptor
= idToDescriptorMap
.get(id
);
390 if (pluginDescriptor
== null) {
391 continue; // Might be an optional dependency
394 final ClassLoader loader
= pluginDescriptor
.getPluginClassLoader();
395 if (loader
== null) {
396 getLogger().assertTrue(false, "Plugin class loader should be initialized for plugin " + id
);
398 classLoaders
.add(loader
);
400 return classLoaders
.toArray(new ClassLoader
[classLoaders
.size()]);
403 private static IdeaPluginDescriptorImpl
[] loadDescriptors() {
404 if (ClassloaderUtil
.isLoadingOfExternalPluginsDisabled()) {
405 return IdeaPluginDescriptorImpl
.EMPTY_ARRAY
;
408 final List
<IdeaPluginDescriptorImpl
> result
= new ArrayList
<IdeaPluginDescriptorImpl
>();
410 loadDescriptors(PathManager
.getPluginsPath(), result
);
411 loadDescriptors(PathManager
.getPreinstalledPluginsPath(), result
);
413 loadDescriptorsFromProperty(result
);
415 loadDescriptorsFromClassPath(result
);
417 IdeaPluginDescriptorImpl
[] pluginDescriptors
= result
.toArray(new IdeaPluginDescriptorImpl
[result
.size()]);
419 Arrays
.sort(pluginDescriptors
, new PluginDescriptorComparator(pluginDescriptors
));
421 catch (Exception e
) {
422 prepareLoadingPluginsErrorMessage(IdeBundle
.message("error.plugins.were.not.loaded", e
.getMessage()));
424 pluginDescriptors
= IdeaPluginDescriptorImpl
.EMPTY_ARRAY
;
426 return pluginDescriptors
;
429 public static synchronized void reportPluginError() {
430 if (myPluginError
!= null) {
431 JOptionPane
.showMessageDialog(null, myPluginError
, IdeBundle
.message("title.plugin.error"), JOptionPane
.ERROR_MESSAGE
);
432 myPluginError
= null;
436 private static void loadDescriptorsFromProperty(final List
<IdeaPluginDescriptorImpl
> result
) {
437 final String pathProperty
= System
.getProperty(PROPERTY_PLUGIN_PATH
);
438 if (pathProperty
== null) return;
440 for (java
.util
.StringTokenizer t
= new java
.util
.StringTokenizer(pathProperty
, File
.pathSeparator
); t
.hasMoreTokens();) {
441 String s
= t
.nextToken();
442 final IdeaPluginDescriptorImpl ideaPluginDescriptor
= loadDescriptor(new File(s
), PLUGIN_XML
);
443 if (ideaPluginDescriptor
!= null) {
444 result
.add(ideaPluginDescriptor
);
449 @SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"})
450 private static void loadDescriptorsFromClassPath(final List
<IdeaPluginDescriptorImpl
> result
) {
452 final Collection
<URL
> urls
= getClassLoaderUrls();
453 for (URL url
: urls
) {
454 final String protocol
= url
.getProtocol();
455 if ("file".equals(protocol
)) {
456 final File file
= new File(URLDecoder
.decode(url
.getFile()));
457 //final String canonicalPath = file.getCanonicalPath();
458 //if (!canonicalPath.startsWith(homePath) || canonicalPath.endsWith(".jar")) continue;
459 //if (!canonicalPath.startsWith(homePath)) continue;
461 final String platformPrefix
= System
.getProperty("idea.platform.prefix");
462 IdeaPluginDescriptorImpl platformPluginDescriptor
= null;
463 if (platformPrefix
!= null) {
464 platformPluginDescriptor
= loadDescriptor(file
, platformPrefix
+ "Plugin.xml");
465 if (platformPluginDescriptor
!= null && !result
.contains(platformPluginDescriptor
)) {
466 platformPluginDescriptor
.setUseCoreClassLoader(true);
467 result
.add(platformPluginDescriptor
);
471 IdeaPluginDescriptorImpl pluginDescriptor
= loadDescriptor(file
, PLUGIN_XML
);
472 if (platformPrefix
!= null && pluginDescriptor
!= null && pluginDescriptor
.getName().equals("IDEA CORE")) {
475 if (pluginDescriptor
!= null && !result
.contains(pluginDescriptor
)) {
476 if (platformPluginDescriptor
!= null) {
477 // if we found a regular plugin.xml in the same .jar/root as a platform-prefixed descriptor, use the core loader for it too
478 pluginDescriptor
.setUseCoreClassLoader(true);
480 result
.add(pluginDescriptor
);
485 catch (Exception e
) {
486 System
.err
.println("Error loading plugins from classpath:");
491 @SuppressWarnings({"EmptyCatchBlock"})
492 private static Collection
<URL
> getClassLoaderUrls() {
493 final ClassLoader classLoader
= PluginManager
.class.getClassLoader();
494 final Class
<?
extends ClassLoader
> aClass
= classLoader
.getClass();
495 if (aClass
.getName().equals(UrlClassLoader
.class.getName())) {
497 return (List
<URL
>)aClass
.getDeclaredMethod("getUrls").invoke(classLoader
);
499 catch (IllegalAccessException e
) {
501 catch (InvocationTargetException e
) {
503 catch (NoSuchMethodException e
) {
506 if (classLoader
instanceof URLClassLoader
) {
507 return Arrays
.asList(((URLClassLoader
)classLoader
).getURLs());
510 return Collections
.emptyList();
514 private static String
filterBadPlugins(List
<IdeaPluginDescriptorImpl
> result
) {
515 final Map
<PluginId
, IdeaPluginDescriptorImpl
> idToDescriptorMap
= new HashMap
<PluginId
, IdeaPluginDescriptorImpl
>();
516 final StringBuffer message
= new StringBuffer();
517 boolean pluginsWithoutIdFound
= false;
518 for (Iterator
<IdeaPluginDescriptorImpl
> it
= result
.iterator(); it
.hasNext();) {
519 final IdeaPluginDescriptorImpl descriptor
= it
.next();
520 final PluginId id
= descriptor
.getPluginId();
522 pluginsWithoutIdFound
= true;
524 if (idToDescriptorMap
.containsKey(id
)) {
525 if (message
.length() > 0) {
526 message
.append("\n");
528 message
.append(IdeBundle
.message("message.duplicate.plugin.id"));
532 else if (descriptor
.isEnabled()) {
533 idToDescriptorMap
.put(id
, descriptor
);
536 final List
<String
> disabledPluginIds
= new ArrayList
<String
>();
537 for (final Iterator
<IdeaPluginDescriptorImpl
> it
= result
.iterator(); it
.hasNext();) {
538 final IdeaPluginDescriptorImpl pluginDescriptor
= it
.next();
539 checkDependants(pluginDescriptor
, new Function
<PluginId
, IdeaPluginDescriptor
>() {
540 public IdeaPluginDescriptor
fun(final PluginId pluginId
) {
541 return idToDescriptorMap
.get(pluginId
);
543 }, new Condition
<PluginId
>() {
544 public boolean value(final PluginId pluginId
) {
545 if (!idToDescriptorMap
.containsKey(pluginId
)) {
546 if (message
.length() > 0) {
547 message
.append("\n");
549 pluginDescriptor
.setEnabled(false);
550 disabledPluginIds
.add(pluginDescriptor
.getPluginId().getIdString());
551 message
.append(getDisabledPlugins().contains(pluginId
.getIdString())
552 ? IdeBundle
.message("error.required.plugin.disabled", pluginDescriptor
.getPluginId(), pluginId
)
553 : IdeBundle
.message("error.required.plugin.not.installed", pluginDescriptor
.getPluginId(), pluginId
));
561 if (!disabledPluginIds
.isEmpty()) {
563 saveDisabledPlugins(disabledPluginIds
, true);
565 catch (IOException e
) {
566 getLogger().error(e
);
569 if (pluginsWithoutIdFound
) {
570 if (message
.length() > 0) {
571 message
.append("\n");
573 message
.append(IdeBundle
.message("error.plugins.without.id.found"));
575 if (message
.length() > 0) {
576 message
.insert(0, IdeBundle
.message("error.problems.found.loading.plugins"));
577 return message
.toString();
582 public static void checkDependants(final IdeaPluginDescriptor pluginDescriptor
,
583 final Function
<PluginId
, IdeaPluginDescriptor
> pluginId2Descriptor
,
584 final Condition
<PluginId
> check
) {
585 checkDependants(pluginDescriptor
, pluginId2Descriptor
, check
, new HashSet
<PluginId
>());
588 private static boolean checkDependants(final IdeaPluginDescriptor pluginDescriptor
,
589 final Function
<PluginId
, IdeaPluginDescriptor
> pluginId2Descriptor
,
590 final Condition
<PluginId
> check
,
591 final Set
<PluginId
> processed
) {
592 processed
.add(pluginDescriptor
.getPluginId());
593 final PluginId
[] dependentPluginIds
= pluginDescriptor
.getDependentPluginIds();
594 final Set
<PluginId
> optionalDependencies
= new HashSet
<PluginId
>(Arrays
.asList(pluginDescriptor
.getOptionalDependentPluginIds()));
595 for (final PluginId dependentPluginId
: dependentPluginIds
) {
596 if (processed
.contains(dependentPluginId
)) continue;
597 if (!optionalDependencies
.contains(dependentPluginId
)) {
598 if (!check
.value(dependentPluginId
)) {
601 final IdeaPluginDescriptor dependantPluginDescriptor
= pluginId2Descriptor
.fun(dependentPluginId
);
602 if (dependantPluginDescriptor
!= null && !checkDependants(dependantPluginDescriptor
, pluginId2Descriptor
, check
, processed
)) {
611 private static String
getBuildNumber() {
612 if (ourBuildNumber
== null) {
614 ourBuildNumber
= new String(FileUtil
.loadFileText(new File(PathManager
.getHomePath() + "/build.txt"))).trim();
616 catch (IOException e
) {
617 ourBuildNumber
= null;
620 return ourBuildNumber
;
624 private static void loadDescriptors(String pluginsPath
, List
<IdeaPluginDescriptorImpl
> result
) {
625 final File pluginsHome
= new File(pluginsPath
);
626 final File
[] files
= pluginsHome
.listFiles();
628 for (File file
: files
) {
629 final IdeaPluginDescriptorImpl descriptor
= loadDescriptor(file
, PLUGIN_XML
);
630 if (descriptor
!= null && !result
.contains(descriptor
)) {
631 result
.add(descriptor
);
637 @SuppressWarnings({"HardCodedStringLiteral"})
639 private static IdeaPluginDescriptorImpl
loadDescriptor(final File file
, final @NonNls String fileName
) {
640 IdeaPluginDescriptorImpl descriptor
= null;
642 if (file
.isDirectory()) {
643 descriptor
= loadDescriptorFromDir(file
, fileName
);
645 if (descriptor
== null) {
646 File libDir
= new File(file
, "lib");
647 if (!libDir
.isDirectory()) {
650 final File
[] files
= libDir
.listFiles();
651 if (files
== null || files
.length
== 0) {
654 for (final File f
: files
) {
655 if (ClassloaderUtil
.isJarOrZip(f
)) {
656 IdeaPluginDescriptorImpl descriptor1
= loadDescriptorFromJar(f
, fileName
);
657 if (descriptor1
!= null) {
658 if (descriptor
!= null) {
659 getLogger().info("Cannot load " + file
+ " because two or more plugin.xml's detected");
662 descriptor
= descriptor1
;
663 descriptor
.setPath(file
);
666 else if (f
.isDirectory()) {
667 IdeaPluginDescriptorImpl descriptor1
= loadDescriptorFromDir(f
, fileName
);
668 if (descriptor1
!= null) {
669 if (descriptor
!= null) {
670 getLogger().info("Cannot load " + file
+ " because two or more plugin.xml's detected");
673 descriptor
= descriptor1
;
674 descriptor
.setPath(file
);
680 else if (StringUtil
.endsWithIgnoreCase(file
.getName(), ".jar")) {
681 descriptor
= loadDescriptorFromJar(file
, fileName
);
684 if (descriptor
!= null && !descriptor
.getOptionalConfigs().isEmpty()) {
685 final Map
<PluginId
, IdeaPluginDescriptorImpl
> descriptors
= new HashMap
<PluginId
, IdeaPluginDescriptorImpl
>(descriptor
.getOptionalConfigs().size());
686 for (Map
.Entry
<PluginId
, String
> entry
: descriptor
.getOptionalConfigs().entrySet()) {
687 final IdeaPluginDescriptorImpl optionalDescriptor
= loadDescriptor(file
, entry
.getValue());
688 if (optionalDescriptor
!= null) {
689 descriptors
.put(entry
.getKey(), optionalDescriptor
);
692 descriptor
.setOptionalDescriptors(descriptors
);
698 private static IdeaPluginDescriptorImpl
loadDescriptorFromDir(final File file
, @NonNls String fileName
) {
699 IdeaPluginDescriptorImpl descriptor
= null;
700 File descriptorFile
= new File(file
, META_INF
+ File
.separator
+ fileName
);
701 if (descriptorFile
.exists()) {
702 descriptor
= new IdeaPluginDescriptorImpl(file
);
705 descriptor
.readExternal(descriptorFile
.toURL());
707 catch (Exception e
) {
708 System
.err
.println("Cannot load: " + descriptorFile
.getAbsolutePath());
716 public static IdeaPluginDescriptorImpl
loadDescriptorFromJar(File file
) {
717 return loadDescriptorFromJar(file
, PLUGIN_XML
);
721 private static IdeaPluginDescriptorImpl
loadDescriptorFromJar(File file
, @NonNls String fileName
) {
724 IdeaPluginDescriptorImpl descriptor
= new IdeaPluginDescriptorImpl(file
);
726 URL fileURL
= file
.toURL();
727 URL jarURL
= new URL(
728 "jar:" + fileURL
.toExternalForm() + "!/META-INF/" + fileName
731 descriptor
.readExternal(jarURL
);
734 catch (XmlSerializationException e
) {
735 getLogger().info("Cannot load " + file
, e
);
736 prepareLoadingPluginsErrorMessage("Plugin file " + file
.getName() + " contains invalid plugin descriptor file.");
738 catch (FileNotFoundException e
) {
741 catch (Exception e
) {
742 getLogger().info("Cannot load " + file
, e
);
749 private static ClassLoader
createPluginClassLoader(final File
[] classPath
,
750 final ClassLoader
[] parentLoaders
,
751 IdeaPluginDescriptor pluginDescriptor
) {
753 if (pluginDescriptor
.getUseIdeaClassLoader()) {
755 final ClassLoader loader
= PluginManager
.class.getClassLoader();
756 final Method addUrlMethod
= getAddUrlMethod(loader
);
759 for (File aClassPath
: classPath
) {
760 final File file
= aClassPath
.getCanonicalFile();
761 addUrlMethod
.invoke(loader
, file
.toURL());
766 catch (NoSuchMethodException e
) {
767 e
.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
769 catch (IOException e
) {
770 e
.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
772 catch (IllegalAccessException e
) {
773 e
.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
775 catch (InvocationTargetException e
) {
776 e
.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
780 PluginId pluginId
= pluginDescriptor
.getPluginId();
781 File pluginRoot
= pluginDescriptor
.getPath();
783 if (ApplicationManager
.getApplication().isUnitTestMode()) return null;
785 final List
<URL
> urls
= new ArrayList
<URL
>(classPath
.length
);
786 for (File aClassPath
: classPath
) {
787 final File file
= aClassPath
.getCanonicalFile(); // it is critical not to have "." and ".." in classpath elements
788 urls
.add(file
.toURL());
790 return new PluginClassLoader(urls
, parentLoaders
, pluginId
, pluginRoot
);
792 catch (MalformedURLException e
) {
795 catch (IOException e
) {
801 @SuppressWarnings({"HardCodedStringLiteral"})
802 private static Method
getAddUrlMethod(final ClassLoader loader
) throws NoSuchMethodException
{
803 if (loader
instanceof URLClassLoader
) {
804 final Method addUrlMethod
= URLClassLoader
.class.getDeclaredMethod("addURL", URL
.class);
805 addUrlMethod
.setAccessible(true);
809 return loader
.getClass().getDeclaredMethod("addURL", URL
.class);
813 public static boolean isPluginInstalled(PluginId id
) {
814 return (getPlugin(id
) != null);
818 public static IdeaPluginDescriptor
getPlugin(PluginId id
) {
819 final IdeaPluginDescriptor
[] plugins
= getPlugins();
820 for (final IdeaPluginDescriptor plugin
: plugins
) {
821 if (Comparing
.equal(id
, plugin
.getPluginId())) {
828 public static void addPluginClass(String className
, PluginId pluginId
) {
829 synchronized(PLUGIN_CLASSES_LOCK
) {
830 if (ourPluginClasses
== null) {
831 ourPluginClasses
= new THashMap
<String
, PluginId
>();
833 ourPluginClasses
.put(className
, pluginId
);
837 public static boolean isPluginClass(String className
) {
838 return getPluginByClassName(className
) != null;
842 public static PluginId
getPluginByClassName(String className
) {
843 synchronized (PLUGIN_CLASSES_LOCK
) {
844 return ourPluginClasses
!= null ? ourPluginClasses
.get(className
) : null;
848 public static void saveDisabledPlugins(List
<String
> ids
, boolean append
) throws IOException
{
849 File plugins
= new File(PathManager
.getConfigPath(), PluginManager
.DISABLED_PLUGINS_FILENAME
);
850 if (!plugins
.isFile()) {
851 plugins
.createNewFile();
853 PrintWriter printWriter
= null;
855 printWriter
= new PrintWriter(new BufferedWriter(new FileWriter(plugins
, append
)));
856 for (String id
: ids
) {
857 printWriter
.println(id
);
862 if (printWriter
!= null) {
868 public static List
<String
> getDisabledPlugins() {
869 if (ourDisabledPlugins
== null) {
870 ourDisabledPlugins
= new ArrayList
<String
>();
871 if (System
.getProperty("idea.ignore.disabled.plugins") == null && !ApplicationManager
.getApplication().isUnitTestMode()) {
872 final File file
= new File(PathManager
.getConfigPath(), DISABLED_PLUGINS_FILENAME
);
874 BufferedReader reader
= null;
876 reader
= new BufferedReader(new FileReader(file
));
878 while ((id
= reader
.readLine()) != null) {
879 ourDisabledPlugins
.add(id
.trim());
882 catch (IOException e
) {
887 if (reader
!= null) {
891 catch (IOException e
) {
898 return ourDisabledPlugins
;
901 private static class IdeaLogProvider
implements LogProvider
{
902 public void error(String message
) {
903 getLogger().error(message
);
906 public void error(String message
, Throwable t
) {
907 getLogger().error(message
, t
);
910 public void error(Throwable t
) {
911 getLogger().error(t
);
914 public void warn(String message
) {
915 getLogger().info(message
);
918 public void warn(String message
, Throwable t
) {
919 getLogger().info(message
, t
);
922 public void warn(Throwable t
) {
927 private static class LoggerHolder
{
928 private static final Logger ourLogger
= Logger
.getInstance("#com.intellij.ide.plugins.PluginManager");
931 public static Logger
getLogger() {
932 return LoggerHolder
.ourLogger
;