001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.inject; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022 023import org.apache.juneau.commons.reflect.*; 024 025/** 026 * FQN-based support for JSR-style annotation detection. 027 * 028 * <p> 029 * Centralizes all framework annotation-name matching for: 030 * <ul> 031 * <li>JSR-330 style injection annotations (<c>jakarta.inject</c> / <c>javax.inject</c>). 032 * <li>JSR-250 lifecycle annotations (<c>jakarta.annotation</c> / <c>javax.annotation</c>). 033 * <li>Spring's <c>Autowired</c> annotation. 034 * </ul> 035 * 036 * <p> 037 * Juneau intentionally matches by annotation class name so modules can interoperate with these 038 * annotations without compile-time dependencies on their APIs. 039 */ 040public final class JsrSupport { 041 042 // JSR-330 / Spring. 043 public static final String JUNEAU_INJECT = "org.apache.juneau.commons.inject.Inject"; 044 public static final String JAKARTA_INJECT = "jakarta.inject.Inject"; 045 public static final String JAVAX_INJECT = "javax.inject.Inject"; 046 public static final String SPRING_AUTOWIRED = "org.springframework.beans.factory.annotation.Autowired"; 047 public static final String JUNEAU_NAMED = "org.apache.juneau.commons.inject.Named"; 048 public static final String JAKARTA_NAMED = "jakarta.inject.Named"; 049 public static final String JAVAX_NAMED = "javax.inject.Named"; 050 public static final String JUNEAU_QUALIFIER = "org.apache.juneau.commons.inject.Qualifier"; 051 public static final String JAKARTA_QUALIFIER = "jakarta.inject.Qualifier"; 052 public static final String JAVAX_QUALIFIER = "javax.inject.Qualifier"; 053 public static final String JUNEAU_SINGLETON = "org.apache.juneau.commons.inject.Singleton"; 054 public static final String JAKARTA_SINGLETON = "jakarta.inject.Singleton"; 055 public static final String JAVAX_SINGLETON = "javax.inject.Singleton"; 056 public static final String JUNEAU_PROVIDER = "org.apache.juneau.commons.inject.Provider"; 057 public static final String JAKARTA_PROVIDER = "jakarta.inject.Provider"; 058 public static final String JAVAX_PROVIDER = "javax.inject.Provider"; 059 060 // JSR-250 lifecycle. 061 public static final String JUNEAU_POSTCONSTRUCT = "org.apache.juneau.commons.inject.PostConstruct"; 062 public static final String JAKARTA_POSTCONSTRUCT = "jakarta.annotation.PostConstruct"; 063 public static final String JAVAX_POSTCONSTRUCT = "javax.annotation.PostConstruct"; 064 public static final String JUNEAU_PREDESTROY = "org.apache.juneau.commons.inject.PreDestroy"; 065 public static final String JAKARTA_PREDESTROY = "jakarta.annotation.PreDestroy"; 066 public static final String JAVAX_PREDESTROY = "javax.annotation.PreDestroy"; 067 068 // Config-value injection (Juneau + Spring). Detected by FQN so juneau-commons stays free of a 069 // compile-time Spring dependency, mirroring the SPRING_AUTOWIRED trick above. 070 public static final String JUNEAU_VALUE = "org.apache.juneau.commons.inject.Value"; 071 public static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value"; 072 073 // Jakarta Bean Validation 3.x (and the older javax. namespace). Detected by FQN so juneau-commons 074 // stays free of a compile-time jakarta.validation dependency; the opt-in marker is recognized whether 075 // the consumer pulls in jakarta.validation-api 3.x, javax.validation:validation-api 2.x, or Spring's 076 // own @Validated. 077 public static final String JAKARTA_VALID = "jakarta.validation.Valid"; 078 public static final String JAVAX_VALID = "javax.validation.Valid"; 079 public static final String SPRING_VALIDATED = "org.springframework.validation.annotation.Validated"; 080 081 private JsrSupport() {} 082 083 /** 084 * Returns <jk>true</jk> if the annotation is an inject marker. 085 * 086 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 087 * @return <jk>true</jk> if the annotation is an inject marker. 088 */ 089 public static boolean isInjectAnnotation(AnnotationInfo<?> annotation) { 090 var name = annotation.getName(); 091 return eqa(name, JUNEAU_INJECT, JAKARTA_INJECT, JAVAX_INJECT, SPRING_AUTOWIRED); 092 } 093 094 /** 095 * Returns <jk>true</jk> if the annotation is a named qualifier. 096 * 097 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 098 * @return <jk>true</jk> if the annotation is a named qualifier. 099 */ 100 public static boolean isNamedAnnotation(AnnotationInfo<?> annotation) { 101 var name = annotation.getName(); 102 return eqa(name, JUNEAU_NAMED, JAKARTA_NAMED, JAVAX_NAMED); 103 } 104 105 /** 106 * Returns <jk>true</jk> if this annotation type itself is annotated with a qualifier marker. 107 * 108 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 109 * @return <jk>true</jk> if this annotation type itself is annotated with a qualifier marker. 110 */ 111 public static boolean isQualifierMeta(AnnotationInfo<?> annotation) { 112 return Arrays.stream(annotation.annotationType().getAnnotations()) 113 .map(a -> a.annotationType().getName()) 114 .anyMatch(n -> eqa(n, JUNEAU_QUALIFIER, JAKARTA_QUALIFIER, JAVAX_QUALIFIER)); 115 } 116 117 /** 118 * Returns <jk>true</jk> if the annotation is a singleton marker. 119 * 120 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 121 * @return <jk>true</jk> if the annotation is a singleton marker. 122 */ 123 public static boolean isSingletonAnnotation(AnnotationInfo<?> annotation) { 124 var name = annotation.getName(); 125 return eqa(name, JUNEAU_SINGLETON, JAKARTA_SINGLETON, JAVAX_SINGLETON); 126 } 127 128 /** 129 * Returns <jk>true</jk> if the class is a supported provider interface. 130 * 131 * @param type The class to inspect. Can be <jk>null</jk>, in which case <jk>false</jk> is returned. 132 * @return <jk>true</jk> if the class is a supported provider interface. 133 */ 134 public static boolean isProviderType(Class<?> type) { 135 return type != null && eqa(type.getName(), JUNEAU_PROVIDER, JAKARTA_PROVIDER, JAVAX_PROVIDER); 136 } 137 138 /** 139 * Returns <jk>true</jk> if the annotation is a post-construct lifecycle marker. 140 * 141 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 142 * @return <jk>true</jk> if the annotation is a post-construct lifecycle marker. 143 */ 144 public static boolean isPostConstructAnnotation(AnnotationInfo<?> annotation) { 145 var name = annotation.getName(); 146 return eqa(name, JUNEAU_POSTCONSTRUCT, JAKARTA_POSTCONSTRUCT, JAVAX_POSTCONSTRUCT); 147 } 148 149 /** 150 * Returns <jk>true</jk> if the annotation is a pre-destroy lifecycle marker. 151 * 152 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 153 * @return <jk>true</jk> if the annotation is a pre-destroy lifecycle marker. 154 */ 155 public static boolean isPreDestroyAnnotation(AnnotationInfo<?> annotation) { 156 var name = annotation.getName(); 157 return eqa(name, JUNEAU_PREDESTROY, JAKARTA_PREDESTROY, JAVAX_PREDESTROY); 158 } 159 160 /** 161 * Reads qualifier value from an annotation when supported. 162 * 163 * <p> 164 * For <c>@Named</c>, returns the annotation value directly. For qualifier-meta annotations, 165 * this returns the annotation's own <c>value()</c> attribute if present and string-typed. 166 * 167 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 168 * @return The qualifier value, or <jk>null</jk> if the annotation carries no qualifier value. 169 */ 170 public static String qualifierValue(AnnotationInfo<?> annotation) { 171 if (isNamedAnnotation(annotation)) 172 return annotation.getValue().orElse(null); 173 if (isQualifierMeta(annotation)) 174 return annotation.getValue().orElse(null); 175 return null; 176 } 177 178 /** 179 * Returns <jk>true</jk> if the annotation is a config-value marker. 180 * 181 * <p> 182 * Recognizes both Juneau's {@code @Value} and Spring's 183 * {@code org.springframework.beans.factory.annotation.Value} by FQN — no compile-time Spring 184 * dependency in {@code juneau-commons}. 185 * 186 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 187 * @return <jk>true</jk> if {@code annotation} is one of the recognized {@code @Value} variants. 188 */ 189 public static boolean isValueAnnotation(AnnotationInfo<?> annotation) { 190 var name = annotation.getName(); 191 return eqa(name, JUNEAU_VALUE, SPRING_VALUE); 192 } 193 194 /** 195 * Returns the configuration expression carried by a {@code @Value} annotation, or {@code null} 196 * if the annotation is not a recognized {@code @Value}. 197 * 198 * <p> 199 * Both Juneau's {@code @Value} and Spring's {@code @Value} expose a single {@code String value()} 200 * attribute, so a uniform {@code getValue()} lookup against the annotation works for either. 201 * 202 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 203 * @return The {@code value()} string, or {@code null} if {@code annotation} is not a {@code @Value}. 204 */ 205 public static String valueExpression(AnnotationInfo<?> annotation) { 206 if (! isValueAnnotation(annotation)) 207 return null; 208 return annotation.getValue().orElse(null); 209 } 210 211 /** 212 * Returns <jk>true</jk> if the annotation is a Jakarta Bean Validation opt-in marker. 213 * 214 * <p> 215 * Recognizes Jakarta 3.x ({@code jakarta.validation.Valid}), the older Javax 2.x equivalent 216 * ({@code javax.validation.Valid}), and Spring's {@code @Validated} group-selector by FQN — no 217 * compile-time Jakarta Validation or Spring dependency in {@code juneau-commons}. 218 * 219 * @param annotation The annotation to inspect. Must not be <jk>null</jk>. 220 * @return <jk>true</jk> if {@code annotation} is one of the recognized validation opt-in markers. 221 */ 222 public static boolean isValidAnnotation(AnnotationInfo<?> annotation) { 223 var name = annotation.getName(); 224 return eqa(name, JAKARTA_VALID, JAVAX_VALID, SPRING_VALIDATED); 225 } 226}