Search in sources :

Example 1 with GroovySystemLoader

use of org.gradle.api.internal.classloading.GroovySystemLoader in project gradle by gradle.

the class InProcessWorkerFactory method executeInWorkerClassLoader.

private <T extends WorkSpec> DefaultWorkResult executeInWorkerClassLoader(WorkerAction<T> action, T spec, DaemonForkOptions forkOptions) {
    ClassLoader actionClasspathLoader = createActionClasspathLoader(forkOptions);
    GroovySystemLoader actionClasspathGroovy = groovySystemLoaderFactory.forClassLoader(actionClasspathLoader);
    ClassLoader workerClassLoader = createWorkerClassLoader(actionClasspathLoader, forkOptions.getSharedPackages(), action.getClass());
    ClassLoader previousContextLoader = Thread.currentThread().getContextClassLoader();
    try {
        Thread.currentThread().setContextClassLoader(workerClassLoader);
        Callable<?> worker = transferWorkerIntoWorkerClassloader(action, spec, workerClassLoader);
        Object result = worker.call();
        return transferResultFromWorkerClassLoader(result);
    } catch (Exception e) {
        throw UncheckedException.throwAsUncheckedException(e);
    } finally {
        // Eventually shutdown any leaky groovy runtime loaded from action classpath loader
        actionClasspathGroovy.shutdown();
        Thread.currentThread().setContextClassLoader(previousContextLoader);
    }
}
Also used : FilteringClassLoader(org.gradle.internal.classloader.FilteringClassLoader) VisitableURLClassLoader(org.gradle.internal.classloader.VisitableURLClassLoader) MultiParentClassLoader(org.gradle.internal.classloader.MultiParentClassLoader) CachingClassLoader(org.gradle.internal.classloader.CachingClassLoader) UncheckedException(org.gradle.internal.UncheckedException) IOException(java.io.IOException) GroovySystemLoader(org.gradle.api.internal.classloading.GroovySystemLoader)

Example 2 with GroovySystemLoader

use of org.gradle.api.internal.classloading.GroovySystemLoader in project gradle by gradle.

the class ClassPathToClassLoaderCache method withCachedClassLoader.

/**
 * Provides execution of arbitrary code that consumes a cached class loader in a memory safe manner,
 * that is to say making sure that concurrent calls reuse the same classloader, or that the class loader
 * is retrieved from cache if available.
 *
 * It will also make sure that once a cached class loader is unused and removed from cache, memory cleanup
 * is done.
 *
 * The action MUST be done on a CachedClassLoader, and not directly with the ClassLoader, in order for
 * a strong reference to be kept on the cached class loader while in use. If we don't do so there are risks
 * that the cached entry gets released by the GC before we've finished working with the classloader it
 * wraps!
 *
 * @param libClasspath the classpath for this classloader
 * @param gradleApiGroovy the Groovy system used by core Gradle API
 * @param antBuilderAdapterGroovy the Groovy system used by the Ant builder adapter
 * @param factory the factory to create a new class loader on cache miss
 * @param action the action to execute with the cached class loader
 */
