Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
The microBean™ Construct project provides classes and interfaces assisting with Java constructs such as types and
elements.

Among other things, this project enables constructs modeled by the `javax.lang.model.*` packages to be accessed at
runtime in a thread-safe manner. This permits the same constructs to be used at build time (e.g. by annotation
processors) and at runtime (e.g. by tools and environments that need to perform reflection-like activities without
actually loading and initializing classes), using only official and sanctioned APIs.

# Status

This project is currently experimental, in a pre-alpha state, and unsuitable for production use.
Expand All @@ -20,8 +25,8 @@ microBean™ Construct requires a Java runtime of version 21 or higher.

# Installation

microBean™ Construct is, or will be, available on [Maven Central](https://search.maven.org/). Include microBean™ Construct
as a Maven dependency:
microBean™ Construct is available on [Maven Central](https://search.maven.org/). Include microBean™ Construct as a Maven
dependency:

```xml
<dependency>
Expand All @@ -31,11 +36,17 @@ as a Maven dependency:
Always check https://search.maven.org/artifact/org.microbean/microbean-construct
for up-to-date available versions.
-->
<version>0.0.18</version>
<version>0.0.19</version>
</dependency>
```

# Documentation

Full documentation is, or will be, available at
Full documentation is available at
[microbean.github.io/microbean-construct](https://microbean.github.io/microbean-construct/).

# References

* This project is tangentially and purely coincidentally related to [JEP 119](https://openjdk.org/jeps/119).
* A seemingly shelved attempt to implement the `javax.lang.model.*` constructs in terms of reflective constructs can be
found in [JDK-8004133](https://bugs.openjdk.org/browse/JDK-8004133).
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,20 @@ public final void close() {
}
}

/**
* Overrides {@link CompletableFuture#completeExceptionally(Throwable)} to additionally {@linkplain #close() close}
* this {@link BlockingCompilationTask}.
*
* @param t a {@link Throwable}; see {@link CompletableFuture#completeExceptionally(Throwable)} for additional
* contractual obligations
*
* @return the result of invoking {@link CompletableFuture#completeExceptionally(Throwable)} with the supplied {@link
* Throwable}
*
* @see #close()
*
* @see CompletableFuture#completeExceptionally(Throwbale)
*/
@Override // CompletableFuture<ProcessingEnvironment>
public final boolean completeExceptionally(final Throwable t) {
final boolean result = super.completeExceptionally(t);
Expand Down Expand Up @@ -185,8 +199,7 @@ public final void run() {
task.addModules(additionalRootModuleNames);

// Set the task's annotation processor whose only function will be to return the ProcessingEnvironment supplied to
// it in its #init(ProcessingEnvironment) method. The supplied latch is used to make this task block forever (unless
// an error occurs) to keep the ProcessingEnvironment "in scope".
// it in its #init(ProcessingEnvironment) method.
task.setProcessors(List.of(this.p));

if (LOGGER.isLoggable(DEBUG)) {
Expand All @@ -196,8 +209,8 @@ public final void run() {
}

try {
final Boolean result = task.call(); // blocks forever deliberately unless an error occurs; see Processor2
if (!TRUE.equals(result)) {
final Boolean result = task.call(); // blocks forever deliberately unless an error occurs; see Processor
if (!TRUE.equals(result)) { // (result could be null, technically speaking)
if (LOGGER.isLoggable(ERROR)) {
LOGGER.log(ERROR, "Calling CompilationTask failed");
}
Expand Down
80 changes: 74 additions & 6 deletions src/main/java/org/microbean/construct/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.lang.System.Logger;

import java.util.List;
import java.util.Objects;
import java.util.Set;

import java.util.concurrent.locks.Condition;
Expand All @@ -38,26 +37,55 @@

import static java.lang.System.getLogger;

import static java.util.Objects.requireNonNull;

final class Processor implements AutoCloseable, javax.annotation.processing.Processor {

private static final Logger LOGGER = getLogger(Processor.class.getName());

// @GuardedBy("lock")
private final Consumer<? super ProcessingEnvironment> cpe;

// @GuardedBy("lock")
// run() method invoked under lock
private final Runnable r;

private final Lock lock;

// @GuardedBy("lock")
private final Condition c;

// @GuardedBy("lock")
private boolean closed;

Processor(final Consumer<? super ProcessingEnvironment> cpe,
final Runnable r) {
/**
* Creates a new {@link Processor}.
*
* <p>This {@link Processor} will be invoked by a {@link BlockingCompilationTask} as part of an invocation of its
* {@link BlockingCompilationTask#run()} method. Its {@link #init(ProcessingEnvironment)} method will be invoked as
* part of {@linkplain javax.annotation.processing.Processor the standard <code>Processor</code> lifecycle}. It will
* call the {@link Consumer#accept(Object) accept(ProcessingEnvironment)} method on the supplied {@link
* Consumer}. Then it will block until {@link #close()} is invoked (from a separate thread, obviously). Before
* exiting, it will invoke the {@link Runnable#run() run()} method of the supplied {@link Runnable}.</p>
*
* @param cpe a {@link Consumer} of {@link ProcessingEnvironment} instances, typically {@link
* BlockingCompilationTask#complete(Object)}; must not be {@code null}
*
* @param r a {@link Runnable} that is invoked at the conclusion of an invocation of the {@link
* #init(ProcessingEnvironment)} method; may be {@code null}
*
* @see #init(ProcessingEnvironment)
*
* @see #close()
*
* @see BlockingCompilationTask
*
* @see javax.annotation.processing.Processor
*/
Processor(final Consumer<? super ProcessingEnvironment> cpe, // usually BlockingCompliationTask::complete
final Runnable r) { // usually BlockingCompliationTask::obtrudeException
super();
this.cpe = Objects.requireNonNull(cpe, "cpe");
this.cpe = requireNonNull(cpe, "cpe");
this.r = r == null ? Processor::sink : r;
this.lock = new ReentrantLock();
this.c = this.lock.newCondition();
Expand All @@ -83,12 +111,15 @@ public final void close() {
}

/**
* Initializes this {@link Processor}.
* Initializes this {@link Processor} by calling the {@link Consumer#accept(Object) accept(Object)} method on the
* {@link Consumer} {@linkplain #Processor(Consumer, Runnable) supplied at construction time} with the supplied {@link
* ProcessingEnvironment}, <strong>and then blocking until another thread invokes the {@link #close()}
* method</strong>.
*
* @param pe a {@link ProcessingEnvironment}; must not be {@code null}
*
* @deprecated This method should be called only by a Java compiler in accordance with annotation processing
* contracts.
* contracts. All other usage will result in undefined behavior.
*/
@Deprecated // to be called only by a Java compiler in accordance with annotation processing contracts
@Override // Processor;
Expand All @@ -108,6 +139,21 @@ public final void init(final ProcessingEnvironment pe) {
}
}

/**
* Returns an {@linkplain List#of() empty, immutable, determinate <code>List</code>} when invoked, regardless of
* arguments.
*
* @param element ignored; may be {@code null}
*
* @param annotation ignored; may be {@code null}
*
* @param member ignored; may be {@code null}
*
* @param userText ignored; may be {@code null}
*
* @return an {@linkplain List#of() empty, immutable, determinate <code>List</code>} when invoked, regardless of
* arguments
*/
@Override // Processor
public final Iterable<? extends Completion> getCompletions(final Element element,
final AnnotationMirror annotation,
Expand All @@ -116,21 +162,43 @@ public final Iterable<? extends Completion> getCompletions(final Element element
return List.of();
}

/**
* Returns an {@linkplain Set#of() empty, immutable, determinate <code>Set</code>} when invoked.
*
* @return an {@linkplain Set#of() empty, immutable, determinate <code>Set</code>} when invoked
*/
@Override // Processor
public final Set<String> getSupportedAnnotationTypes() {
return Set.of();
}

/**
* Returns an {@linkplain Set#of() empty, immutable, determinate <code>Set</code>} when invoked.
*
* @return an {@linkplain Set#of() empty, immutable, determinate <code>Set</code>} when invoked
*/
@Override // Processor
public final Set<String> getSupportedOptions() {
return Set.of();
}

/**
* Returns the return value of an invocation of the {@link SourceVersion#latestSupported()} method when invoked.
*
* @return the return value of an invocation of the {@link SourceVersion#latestSupported()} method when invoked
*/
@Override // Processor
public final SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}

/**
* Returns {@code false} when invoked, regardless of arguments.
*
* @param annotations ignored; may be {@code null}
*
* @param roundEnvironment ignored; may be {@code null}
*/
@Override // Processor
public final boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnvironment) {
return false;
Expand Down
Loading