001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2025 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.construct; 015 016import java.lang.System.Logger; 017 018import java.util.concurrent.CompletableFuture; 019import java.util.concurrent.RunnableFuture; 020 021import java.util.concurrent.atomic.AtomicReference; 022 023import java.util.function.Consumer; 024import java.util.function.Supplier; 025 026import javax.annotation.processing.ProcessingEnvironment; 027 028import static java.lang.System.getLogger; 029 030import static java.lang.System.Logger.Level.ERROR; 031 032/** 033 * A utility class that can {@linkplain #of() supply} a {@link ProcessingEnvironment} suitable for use at runtime. 034 * 035 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 036 * 037 * @see #get() 038 * 039 * @see #of() 040 * 041 * @see #close() 042 * 043 * @see ProcessingEnvironment 044 * 045 * @see Domain 046 */ 047public final class RuntimeProcessingEnvironmentSupplier implements AutoCloseable, Supplier<ProcessingEnvironment> { 048 049 050 /* 051 * Static fields. 052 */ 053 054 055 private static final Logger LOGGER = getLogger(RuntimeProcessingEnvironmentSupplier.class.getName()); 056 057 private static final RuntimeProcessingEnvironmentSupplier INSTANCE = new RuntimeProcessingEnvironmentSupplier(); 058 059 060 /* 061 * Instance fields. 062 */ 063 064 065 private final AtomicReference<BlockingCompilationTask> r; 066 067 068 /* 069 * Constructors. 070 */ 071 072 073 private RuntimeProcessingEnvironmentSupplier() { 074 super(); 075 this.r = new AtomicReference<>(); 076 install(this.r::set); 077 } 078 079 080 /* 081 * Instance methods. 082 */ 083 084 085 /** 086 * Closes this {@link RuntimeProcessingEnvironmentSupplier}, which invalidates all {@link ProcessingEnvironment}s 087 * {@linkplain #get() supplied} by it. 088 * 089 * <p>A subsequent call to {@link #get()} will reset this {@link RuntimeProcessingEnvironmentSupplier}.</p> 090 * 091 * <p>Closing a {@link RuntimeProcessingEnvironmentSupplier} that has already been closed has no effect.</p> 092 * 093 * <p>Most users should not have a need to call this method. {@link RuntimeProcessingEnvironmentSupplier} instances do 094 * not have to be closed.</p> 095 * 096 * @see #get() 097 */ 098 @Override // AutoCloseable 099 public final void close() { 100 this.r.get().cancel(true); 101 } 102 103 /** 104 * Returns a non-{@code null}, {@link ProcessingEnvironment} suitable for runtime use. 105 * 106 * <p>{@link ProcessingEnvironment} instances are not guaranteed to be thread-safe.</p> 107 * 108 * @return a non-{@code null} {@link ProcessingEnvironment} 109 * 110 * @see Domain 111 */ 112 @Override // Supplier<ProcessingEnvironment> 113 public final ProcessingEnvironment get() { 114 final BlockingCompilationTask f = this.r.get(); 115 return 116 (f.isCompletedExceptionally() && f.exceptionNow() instanceof ClosedProcessorException ? install(this.r::set) : f) 117 .join(); 118 } 119 120 121 /* 122 * Static methods. 123 */ 124 125 126 private static final BlockingCompilationTask install(final Consumer<? super BlockingCompilationTask> c) { 127 final BlockingCompilationTask f = new BlockingCompilationTask(); 128 c.accept(f); 129 Thread.ofVirtual() 130 .name(RuntimeProcessingEnvironmentSupplier.class.getName()) 131 .uncaughtExceptionHandler((thread, exception) -> { 132 f.completeExceptionally(exception); 133 if (LOGGER.isLoggable(ERROR)) { 134 LOGGER.log(ERROR, exception.getMessage(), exception); 135 } 136 }) 137 .start(f); 138 return f; 139 } 140 141 /** 142 * Returns a non-{@code null} {@link RuntimeProcessingEnvironmentSupplier}. 143 * 144 * @return a non-{@code null} {@link RuntimeProcessingEnvironmentSupplier} 145 * 146 * @see ProcessingEnvironment 147 * 148 * @see Domain 149 */ 150 public static final RuntimeProcessingEnvironmentSupplier of() { 151 return INSTANCE; 152 } 153 154}