public void withCachedClassLoader(ClassPath libClasspath, GroovySystemLoader gradleApiGroovy, GroovySystemLoader antBuilderAdapterGroovy, Factory<? extends ClassLoader> factory, Action<? super CachedClassLoader> action) {
    CachedClassLoader cachedClassLoader;
    lock.lock();
    try {
        CacheEntry cacheEntry = cacheEntries.get(libClasspath);
        cachedClassLoader = maybeGet(cacheEntry);
        if (cachedClassLoader == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Classloader cache miss for classpath : {}. Creating classloader.", libClasspath.getAsURIs());
            }
            // Lock is held while creating ClassLoader - nothing else can happen while this is running
            ClassLoader classLoader = factory.create();
            cachedClassLoader = new CachedClassLoader(libClasspath, classLoader);
            cacheEntry = new CacheEntry(libClasspath, cachedClassLoader);
            GroovySystemLoader groovySystemForLoader = groovySystemLoaderFactory.forClassLoader(classLoader);
            Cleanup cleanup = new Cleanup(libClasspath, cachedClassLoader, finalizerThread.getReferenceQueue(), classLoader, groovySystemForLoader, gradleApiGroovy, antBuilderAdapterGroovy);
            finalizerThread.putCleanup(libClasspath, cleanup);
            cacheEntries.put(libClasspath, cacheEntry);
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Classloader found in cache: {}", libClasspath.getAsURIs());
            }
        }
        // in order to make sure that the CacheEntry is not collected
        // while the cached class loader is still in use, we need to keep a strong reference onto
        // the cached class loader as long as the action is executed
        inUseClassLoaders.add(cachedClassLoader);
    } finally {
        lock.unlock();
    }
    try {
        action.execute(cachedClassLoader);
    } finally {
        lock.lock();
        try {
            inUseClassLoaders.remove(cachedClassLoader);
        } finally {
            lock.unlock();
        }
    }
}
Also used : GroovySystemLoader(org.gradle.api.internal.classloading.GroovySystemLoader)

Example 3 with GroovySystemLoader

use of org.gradle.api.internal.classloading.GroovySystemLoader in project gradle by gradle.

the class IsolatedClassloaderWorkerFactory method executeInWorkerClassLoader.

private DefaultWorkResult executeInWorkerClassLoader(ActionExecutionSpec spec, DaemonForkOptions forkOptions) {
    ClassLoader actionClasspathLoader = createActionClasspathLoader(forkOptions);
    GroovySystemLoader actionClasspathGroovy = groovySystemLoaderFactory.forClassLoader(actionClasspathLoader);
    ClassLoader workerClassLoader = createWorkerClassLoader(actionClasspathLoader, forkOptions.getSharedPackages(), spec.getClass());
    ClassLoader previousContextLoader = Thread.currentThread().getContextClassLoader();
    try {
        Thread.currentThread().setContextClassLoader(workerClassLoader);
        Callable<?> worker = transferWorkerIntoWorkerClassloader(spec, workerClassLoader);
        Object result = worker.call();
        return transferResultFromWorkerClassLoader(result);
    } catch (Exception e) {
        throw UncheckedException.throwAsUncheckedException(e);
    } finally {
        // Eventually shutdown any leaky groovy runtime loaded from action classpath loader
        actionClasspathGroovy.shutdown();
        Thread.currentThread().setContextClassLoader(previousContextLoader);
    }
}
Also used : FilteringClassLoader(org.gradle.internal.classloader.FilteringClassLoader) VisitableURLClassLoader(org.gradle.internal.classloader.VisitableURLClassLoader) MultiParentClassLoader(org.gradle.internal.classloader.MultiParentClassLoader) CachingClassLoader(org.gradle.internal.classloader.CachingClassLoader) UncheckedException(org.gradle.internal.UncheckedException) IOException(java.io.IOException) GroovySystemLoader(org.gradle.api.internal.classloading.GroovySystemLoader)

Example 4 with GroovySystemLoader

use of org.gradle.api.internal.classloading.GroovySystemLoader in project gradle by gradle.

the class IsolatedClassloaderWorker method run.

@Override
public DefaultWorkResult run(TransportableActionExecutionSpec spec) {
    GroovySystemLoader workerClasspathGroovy = groovySystemLoaderFactory.forClassLoader(workerClassLoader);
    try {
        return executeInClassLoader(spec, workerClassLoader);
    } finally {
        workerClasspathGroovy.shutdown();
        // TODO: we should just cache these classloaders and eject/stop them when they are no longer in use
        if (!reuseClassloader) {
            CompositeStoppable.stoppable(workerClassLoader).stop();
            this.workerClassLoader = null;
        }
    }
}
Also used : GroovySystemLoader(org.gradle.api.internal.classloading.GroovySystemLoader)

