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.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 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 033 * 034 * @see #get() 035 * 036 * @see #of() 037 * 038 * @see #close() 039 * 040 * @see ProcessingEnvironment 041 * 042 * @see Domain 043 */ 044public final class RuntimeProcessingEnvironmentSupplier implements AutoCloseable, Supplier<ProcessingEnvironment> { 045 046 047 /* 048 * Static fields. 049 */ 050 051 052 private static final Logger LOGGER = getLogger(RuntimeProcessingEnvironmentSupplier.class.getName()); 053 054 private static final RuntimeProcessingEnvironmentSupplier INSTANCE = new RuntimeProcessingEnvironmentSupplier(); 055 056 057 /* 058 * Instance fields. 059 */ 060 061 062 private final AtomicReference<BlockingCompilationTask> r; 063 064 065 /* 066 * Constructors. 067 */ 068 069 070 private RuntimeProcessingEnvironmentSupplier() { 071 super(); 072 this.r = new AtomicReference<>(); 073 install(this.r::set); 074 } 075 076 077 /* 078 * Instance methods. 079 */ 080 081 082 /** 083 * Closes this {@link RuntimeProcessingEnvironmentSupplier}, <strong>which invalidates all {@link 084 * ProcessingEnvironment}s {@linkplain #get() supplied} by it</strong>. 085 * 086 * <p>A subsequent call to {@link #get()} will reset this {@link RuntimeProcessingEnvironmentSupplier}.</p> 087 * 088 * <p>Closing a {@link RuntimeProcessingEnvironmentSupplier} that has already been closed has no effect.</p> 089 * 090 * <p>Most users should not have a need to call this method. {@link RuntimeProcessingEnvironmentSupplier} instances do 091 * not have to be closed.</p> 092 * 093 * @see #get() 094 */ 095 @Override // AutoCloseable 096 public final void close() { 097 this.r.get().cancel(true); 098 } 099 100 /** 101 * Returns a non-{@code null}, {@link ProcessingEnvironment} suitable for runtime use. 102 * 103 * <p><strong>{@link ProcessingEnvironment} instances are not guaranteed to be safe for concurrent use by multiple 104 * threads.</strong></p> 105 * 106 * @return a non-{@code null} {@link ProcessingEnvironment} 107 * 108 * @see Domain 109 */ 110 @Override // Supplier<ProcessingEnvironment> 111 public final ProcessingEnvironment get() { 112 final BlockingCompilationTask f = this.r.get(); 113 return 114 (f.isCompletedExceptionally() && f.exceptionNow() instanceof ClosedProcessorException ? install(this.r::set) : f) 115 .join(); 116 } 117 118 119 /* 120 * Static methods. 121 */ 122 123 124 private static final BlockingCompilationTask install(final Consumer<? super BlockingCompilationTask> c) { 125 final BlockingCompilationTask f = new BlockingCompilationTask(); 126 c.accept(f); 127 Thread.ofVirtual() 128 .name(RuntimeProcessingEnvironmentSupplier.class.getName()) 129 .uncaughtExceptionHandler((thread, exception) -> { 130 f.completeExceptionally(exception); 131 if (LOGGER.isLoggable(ERROR)) { 132 LOGGER.log(ERROR, exception.getMessage(), exception); 133 } 134 }) 135 .start(f); 136 return f; 137 } 138 139 /** 140 * Returns a non-{@code null} {@link RuntimeProcessingEnvironmentSupplier}. 141 * 142 * @return a non-{@code null} {@link RuntimeProcessingEnvironmentSupplier} 143 * 144 * @see ProcessingEnvironment 145 * 146 * @see Domain 147 */ 148 public static final RuntimeProcessingEnvironmentSupplier of() { 149 return INSTANCE; 150 } 151 152}