Skip to content

Commit

Permalink
Avoid propagating an exception to the compiler if we fail to load Aut…
Browse files Browse the repository at this point in the history
…oValue extensions due to a corrupt jar file in the class path.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=129374653
  • Loading branch information
eamonnmcmanus committed Aug 30, 2016
1 parent 3574a39 commit 4a052a8
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import java.beans.Introspector;
import java.io.IOException;
import java.io.Serializable;
Expand All @@ -52,10 +51,10 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.Generated;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
Expand Down Expand Up @@ -93,12 +92,19 @@
@AutoService(Processor.class)
public class AutoValueProcessor extends AbstractProcessor {
public AutoValueProcessor() {
this(ServiceLoader.load(AutoValueExtension.class, AutoValueProcessor.class.getClassLoader()));
this(AutoValueProcessor.class.getClassLoader());
}

@VisibleForTesting
AutoValueProcessor(ClassLoader loaderForExtensions) {
this.extensions = null;
this.loaderForExtensions = loaderForExtensions;
}

@VisibleForTesting
public AutoValueProcessor(Iterable<? extends AutoValueExtension> extensions) {
this.extensions = extensions;
this.extensions = ImmutableList.<AutoValueExtension>copyOf(extensions);
this.loaderForExtensions = null;
}

@Override
Expand Down Expand Up @@ -126,7 +132,11 @@ public SourceVersion getSupportedSourceVersion() {
*/
private final List<String> deferredTypeNames = new ArrayList<String>();

private Iterable<? extends AutoValueExtension> extensions;
// Depending on how this AutoValueProcessor was constructed, we might already have a list of
// extensions when init() is run, or, if `extensions` is null, we have a ClassLoader that will be
// used to get the list using the ServiceLoader API.
private ImmutableList<AutoValueExtension> extensions;
private final ClassLoader loaderForExtensions;

private Types typeUtils;

Expand All @@ -135,6 +145,26 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
errorReporter = new ErrorReporter(processingEnv);
typeUtils = processingEnv.getTypeUtils();

if (extensions == null) {
try {
extensions = ImmutableList.copyOf(
ServiceLoader.load(AutoValueExtension.class, loaderForExtensions));
// ServiceLoader.load returns a lazily-evaluated Iterable, so evaluate it eagerly now
// to discover any exceptions.
} catch (Throwable t) {
StringBuilder warning = new StringBuilder();
warning.append(
"An exception occurred while looking for AutoValue extensions. "
+ "No extensions will function.");
if (t instanceof ServiceConfigurationError) {
warning.append(" This may be due to a corrupt jar file in the compiler's classpath.");
}
warning.append(" Exception: ").append(t);
errorReporter.reportWarning(warning.toString(), null);
extensions = ImmutableList.of();
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,28 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.truth.Truth;
import com.google.testing.compile.CompileTester.SuccessfulCompilationClause;
import com.google.testing.compile.JavaFileObjects;

import junit.framework.TestCase;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.Set;

import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import junit.framework.TestCase;

public class ExtensionTest extends TestCase {
public void testExtensionCompilation() throws Exception {
Expand Down Expand Up @@ -463,6 +470,50 @@ public void testUnconsumedMethod() throws Exception {
// so we know what to expect.
}

/**
* Tests that the search for extensions doesn't completely blow AutoValue up if there is a
* corrupt jar in the {@code processorpath}. If we're not careful, that can lead to a
* ServiceConfigurationError.
*/
public void testBadJarDoesntBlowUp() throws IOException {
File badJar = File.createTempFile("bogus", ".jar");
try {
doTestBadJarDoesntBlowUp(badJar);
} finally {
badJar.delete();
}
}

private void doTestBadJarDoesntBlowUp(File badJar) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(badJar);
JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream);
byte[] bogusLine = "bogus line\n".getBytes("UTF-8");
ZipEntry zipEntry = new ZipEntry("META-INF/services/" + AutoValueExtension.class.getName());
zipEntry.setSize(bogusLine.length);
jarOutputStream.putNextEntry(zipEntry);
jarOutputStream.write(bogusLine);
jarOutputStream.close();
ClassLoader badJarLoader = new URLClassLoader(new URL[] {badJar.toURI().toURL()});
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
"}");
SuccessfulCompilationClause success = assertThat(javaFileObject)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new AutoValueProcessor(badJarLoader))
.compilesWithoutError();
success.withWarningContaining(
"This may be due to a corrupt jar file in the compiler's classpath. Exception: "
+ ServiceConfigurationError.class.getName());
success.and()
.generatesFileNamed(StandardLocation.SOURCE_OUTPUT, "foo.bar", "AutoValue_Baz.java");
}

private static class FooExtension extends AutoValueExtension {

@Override
Expand Down

0 comments on commit 4a052a8

Please sign in to comment.