Example 5 with GroovySystemLoader

use of org.gradle.api.internal.classloading.GroovySystemLoader in project gradle by gradle.

the class ApiGroovyCompiler method execute.

@Override
public WorkResult execute(final GroovyJavaJointCompileSpec spec) {
    ApiCompilerResult result = new ApiCompilerResult();
    result.getAnnotationProcessingResult().setFullRebuildCause("Incremental annotation processing is not supported by Groovy.");
    GroovySystemLoaderFactory groovySystemLoaderFactory = new GroovySystemLoaderFactory();
    ClassLoader compilerClassLoader = this.getClass().getClassLoader();
    GroovySystemLoader compilerGroovyLoader = groovySystemLoaderFactory.forClassLoader(compilerClassLoader);
    CompilerConfiguration configuration = new CompilerConfiguration();
    configuration.setVerbose(spec.getGroovyCompileOptions().isVerbose());
    configuration.setSourceEncoding(spec.getGroovyCompileOptions().getEncoding());
    configuration.setTargetBytecode(spec.getTargetCompatibility());
    configuration.setTargetDirectory(spec.getDestinationDir());
    canonicalizeValues(spec.getGroovyCompileOptions().getOptimizationOptions());
    VersionNumber version = parseGroovyVersion();
    if (version.compareTo(VersionNumber.parse("2.5")) >= 0) {
        configuration.setParameters(spec.getGroovyCompileOptions().isParameters());
    } else if (spec.getGroovyCompileOptions().isParameters()) {
        throw new GradleException("Using Groovy compiler flag '--parameters' requires Groovy 2.5+ but found Groovy " + version);
    }
    IncrementalCompilationCustomizer customizer = IncrementalCompilationCustomizer.fromSpec(spec, result);
    customizer.addToConfiguration(configuration);
    if (spec.getGroovyCompileOptions().getConfigurationScript() != null) {
        applyConfigurationScript(spec.getGroovyCompileOptions().getConfigurationScript(), configuration);
    }
    try {
        configuration.setOptimizationOptions(spec.getGroovyCompileOptions().getOptimizationOptions());
    } catch (NoSuchMethodError ignored) {
    /* method was only introduced in Groovy 1.8 */
    }
    try {
        configuration.setDisabledGlobalASTTransformations(spec.getGroovyCompileOptions().getDisabledGlobalASTTransformations());
    } catch (NoSuchMethodError ignored) {
    /* method was only introduced in Groovy 2.0.0 */
    }
    Map<String, Object> jointCompilationOptions = new HashMap<String, Object>();
    final File stubDir = spec.getGroovyCompileOptions().getStubDir();
    stubDir.mkdirs();
    jointCompilationOptions.put("stubDir", stubDir);
    jointCompilationOptions.put("keepStubs", spec.getGroovyCompileOptions().isKeepStubs());
    configuration.setJointCompilationOptions(jointCompilationOptions);
    ClassLoader classPathLoader;
    if (version.compareTo(VersionNumber.parse("2.0")) < 0) {
        // using a transforming classloader is only required for older buggy Groovy versions
        classPathLoader = new GroovyCompileTransformingClassLoader(getExtClassLoader(), DefaultClassPath.of(spec.getCompileClasspath()));
    } else {
        classPathLoader = new DefaultClassLoaderFactory().createIsolatedClassLoader("api-groovy-compile-loader", DefaultClassPath.of(spec.getCompileClasspath()));
    }
    GroovyClassLoader compileClasspathClassLoader = new GroovyClassLoader(classPathLoader, null);
    GroovySystemLoader compileClasspathLoader = groovySystemLoaderFactory.forClassLoader(classPathLoader);
    FilteringClassLoader.Spec groovyCompilerClassLoaderSpec = new FilteringClassLoader.Spec();
    groovyCompilerClassLoaderSpec.allowPackage("org.codehaus.groovy");
    groovyCompilerClassLoaderSpec.allowPackage("groovy");
    groovyCompilerClassLoaderSpec.allowPackage("groovyjarjarasm");
    // Disallow classes from Groovy Jar that reference external classes. Such classes must be loaded from astTransformClassLoader,
    // or a NoClassDefFoundError will occur. Essentially this is drawing a line between the Groovy compiler and the Groovy
    // library, albeit only for selected classes that run a high risk of being statically referenced from a transform.
    groovyCompilerClassLoaderSpec.disallowClass("groovy.util.GroovyTestCase");
    groovyCompilerClassLoaderSpec.disallowClass("org.codehaus.groovy.transform.NotYetImplementedASTTransformation");
    groovyCompilerClassLoaderSpec.disallowPackage("groovy.servlet");
    FilteringClassLoader groovyCompilerClassLoader = new FilteringClassLoader(GroovyClassLoader.class.getClassLoader(), groovyCompilerClassLoaderSpec);
    // AST transforms need their own class loader that shares compiler classes with the compiler itself
    final GroovyClassLoader astTransformClassLoader = new GroovyClassLoader(groovyCompilerClassLoader, null);
    // where the transform class is loaded from)
    for (File file : spec.getCompileClasspath()) {
        astTransformClassLoader.addClasspath(file.getPath());
    }
    JavaAwareCompilationUnit unit = new JavaAwareCompilationUnit(configuration, compileClasspathClassLoader) {

        @Override
        public GroovyClassLoader getTransformLoader() {
            return astTransformClassLoader;
        }
    };
    final boolean shouldProcessAnnotations = shouldProcessAnnotations(spec);
    if (shouldProcessAnnotations) {
        // If an annotation processor is detected, we need to force Java stub generation, so the we can process annotations on Groovy classes
        // We are forcing stub generation by tricking the groovy compiler into thinking there are java files to compile.
        // All java files are just passed to the compile method of the JavaCompiler and aren't processed internally by the Groovy Compiler.
        // Since we're maintaining our own list of Java files independent of what's passed by the Groovy compiler, adding a non-existent java file
        // to the sources won't cause any issues.
        unit.addSources(new File[] { new File("ForceStubGeneration.java") });
    }
    unit.addSources(getSortedSourceFiles(spec));
    unit.setCompilerFactory(new JavaCompilerFactory() {

        @Override
        public JavaCompiler createCompiler(final CompilerConfiguration config) {
            return new JavaCompiler() {

                @Override
                public void compile(List<String> files, CompilationUnit cu) {
                    if (shouldProcessAnnotations) {
                        // In order for the Groovy stubs to have annotation processors invoked against them, they must be compiled as source.
                        // Classes compiled as a result of being on the -sourcepath do not have the annotation processor run against them
                        spec.setSourceFiles(Iterables.concat(spec.getSourceFiles(), projectLayout.files(stubDir).getAsFileTree()));
                    } else {
                        // When annotation processing isn't required, it's better to add the Groovy stubs as part of the source path.
                        // This allows compilations to complete faster, because only the Groovy stubs that are needed by the java source are compiled.
                        ImmutableList.Builder<File> sourcepathBuilder = ImmutableList.builder();
                        sourcepathBuilder.add(stubDir);
                        if (spec.getCompileOptions().getSourcepath() != null) {
                            sourcepathBuilder.addAll(spec.getCompileOptions().getSourcepath());
                        }
                        spec.getCompileOptions().setSourcepath(sourcepathBuilder.build());
                    }
                    spec.setSourceFiles(Iterables.filter(spec.getSourceFiles(), new Predicate<File>() {

                        @Override
                        public boolean apply(File file) {
                            return hasExtension(file, ".java");
                        }
                    }));
                    try {
                        WorkResult javaCompilerResult = javaCompiler.execute(spec);
                        if (javaCompilerResult instanceof ApiCompilerResult) {
                            result.getSourceClassesMapping().putAll(((ApiCompilerResult) javaCompilerResult).getSourceClassesMapping());
                        }
                    } catch (CompilationFailedException e) {
                        cu.getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(), cu));
                    }
                }
            };
        }
    });
    try {
        unit.compile();
        return result;
    } catch (org.codehaus.groovy.control.CompilationFailedException e) {
        System.err.println(e.getMessage());
        // Explicit flush, System.err is an auto-flushing PrintWriter unless it is replaced.
        System.err.flush();
        throw new CompilationFailedException();
    } finally {
        // Remove compile and AST types from the Groovy loader
        compilerGroovyLoader.discardTypesFrom(classPathLoader);
        compilerGroovyLoader.discardTypesFrom(astTransformClassLoader);
        // Discard the compile loader
        compileClasspathLoader.shutdown();
        CompositeStoppable.stoppable(classPathLoader, astTransformClassLoader).stop();
    }
}
Also used : HashMap(java.util.HashMap) JavaAwareCompilationUnit(org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit) GroovyClassLoader(groovy.lang.GroovyClassLoader) CompilerConfiguration(org.codehaus.groovy.control.CompilerConfiguration) FilteringClassLoader(org.gradle.internal.classloader.FilteringClassLoader) GroovyClassLoader(groovy.lang.GroovyClassLoader) GroovySystemLoader(org.gradle.api.internal.classloading.GroovySystemLoader) GroovySystemLoaderFactory(org.gradle.api.internal.classloading.GroovySystemLoaderFactory) DefaultClassLoaderFactory(org.gradle.internal.classloader.DefaultClassLoaderFactory) JavaAwareCompilationUnit(org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit) CompilationUnit(org.codehaus.groovy.control.CompilationUnit) SimpleMessage(org.codehaus.groovy.control.messages.SimpleMessage) JavaCompiler(org.codehaus.groovy.tools.javac.JavaCompiler) JavaCompilerFactory(org.codehaus.groovy.tools.javac.JavaCompilerFactory) VersionNumber(org.gradle.util.internal.VersionNumber) GradleException(org.gradle.api.GradleException) FilteringClassLoader(org.gradle.internal.classloader.FilteringClassLoader) WorkResult(org.gradle.api.tasks.WorkResult) File(java.io.File)

