001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2026 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.atomic.AtomicReference; 019 020import java.util.function.Consumer; 021import java.util.function.Supplier; 022 023import javax.annotation.processing.ProcessingEnvironment; 024 025import static java.lang.System.getLogger; 026 027import static java.lang.System.Logger.Level.ERROR; 028 029/** 030 * A utility class that can {@linkplain #of() supply} a {@link ProcessingEnvironment} suitable for use at runtime. 031 * 032 * <p>Most users should simply {@linkplain DefaultDomain#DefaultDomain() use an appropriate <code>DefaultDomain</code>} 033 * instead of working directly with instances of this class.</p> 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 DefaultDomain#DefaultDomain() 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}, <strong>which invalidates all {@link 087 * ProcessingEnvironment}s {@linkplain #get() supplied} by it</strong>. 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><strong>{@link ProcessingEnvironment} instances are not guaranteed to be safe for concurrent use by multiple 107 * threads.</strong></p> 108 * 109 * <p>Most users should simply use an appropriate {@link DefaultDomain} instead of working directly with instances of 110 * this class.</p> 111 * 112 * @return a non-{@code null} {@link ProcessingEnvironment} 113 * 114 * @see DefaultDomain#DefaultDomain() 115 */ 116 @Override // Supplier<ProcessingEnvironment> 117 public final ProcessingEnvironment get() { 118 final BlockingCompilationTask f = this.r.get(); 119 return 120 (f.isCompletedExceptionally() && f.exceptionNow() instanceof ClosedProcessorException ? install(this.r::set) : f) 121 .join(); 122 } 123 124 125 /* 126 * Static methods. 127 */ 128 129 130 private static final BlockingCompilationTask install(final Consumer<? super BlockingCompilationTask> c) { 131 final BlockingCompilationTask f = new BlockingCompilationTask(); 132 c.accept(f); 133 Thread.ofVirtual() 134 .name(RuntimeProcessingEnvironmentSupplier.class.getName()) 135 .uncaughtExceptionHandler((thread, exception) -> { 136 f.completeExceptionally(exception); 137 if (LOGGER.isLoggable(ERROR)) { 138 LOGGER.log(ERROR, exception.getMessage(), exception); 139 } 140 }) 141 .start(f); 142 return f; 143 } 144 145 /** 146 * Returns a non-{@code null} {@link RuntimeProcessingEnvironmentSupplier}. 147 * 148 * <p>Most users should simply use an appropriate {@link DefaultDomain} instead of working directly with instances of 149 * this class.</p> 150 * 151 * @return a non-{@code null} {@link RuntimeProcessingEnvironmentSupplier} 152 * 153 * @see ProcessingEnvironment 154 * 155 * @see DefaultDomain#DefaultDomain() 156 */ 157 public static final RuntimeProcessingEnvironmentSupplier of() { 158 return INSTANCE; 159 } 160 161}