GRADLE-1856 Tooling API: Make it possible to get IntelliJ IDEA view to the gradle project

Improved gradle api exceptions reporting
This commit is contained in:
Denis.Zhdanov
2011-10-21 15:17:56 +04:00
parent 7dc0091138
commit 9e88933413
4 changed files with 117 additions and 56 deletions
@@ -17,6 +17,7 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.packaging.artifacts.ModifiableArtifactModel;
import com.intellij.projectImport.ProjectImportBuilder;
import org.jetbrains.annotations.NotNull;
@@ -27,6 +28,7 @@ import org.jetbrains.plugins.gradle.importing.model.GradleModule;
import org.jetbrains.plugins.gradle.importing.model.GradleProject;
import org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager;
import org.jetbrains.plugins.gradle.remote.GradleProjectResolver;
import org.jetbrains.plugins.gradle.remote.GradleApiException;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleIcons;
import org.jetbrains.plugins.gradle.util.GradleLog;
@@ -157,14 +159,21 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder<GradleProje
}
catch (Exception e) {
Throwable unwrapped = RemoteUtil.unwrap(e);
String reason = unwrapped.getLocalizedMessage();
if (!StringUtil.isEmpty(reason)) {
errorReason.set(reason);
}
if (unwrapped.getClass() == NoClassDefFoundError.class) {
errorReason.set(GradleBundle.message("gradle.import.text.incomplete.tooling.api"));
}
else if (unwrapped.getClass() == GradleApiException.class) {
GradleLog.LOG.warn("Can't resolve gradle project. Reason: gradle api threw an exception:\n"
+ ((GradleApiException)unwrapped).getOriginalReason()
);
}
else {
errorReason.set(unwrapped.getLocalizedMessage());
GradleLog.LOG.warn("Can't resolve gradle project", e);
}
// Ignore here because it will be reported on method exit.
GradleLog.LOG.warn("Can't resolve gradle project", e);
}
}
});
@@ -0,0 +1,85 @@
package org.jetbrains.plugins.gradle.remote;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* We talk to Gradle API from the dedicated process, i.e. all gradle classes are loaded at that process in order to avoid
* memory problems at IJ process. That means that if Gradle API throws an exception, it can't be correctly read at IJ process
* (NoClassDefFoundError and ClassNotFoundException).
* <p/>
* This class allows to extract textual description of the target problem and deliver it for further processing without risking to
* get the problems mentioned above. I.e. it doesn't require anything specific can be safely delivered to IJ process then.
*
* @author Denis Zhdanov
* @since 10/21/11 11:42 AM
*/
public class GradleApiException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String myOriginalReason;
public GradleApiException() {
this(null, null);
}
public GradleApiException(@Nullable String message) {
this(message, null);
}
public GradleApiException(@Nullable Throwable cause) {
this("", cause);
}
public GradleApiException(@Nullable String message, @Nullable Throwable cause) {
super(extractMessage(message, cause));
if (cause == null) {
myOriginalReason = "";
return;
}
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
try {
cause.printStackTrace(printWriter);
}
finally {
printWriter.close();
}
myOriginalReason = stringWriter.toString();
}
/**
* @return textual description of the wrapped exception (if any); empty string otherwise
*/
@NotNull
public String getOriginalReason() {
return myOriginalReason;
}
@Override
public void printStackTrace(PrintWriter s) {
super.printStackTrace(s);
s.println(myOriginalReason);
}
@Override
public void printStackTrace(PrintStream s) {
super.printStackTrace(s);
s.println(myOriginalReason);
}
@Nullable
private static String extractMessage(@Nullable String message, @Nullable Throwable cause) {
if (message != null) {
return message;
}
return cause == null ? "" : cause.getMessage();
}
}
@@ -25,11 +25,12 @@ public interface GradleProjectResolver extends Remote {
* @param downloadLibraries flag that specifies if third-party libraries that are not available locally should be resolved (downloaded)
* @return object-level representation of the target gradle project
* @throws RemoteException in case of unexpected exception during remote communications
* @throws GradleApiException in case of unexpected exception thrown from Gradle API
* @throws IllegalArgumentException if given path doesn't point to directory that contains gradle project or if gradle api
* returns invalid data
* @throws IllegalStateException if it's not possible to resolve target project info
*/
@NotNull
GradleProject resolveProjectInfo(@NotNull String projectPath, boolean downloadLibraries)
throws RemoteException, IllegalArgumentException, IllegalStateException;
throws RemoteException, GradleApiException, IllegalArgumentException, IllegalStateException;
}
@@ -13,16 +13,13 @@ import org.gradle.tooling.model.idea.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.importing.model.*;
import org.jetbrains.plugins.gradle.remote.GradleProjectResolver;
import org.jetbrains.plugins.gradle.remote.RemoteGradleProcessSettings;
import org.jetbrains.plugins.gradle.remote.RemoteGradleService;
import org.jetbrains.plugins.gradle.remote.*;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleLog;
import org.jetbrains.plugins.gradle.util.GradleUtil;
import java.io.File;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -37,16 +34,25 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class GradleProjectResolverImpl extends RemoteObject implements GradleProjectResolver, RemoteGradleService {
private static final Set<String> NON_UNIQUE_PATH_ENTRIES = new HashSet<String>(Arrays.asList(
"src", "main", "java", "test", "resources"
));
private final BlockingQueue<ProjectConnection> myConnections = new LinkedBlockingQueue<ProjectConnection>();
private final AtomicReference<RemoteGradleProcessSettings> mySettings = new AtomicReference<RemoteGradleProcessSettings>();
private final BlockingQueue<ProjectConnection> myConnections = new LinkedBlockingQueue<ProjectConnection>();
private final AtomicReference<RemoteGradleProcessSettings> mySettings = new AtomicReference<RemoteGradleProcessSettings>();
private final GradleLibraryNamesMixer myLibraryNamesMixer = new GradleLibraryNamesMixer();
@NotNull
@Override
public GradleProject resolveProjectInfo(@NotNull String projectPath, boolean downloadLibraries)
throws RemoteException, GradleApiException, IllegalArgumentException, IllegalStateException
{
try {
return doResolveProjectInfo(projectPath, downloadLibraries);
}
catch (Throwable e) {
throw new GradleApiException(e);
}
}
@NotNull
private GradleProject doResolveProjectInfo(@NotNull String projectPath, boolean downloadLibraries)
throws RemoteException, IllegalArgumentException, IllegalStateException
{
ProjectConnection connection = getConnection(projectPath);
@@ -58,7 +64,7 @@ public class GradleProjectResolverImpl extends RemoteObject implements GradlePro
// populating dependent module object.
Map<String, Pair<GradleModule, IdeaModule>> modules = createModules(project, result);
populateModules(modules.values(), result);
diversifyLibraryNamesIfNecessary(result);
myLibraryNamesMixer.mixNames(result.getLibraries());
return result;
}
@@ -315,46 +321,6 @@ public class GradleProjectResolverImpl extends RemoteObject implements GradlePro
return null;
}
/**
* Gradle API doesn't provide library names at the moment, so, we deduce them from the path. However, it's possible to have identical
* names then.
* <p/>
* This method solves that by diversifying duplicate library names.
*
* @param project target project which library names should be diversified if necessary
*/
private static void diversifyLibraryNamesIfNecessary(@NotNull GradleProject project) {
Map<String, GradleLibrary> libraries = new HashMap<String, GradleLibrary>();
for (GradleLibrary library : project.getLibraries()) {
GradleLibrary previous = libraries.remove(library.getName());
if (previous == null) {
libraries.put(library.getName(), library);
continue;
}
previous.setName(generateName(previous));
libraries.put(previous.getName(), previous);
library.setName(generateName(library));
libraries.put(library.getName(), library);
}
}
@NotNull
private static String generateName(@NotNull GradleLibrary library) {
for (LibraryPathType pathType : LibraryPathType.values()) {
String path = library.getPath(pathType);
if (path == null) {
continue;
}
File file = new File(path).getParentFile();
for (; file != null; file = file.getParentFile()) {
if (!NON_UNIQUE_PATH_ENTRIES.contains(file.getName())) {
return file.getName() + "-" + library.getName();
}
}
}
return library.getName();
}
/**
* Allows to retrieve gradle api connection to use for the given project.
*