/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.core; import com.intellij.ide.ApplicationLoadListener; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.cl.PluginClassLoader; import com.intellij.idea.IdeaApplication; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationStarter; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginId; import org.jetbrains.annotations.NotNull; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; public class GosuApplicationStarter implements ApplicationStarter, ApplicationLoadListener { private static final Logger LOG = Logger.getInstance(GosuApplicationStarter.class); public static final String COM_GUIDEWIRE = "com.guidewire."; public static final String COM_GUIDEWIRE_GOSU = COM_GUIDEWIRE + "gosu"; public static final String COM_GUIDEWIRE_GUNIT = COM_GUIDEWIRE + "gunit"; private ApplicationStarter defaultStarter; public GosuApplicationStarter() { Class<?> aClass = lookupStarterClass( "com.intellij.idea.IdeaUltimateApplication", "IdeaUltimateStarter" ); if (aClass == null) { aClass = lookupStarterClass( "com.intellij.idea.IdeaApplication", "IdeStarter" ); } try { final Constructor<?> constructor = aClass.getDeclaredConstructors()[0]; constructor.setAccessible(true); defaultStarter = (ApplicationStarter) constructor.newInstance(IdeaApplication.getInstance()); } catch (Exception e) { LOG.error(e); } } @Override public void beforeApplicationLoaded(Application application) { // When started without "gosu" command line argument fixClassLoaders(); } private Class<?> lookupStarterClass(String appClass, String starterClassName) { try { // IDEA Ultimate Class<?> aClass = Class.forName( appClass ); for (Class<?> starterClass : aClass.getDeclaredClasses()) { if (starterClass.getSimpleName().equals(starterClassName)) { return starterClass; } } } catch ( ClassNotFoundException ex ) { // Just ignore } return null; } @NotNull @Override public String getCommandName() { return "gosu"; } @Override public void premain(String[] args) { removeCommandLineArgument(); // When started with "gosu" command line argument fixClassLoaders(); defaultStarter.premain(args); } public static void fixClassLoaders() { IdeaPluginDescriptor mainPlugin = GosuAppComponent.getEditorPlugin(); // Only fix if we are running in regular IntelliJ (not in tests) if (mainPlugin.getPluginClassLoader() instanceof PluginClassLoader) { PluginClassLoader mainClassLoader = (PluginClassLoader) mainPlugin.getPluginClassLoader(); for (IdeaPluginDescriptor plugin : PluginManager.getPlugins()) { String id = plugin.getPluginId().getIdString(); if (id.startsWith(COM_GUIDEWIRE) && !id.equals(COM_GUIDEWIRE_GOSU)) { recoverParents(plugin, mainClassLoader); recoverClasspath(plugin, mainClassLoader); // recoverDependencies(plugin, (IdeaPluginDescriptorImpl) mainPlugin); ((IdeaPluginDescriptorImpl)plugin).setLoader(mainClassLoader, false); } } } } private static void recoverParents(IdeaPluginDescriptor plugin, PluginClassLoader mainPluginPluginClassLoader) { try { final Field field = IdeaReflectionUtil.getField("myParents", "a", PluginClassLoader.class); field.setAccessible(true); List<ClassLoader> mainParents = new ArrayList<>(Arrays.asList((ClassLoader[]) field.get(mainPluginPluginClassLoader))); for (ClassLoader cl : (ClassLoader[])field.get(plugin.getPluginClassLoader())) { if (!mainParents.contains(cl) && !((PluginClassLoader)cl).getPluginId().getIdString().startsWith(COM_GUIDEWIRE)) { mainParents.add(cl); } } field.set(mainPluginPluginClassLoader, mainParents.toArray(new ClassLoader[mainParents.size()])); } catch (Exception e) { LOG.error(e); } } private void recoverDependencies(IdeaPluginDescriptor plugin, IdeaPluginDescriptorImpl mainPlugin) { HashSet<PluginId> existingPlugins = new HashSet<>(Arrays.asList(mainPlugin.getDependentPluginIds())); for (PluginId id : plugin.getDependentPluginIds()) { if (!existingPlugins.contains(id) && !id.equals(mainPlugin.getPluginId())) { mainPlugin.insertDependency(PluginManager.getPlugin(id)); } } } private static void recoverClasspath(@NotNull IdeaPluginDescriptor plugin, @NotNull PluginClassLoader mainClassLoader) { HashSet<URL> existingURLs = new HashSet<>(mainClassLoader.getUrls()); for (URL url : ((PluginClassLoader) plugin.getPluginClassLoader()).getUrls()) { if (!existingURLs.contains(url)) { mainClassLoader.addURL(url); } } } private void removeCommandLineArgument() { try { final Field field = IdeaReflectionUtil.getField("myArgs", "a", IdeaApplication.class); field.setAccessible(true); field.set(IdeaApplication.getInstance(), new String[0]); } catch (Exception e) { LOG.error(e); } } @Override public void main(String[] args) { defaultStarter.main(new String[0]); } }