IDEADEV-27889 CGE: AbstractClassGenerator.create
[fedora-idea.git] / platform-api / src / com / intellij / ide / plugins / PluginManager.java
blob9a7f7bace79c1c36b161a29ff1aa4706077caa1a
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;
31 import javax.swing.*;
32 import java.io.*;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.net.MalformedURLException;
36 import java.net.URL;
37 import java.net.URLClassLoader;
38 import java.net.URLDecoder;
39 import java.util.*;
41 /**
42 * @author mike
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;
78 /**
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) {
83 initializePlugins();
84 ClassloaderUtil.clearJarURLCache();
86 return ourPlugins;
89 /**
90 * Called via reflection
92 @SuppressWarnings({"UnusedDeclaration"})
93 protected static void start(final String mainClass, final String methodName, final String[] args) {
94 try {
95 //noinspection HardCodedStringLiteral
96 ThreadGroup threadGroup = new ThreadGroup("Idea Thread Group") {
97 public void uncaughtException(Thread t, Throwable e) {
98 getLogger().error(e);
102 Runnable runnable = new Runnable() {
103 public void run() {
104 try {
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) {
118 e.printStackTrace();
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);
144 } else {
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);
181 else {
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},
188 pluginDescriptor);
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;
221 else {
222 myPluginError += "\n" + errorMessage;
224 } else {
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() {
237 try {
238 // no plugins during bootstrap
239 Class.forName("com.intellij.openapi.extensions.Extensions");
241 catch (ClassNotFoundException e) {
242 return false;
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);
258 return 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)) {
266 return false;
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;
277 boolean shouldLoad;
278 //noinspection HardCodedStringLiteral
279 final String loadPluginCategory = System.getProperty("idea.load.plugins.category");
280 if (loadPluginCategory != null) {
281 shouldLoad = loadPluginCategory.equals(descriptor.getCategory());
283 else {
284 if (pluginId != null) {
285 shouldLoad = pluginId.equals(idString);
286 if (!shouldLoad) {
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);
294 } else {
295 shouldLoad = !getDisabledPlugins().contains(idString);
297 if (shouldLoad && descriptor instanceof IdeaPluginDescriptorImpl) {
298 if (isIncompatible(descriptor)) return true;
303 return !shouldLoad;
306 private static boolean isDependent(final IdeaPluginDescriptor descriptor, final PluginId on, Map<PluginId, IdeaPluginDescriptor> map) {
307 for (PluginId id: descriptor.getDependentPluginIds()) {
308 if (id.equals(on)) {
309 return true;
311 final IdeaPluginDescriptor depDescriptor = map.get(id);
312 if (depDescriptor != null && isDependent(depDescriptor, on, map)) {
313 return true;
316 return false;
319 public static boolean isIncompatible(final IdeaPluginDescriptor descriptor) {
320 final String buildNumber = getBuildNumber();
321 if (buildNumber != null) {
322 final String sinceBuild = descriptor.getSinceBuild();
323 try {
324 Integer.parseInt(sinceBuild);
325 if (sinceBuild.compareToIgnoreCase(buildNumber) > 0) {
326 return true;
329 catch (NumberFormatException e) {
330 //skip invalid numbers
333 final String untilBuild = descriptor.getUntilBuild();
334 try {
335 Integer.parseInt(untilBuild);
336 if (untilBuild.compareToIgnoreCase(buildNumber) < 0) {
337 return true;
340 catch (NumberFormatException e) {
341 //skip invalid numbers
344 return false;
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();
382 }));
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()]);
418 try {
419 Arrays.sort(pluginDescriptors, new PluginDescriptorComparator(pluginDescriptors));
421 catch (Exception e) {
422 prepareLoadingPluginsErrorMessage(IdeBundle.message("error.plugins.were.not.loaded", e.getMessage()));
423 getLogger().info(e);
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) {
451 try {
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")) {
473 continue;
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:");
487 e.printStackTrace();
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())) {
496 try {
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();
513 @Nullable
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();
521 if (id == null) {
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"));
529 message.append(id);
530 it.remove();
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));
554 it.remove();
555 return false;
557 return true;
561 if (!disabledPluginIds.isEmpty()) {
562 try {
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();
579 return null;
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)) {
599 return false;
601 final IdeaPluginDescriptor dependantPluginDescriptor = pluginId2Descriptor.fun(dependentPluginId);
602 if (dependantPluginDescriptor != null && !checkDependants(dependantPluginDescriptor, pluginId2Descriptor, check, processed)) {
603 return false;
607 return true;
610 @Nullable
611 private static String getBuildNumber() {
612 if (ourBuildNumber == null) {
613 try {
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();
627 if (files != null) {
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"})
638 @Nullable
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()) {
648 return null;
650 final File[] files = libDir.listFiles();
651 if (files == null || files.length == 0) {
652 return null;
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");
660 return null;
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");
671 return null;
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);
694 return descriptor;
697 @Nullable
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);
704 try {
705 descriptor.readExternal(descriptorFile.toURL());
707 catch (Exception e) {
708 System.err.println("Cannot load: " + descriptorFile.getAbsolutePath());
709 e.printStackTrace();
712 return descriptor;
715 @Nullable
716 public static IdeaPluginDescriptorImpl loadDescriptorFromJar(File file) {
717 return loadDescriptorFromJar(file, PLUGIN_XML);
720 @Nullable
721 private static IdeaPluginDescriptorImpl loadDescriptorFromJar(File file, @NonNls String fileName) {
722 try {
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);
732 return descriptor;
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) {
739 return null;
741 catch (Exception e) {
742 getLogger().info("Cannot load " + file, e);
745 return null;
748 @Nullable
749 private static ClassLoader createPluginClassLoader(final File[] classPath,
750 final ClassLoader[] parentLoaders,
751 IdeaPluginDescriptor pluginDescriptor) {
753 if (pluginDescriptor.getUseIdeaClassLoader()) {
754 try {
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());
764 return loader;
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;
784 try {
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) {
793 e.printStackTrace();
795 catch (IOException e) {
796 e.printStackTrace();
798 return null;
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);
806 return addUrlMethod;
809 return loader.getClass().getDeclaredMethod("addURL", URL.class);
813 public static boolean isPluginInstalled(PluginId id) {
814 return (getPlugin(id) != null);
817 @Nullable
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())) {
822 return plugin;
825 return null;
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;
841 @Nullable
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;
854 try {
855 printWriter = new PrintWriter(new BufferedWriter(new FileWriter(plugins, append)));
856 for (String id : ids) {
857 printWriter.println(id);
859 printWriter.flush();
861 finally {
862 if (printWriter != null) {
863 printWriter.close();
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);
873 if (file.isFile()) {
874 BufferedReader reader = null;
875 try {
876 reader = new BufferedReader(new FileReader(file));
877 String id;
878 while ((id = reader.readLine()) != null) {
879 ourDisabledPlugins.add(id.trim());
882 catch (IOException e) {
883 //do nothing
885 finally {
886 try {
887 if (reader != null) {
888 reader.close();
891 catch (IOException e) {
892 //do nothing
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) {
923 getLogger().info(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;