blob: 95b93362c14d05e142cd70b653f401d91c0ff71f [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.debugger;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.ModuleBasedConfiguration;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.JavaProgramPatcher;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JdkUtil;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LanguageLevelModuleExtension;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import java.io.File;
import java.io.IOException;
import java.util.jar.Attributes;
import java.util.regex.Pattern;
/**
* @author peter
*/
public class GroovyHotSwapper extends JavaProgramPatcher {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.debugger.GroovyHotSwapper");
private static final String GROOVY_HOTSWAP_AGENT_PATH = "groovy.hotswap.agent.path";
private static final Pattern SPRING_LOADED_PATTERN = Pattern.compile("-javaagent:.+springloaded-[^/\\\\]+\\.jar");
private static boolean containsGroovyClasses(final Project project) {
return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<Boolean>() {
@Nullable
@Override
public Result<Boolean> compute() {
return Result.create(FileTypeIndex.containsFileOfType(GroovyFileType.GROOVY_FILE_TYPE, GlobalSearchScope.projectScope(project)),
PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
});
}
private static boolean hasSpringLoadedReloader(JavaParameters javaParameters) {
for (String param : javaParameters.getVMParametersList().getParameters()) {
if (SPRING_LOADED_PATTERN.matcher(param).matches()) {
return true;
}
}
return false;
}
@Override
public void patchJavaParameters(Executor executor, RunProfile configuration, JavaParameters javaParameters) {
if (!executor.getId().equals(DefaultDebugExecutor.EXECUTOR_ID)) {
return;
}
if (!GroovyDebuggerSettings.getInstance().ENABLE_GROOVY_HOTSWAP) {
return;
}
if (hasSpringLoadedReloader(javaParameters)) {
return;
}
if (!(configuration instanceof RunConfiguration)) {
return;
}
final Project project = ((RunConfiguration)configuration).getProject();
if (project == null) {
return;
}
if (!LanguageLevelProjectExtension.getInstance(project).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_5)) {
return;
}
if (configuration instanceof ModuleBasedConfiguration) {
final Module module = ((ModuleBasedConfiguration)configuration).getConfigurationModule().getModule();
if (module != null) {
final LanguageLevel level = LanguageLevelModuleExtension.getInstance(module).getLanguageLevel();
if (level != null && !level.isAtLeast(LanguageLevel.JDK_1_5)) {
return;
}
}
}
Sdk jdk = javaParameters.getJdk();
if (jdk != null) {
String vendor = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VENDOR);
if (vendor != null && vendor.contains("IBM")) {
LOG.info("Due to IBM JDK pecularities (IDEA-59070) we don't add groovy agent when running applications under it");
return;
}
}
if (!project.isDefault() && containsGroovyClasses(project)) {
final String agentPath = handleSpacesInPath(getAgentJarPath());
if (agentPath != null) {
javaParameters.getVMParametersList().add("-javaagent:" + agentPath);
}
}
}
@Nullable
private static String handleSpacesInPath(String agentPath) {
if (agentPath.contains(" ")) {
final File dir = new File(PathManager.getSystemPath(), "groovyHotSwap");
if (dir.getAbsolutePath().contains(" ")) {
LOG.info("Groovy hot-swap not used since the agent path contains spaces: " + agentPath + "\n" +
"One can move the agent to a directory with no spaces in path and specify its path in <IDEA dist>/bin/idea.properties as " + GROOVY_HOTSWAP_AGENT_PATH + "=<path>");
return null;
}
final File toFile = new File(dir, "gragent.jar");
try {
FileUtil.copy(new File(agentPath), toFile);
return toFile.getPath();
}
catch (IOException e) {
LOG.info(e);
}
}
return agentPath;
}
private static String getAgentJarPath() {
final String userDefined = System.getProperty(GROOVY_HOTSWAP_AGENT_PATH);
if (userDefined != null && new File(userDefined).exists()) {
return userDefined;
}
final File ourJar = new File(PathUtil.getJarPathForClass(GroovyHotSwapper.class));
if (ourJar.isDirectory()) { //development mode
return PluginPathManager.getPluginHomePath("groovy") + "/hotswap/gragent.jar";
}
final File pluginDir = ourJar.getParentFile();
return pluginDir.getPath() + File.separator + "agent" + File.separator + "gragent.jar";
}
}