Aggregations

GroovySystemLoader (org.gradle.api.internal.classloading.GroovySystemLoader)5 FilteringClassLoader (org.gradle.internal.classloader.FilteringClassLoader)3 IOException (java.io.IOException)2 UncheckedException (org.gradle.internal.UncheckedException)2 CachingClassLoader (org.gradle.internal.classloader.CachingClassLoader)2 MultiParentClassLoader (org.gradle.internal.classloader.MultiParentClassLoader)2 VisitableURLClassLoader (org.gradle.internal.classloader.VisitableURLClassLoader)2 GroovyClassLoader (groovy.lang.GroovyClassLoader)1 File (java.io.File)1 HashMap (java.util.HashMap)1 CompilationUnit (org.codehaus.groovy.control.CompilationUnit)1 CompilerConfiguration (org.codehaus.groovy.control.CompilerConfiguration)1 SimpleMessage (org.codehaus.groovy.control.messages.SimpleMessage)1 JavaAwareCompilationUnit (org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit)1 JavaCompiler (org.codehaus.groovy.tools.javac.JavaCompiler)1 JavaCompilerFactory (org.codehaus.groovy.tools.javac.JavaCompilerFactory)1 GradleException (org.gradle.api.GradleException)1 GroovySystemLoaderFactory (org.gradle.api.internal.classloading.GroovySystemLoaderFactory)1 WorkResult (org.gradle.api.tasks.WorkResult)1 DefaultClassLoaderFactory (org.gradle.internal.classloader.DefaultClassLoaderFactory)1