diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/AbstractContainerGroupAssert.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/AbstractContainerGroupAssert.java index 229c88cc5..80b90c819 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/AbstractContainerGroupAssert.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/AbstractContainerGroupAssert.java @@ -52,15 +52,6 @@ protected AbstractContainerGroupAssert(C containerGroup, Class selfType) { super(containerGroup, selfType); } - /** - * Get assertions to perform on the class loader associated with this container group. - * - * @return the assertions to perform. - */ - public ClassLoaderAssert classLoader() { - return new ClassLoaderAssert(actual.getClassLoader()); - } - /** * Get assertions to perform on the location of this container group. * diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/PackageContainerGroupAssert.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/PackageContainerGroupAssert.java index 0f8a54743..964838724 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/PackageContainerGroupAssert.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/assertions/PackageContainerGroupAssert.java @@ -58,7 +58,7 @@ public PackageContainerGroupAssert(PackageContainerGroup containerGroup) { * @throws AssertionError if the file exists. */ public PackageContainerGroupAssert fileDoesNotExist(String path) { - var file = actual.findFile(path); + var file = actual.getFile(path); if (file == null) { return this; @@ -95,6 +95,15 @@ public void allFilesExist(Iterable paths) { assertThat(paths).allSatisfy(this::fileExists); } + /** + * Get assertions to perform on the class loader associated with this container group. + * + * @return the assertions to perform. + */ + public ClassLoaderAssert classLoader() { + return new ClassLoaderAssert(actual.getClassLoader()); + } + /** * Assert that the given file exists. * @@ -103,7 +112,7 @@ public void allFilesExist(Iterable paths) { * @throws AssertionError if the file does not exist. */ public AbstractPathAssert fileExists(String path) { - var file = actual.findFile(path); + var file = actual.getFile(path); if (file != null) { return assertThat(file); diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/compilers/impl/JctFileManagerImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/compilers/impl/JctFileManagerImpl.java index 2b240f4a9..f47831aef 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/compilers/impl/JctFileManagerImpl.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/compilers/impl/JctFileManagerImpl.java @@ -28,7 +28,6 @@ import io.github.ascopes.jct.containers.impl.OutputContainerGroupImpl; import io.github.ascopes.jct.containers.impl.PackageContainerGroupImpl; import io.github.ascopes.jct.pathwrappers.PathWrapper; -import io.github.ascopes.jct.utils.GarbageDisposalUtils; import io.github.ascopes.jct.utils.ToStringBuilder; import java.io.IOException; import java.lang.module.ModuleFinder; diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/ContainerGroup.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/ContainerGroup.java index d5cab499b..c2d17446c 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/ContainerGroup.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/ContainerGroup.java @@ -42,16 +42,6 @@ public interface ContainerGroup extends Closeable { */ boolean contains(PathFileObject fileObject); - /** - * Get a class loader for this group of containers. - * - *

Note that adding additional containers to this group after accessing this class loader - * may result in the class loader being destroyed or re-created. - * - * @return the class loader. - */ - ClassLoader getClassLoader(); - /** * Get the location of this container group. * diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java index 3b4b5d4a4..7414eb7d9 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java @@ -29,8 +29,7 @@ * @since 0.0.1 */ @API(since = "0.0.1", status = Status.EXPERIMENTAL) -public interface OutputContainerGroup - extends PackageContainerGroup, ModuleContainerGroup { +public interface OutputContainerGroup extends PackageContainerGroup, ModuleContainerGroup { /** * Get the output-oriented location. diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/PackageContainerGroup.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/PackageContainerGroup.java index a00a1fbfd..0cb2bb0c9 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/PackageContainerGroup.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/PackageContainerGroup.java @@ -66,6 +66,16 @@ public interface PackageContainerGroup extends ContainerGroup { */ void addPackage(@WillNotClose PathWrapper path); + /** + * Get a class loader for this group of containers. + * + *

Note that adding additional containers to this group after accessing this class loader + * may result in the class loader being destroyed or re-created. + * + * @return the class loader. + */ + ClassLoader getClassLoader(); + /** * Find the first occurrence of a given path to a file in packages or modules. * @@ -75,7 +85,7 @@ public interface PackageContainerGroup extends ContainerGroup { * @return the first occurrence of the path in this group, or null if not found. */ @Nullable - Path findFile(String path); + Path getFile(String path); /** * Get a {@link FileObject} that can have content read from it. diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/AbstractPackageContainerGroup.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/AbstractPackageContainerGroup.java index b3cd126a6..bc526ee2f 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/AbstractPackageContainerGroup.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/AbstractPackageContainerGroup.java @@ -15,6 +15,7 @@ */ package io.github.ascopes.jct.containers.impl; +import static java.util.Collections.synchronizedSet; import static java.util.Objects.requireNonNull; import io.github.ascopes.jct.compilers.PathFileObject; @@ -67,7 +68,7 @@ public abstract class AbstractPackageContainerGroup implements PackageContainerG // Use a linked hash set here to deduplicate while retaining order. This is an optimisation // since defining the same path more than once does not make sense anyway. - protected final LinkedHashSet containers; + protected final Set containers; protected final Lazy classLoaderLazy; /** @@ -80,7 +81,7 @@ protected AbstractPackageContainerGroup(Location location, String release) { this.location = requireNonNull(location, "location"); this.release = requireNonNull(release, "release"); - containers = new LinkedHashSet<>(); + containers = synchronizedSet(new LinkedHashSet<>()); classLoaderLazy = new Lazy<>(this::createClassLoader); } @@ -149,7 +150,7 @@ public void close() throws IOException { @Override @Nullable - public Path findFile(String path) { + public Path getFile(String path) { for (var container : containers) { var result = container.getFile(path); if (result != null) { diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupUrlClassLoader.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupUrlClassLoader.java deleted file mode 100644 index c3894675d..000000000 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupUrlClassLoader.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2022 - 2022 Ashley Scopes - * - * 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 io.github.ascopes.jct.containers.impl; - -import io.github.ascopes.jct.containers.Container; -import io.github.ascopes.jct.containers.ModuleContainerGroup; -import io.github.ascopes.jct.containers.OutputContainerGroup; -import io.github.ascopes.jct.containers.PackageContainerGroup; -import io.github.ascopes.jct.pathwrappers.PathWrapper; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Collection; -import java.util.stream.Stream; -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; - -/** - * An extension of the Java {@link URLClassLoader} that wraps around container groups. - * - * @author Ashley Scopes - * @since 0.0.1 - */ -@API(since = "0.0.1", status = Status.INTERNAL) -public final class ContainerGroupUrlClassLoader extends URLClassLoader { - - private ContainerGroupUrlClassLoader(String name, URL[] urls) { - super(name, urls, ClassLoader.getSystemClassLoader()); - } - - /** - * Create a class loader for a given package container group. - * - * @param group the package container group to wrap. - * @return the class loader. - */ - public static ContainerGroupUrlClassLoader createClassLoaderFor(PackageContainerGroup group) { - return new ContainerGroupUrlClassLoader( - "Packages within package group " + group.getLocation().getName(), - urlsForPackageGroup(group) - .distinct() - .toArray(URL[]::new) - ); - } - - /** - * Create a class loader for a given module container group. - * - * @param group the module container group to wrap. - * @return the class loader. - */ - public static ContainerGroupUrlClassLoader createClassLoaderFor(ModuleContainerGroup group) { - return new ContainerGroupUrlClassLoader( - "Packages within module group " + group.getLocation().getName(), - urlsForModuleGroup(group) - .distinct() - .toArray(URL[]::new) - ); - } - - /** - * Create a class loader for a given output container group. - * - * @param group the output container group to wrap. - * @return the class loader. - */ - public static ContainerGroupUrlClassLoader createClassLoaderFor(OutputContainerGroup group) { - return new ContainerGroupUrlClassLoader( - "Packages within output group " + group.getLocation().getName(), - Stream.concat(urlsForPackageGroup(group), urlsForModuleGroup(group)) - .distinct() - .toArray(URL[]::new) - ); - } - - private static Stream urlsForPackageGroup(PackageContainerGroup group) { - return group - .getPackages() - .stream() - .map(Container::getPathWrapper) - .map(PathWrapper::getUrl); - } - - private static Stream urlsForModuleGroup(ModuleContainerGroup group) { - return group - .getModules() - .values() - .stream() - .map(PackageContainerGroup::getPackages) - .flatMap(Collection::stream) - .map(Container::getPathWrapper) - .map(PathWrapper::getUrl); - } -} diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ModuleContainerGroupImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ModuleContainerGroupImpl.java index 68e457cf1..9a227be81 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ModuleContainerGroupImpl.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ModuleContainerGroupImpl.java @@ -23,18 +23,18 @@ import io.github.ascopes.jct.containers.ModuleContainerGroup; import io.github.ascopes.jct.containers.PackageContainerGroup; import io.github.ascopes.jct.pathwrappers.PathWrapper; -import io.github.ascopes.jct.utils.Lazy; import io.github.ascopes.jct.utils.StringUtils; import io.github.ascopes.jct.utils.ToStringBuilder; import java.io.IOException; import java.lang.module.ModuleFinder; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; import javax.annotation.WillCloseWhenClosed; import javax.tools.JavaFileManager.Location; import org.apiguardian.api.API; @@ -52,7 +52,6 @@ public final class ModuleContainerGroupImpl implements ModuleContainerGroup { private final Location location; private final Map modules; private final String release; - private final Lazy classLoaderLazy; /** * Initialize this container group. @@ -82,8 +81,7 @@ public ModuleContainerGroupImpl(Location location, String release) { ); } - modules = new HashMap<>(); - classLoaderLazy = new Lazy<>(this::createClassLoader); + modules = new ConcurrentHashMap<>(); } @Override @@ -96,21 +94,6 @@ public void addModule(String module, PathWrapper path) { getOrCreateModule(module).addPackage(path); } - @Override - public PackageContainerGroup getModule(String module) { - if (module.isEmpty()) { - throw new IllegalArgumentException("Cannot have module sources with no valid module name"); - } - - return modules - .keySet() - .stream() - .filter(location -> location.getModuleName().equals(module)) - .findFirst() - .map(modules::get) - .orElse(null); - } - @Override public void close() throws IOException { var exceptions = new ArrayList(); @@ -143,11 +126,6 @@ public boolean contains(PathFileObject fileObject) { return false; } - @Override - public ClassLoader getClassLoader() { - return classLoaderLazy.access(); - } - @Override public Location getLocation() { return location; @@ -158,14 +136,24 @@ public List> getLocationsForModules() { return List.of(Set.copyOf(modules.keySet())); } + @Nullable + @Override + public PackageContainerGroup getModule(String name) { + if (name.isEmpty()) { + throw new IllegalArgumentException("Cannot have module sources with no valid module name"); + } + + return modules.get(new ModuleLocation(location, name)); + } + @Override public Map getModules() { return Map.copyOf(modules); } @Override - public boolean hasLocation(ModuleLocation location) { - return modules.containsKey(location); + public PackageContainerGroup getOrCreateModule(String moduleName) { + return modules.computeIfAbsent(new ModuleLocation(location, moduleName), this::newPackageGroup); } @Override @@ -193,8 +181,8 @@ public ServiceLoader getServiceLoader(Class service) { } @Override - public PackageContainerGroup getOrCreateModule(String moduleName) { - return modules.computeIfAbsent(new ModuleLocation(location, moduleName), this::newPackageGroup); + public boolean hasLocation(ModuleLocation location) { + return modules.containsKey(location); } @Override @@ -205,10 +193,6 @@ public String toString() { .toString(); } - private ClassLoader createClassLoader() { - return ContainerGroupUrlClassLoader.createClassLoaderFor(this); - } - private ModulePackageContainerGroupImpl newPackageGroup(ModuleLocation location) { return new ModulePackageContainerGroupImpl(location, release); } @@ -226,7 +210,7 @@ private ModulePackageContainerGroupImpl(ModuleLocation location, String release) @Override protected ClassLoader createClassLoader() { - return ContainerGroupUrlClassLoader.createClassLoaderFor(this); + return new PackageContainerGroupUrlClassLoader(this); } } } diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java index 04252a2b7..dce20e4ee 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java @@ -48,7 +48,7 @@ * @since 0.0.1 */ @API(since = "0.0.1", status = Status.INTERNAL) -public class OutputContainerGroupImpl +public final class OutputContainerGroupImpl extends AbstractPackageContainerGroup implements OutputContainerGroup { @@ -123,7 +123,7 @@ public PathFileObject getFileForInput(String packageName, String relativeName) { var moduleHandle = ModuleHandle.tryExtract(packageName); if (moduleHandle != null) { - var module = getOrCreateModule(moduleHandle.getModuleName()); + var module = getModule(moduleHandle.getModuleName()); if (module != null) { var file = module.getFileForInput(moduleHandle.getRest(), relativeName); @@ -144,13 +144,10 @@ public PathFileObject getFileForOutput(String packageName, String relativeName) if (moduleHandle != null) { var module = getOrCreateModule(moduleHandle.getModuleName()); + var file = module.getFileForOutput(moduleHandle.getRest(), relativeName); - if (module != null) { - var file = module.getFileForOutput(moduleHandle.getRest(), relativeName); - - if (file != null) { - return file; - } + if (file != null) { + return file; } } @@ -163,7 +160,7 @@ public PathFileObject getJavaFileForInput(String className, Kind kind) { var moduleHandle = ModuleHandle.tryExtract(className); if (moduleHandle != null) { - var module = getOrCreateModule(moduleHandle.getModuleName()); + var module = getModule(moduleHandle.getModuleName()); if (module != null) { var file = module.getJavaFileForInput(moduleHandle.getRest(), kind); @@ -184,13 +181,10 @@ public PathFileObject getJavaFileForOutput(String className, Kind kind) { if (moduleHandle != null) { var module = getOrCreateModule(moduleHandle.getModuleName()); + var file = module.getJavaFileForOutput(moduleHandle.getRest(), kind); - if (module != null) { - var file = module.getJavaFileForOutput(moduleHandle.getRest(), kind); - - if (file != null) { - return file; - } + if (file != null) { + return file; } } @@ -224,7 +218,7 @@ public boolean hasLocation(ModuleLocation location) { @Override protected ClassLoader createClassLoader() { - return ContainerGroupUrlClassLoader.createClassLoaderFor(this); + return new PackageContainerGroupUrlClassLoader(this); } @SuppressWarnings("resource") @@ -258,7 +252,7 @@ private OutputPackageContainerGroupImpl(Location location, String release) { @Override protected ClassLoader createClassLoader() { - return ContainerGroupUrlClassLoader.createClassLoaderFor(this); + return new PackageContainerGroupUrlClassLoader(this); } } } diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupImpl.java index 6d76f5092..93ba0e40b 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupImpl.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupImpl.java @@ -31,7 +31,7 @@ * @since 0.0.1 */ @API(since = "0.0.1", status = Status.INTERNAL) -public class PackageContainerGroupImpl extends AbstractPackageContainerGroup { +public final class PackageContainerGroupImpl extends AbstractPackageContainerGroup { /** * Initialize this group. @@ -62,6 +62,6 @@ public PackageContainerGroupImpl(Location location, String release) { @Override protected ClassLoader createClassLoader() { - return ContainerGroupUrlClassLoader.createClassLoaderFor(this); + return new PackageContainerGroupUrlClassLoader(this); } } diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupUrlClassLoader.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupUrlClassLoader.java new file mode 100644 index 000000000..fa7b58571 --- /dev/null +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PackageContainerGroupUrlClassLoader.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * 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 io.github.ascopes.jct.containers.impl; + +import io.github.ascopes.jct.containers.Container; +import io.github.ascopes.jct.containers.PackageContainerGroup; +import io.github.ascopes.jct.pathwrappers.PathWrapper; +import java.net.URL; +import java.net.URLClassLoader; +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +/** + * An extension of the Java {@link URLClassLoader} that wraps around container groups. + * + * @author Ashley Scopes + * @since 0.0.1 + */ +@API(since = "0.0.1", status = Status.INTERNAL) +public final class PackageContainerGroupUrlClassLoader extends URLClassLoader { + + /** + * Initialise this class loader. + * + * @param group the container group to use. + */ + public PackageContainerGroupUrlClassLoader(PackageContainerGroup group) { + super( + "Packages within " + group.getLocation().getName(), + group + .getPackages() + .stream() + .map(Container::getPathWrapper) + .map(PathWrapper::getUrl) + .distinct() + .toArray(URL[]::new), + ClassLoader.getSystemClassLoader() + ); + } +} diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PathWrappingContainerImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PathWrappingContainerImpl.java index 0f2e359d9..2ad9ccc2f 100644 --- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PathWrappingContainerImpl.java +++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/PathWrappingContainerImpl.java @@ -48,7 +48,7 @@ * @since 0.0.1 */ @API(since = "0.0.1", status = Status.INTERNAL) -public class PathWrappingContainerImpl implements Container { +public final class PathWrappingContainerImpl implements Container { private static final Logger LOGGER = LoggerFactory.getLogger(PathWrappingContainerImpl.class);