Add code contribution infrastructure
This commit adds an API that lets individual components contribute code, runtime hints, and protected access information. This ease the cases where code need to be written in a privileged package if necessary and let contributors provide hints for the code they generate. Closes gh-28030
This commit is contained in:
parent
d64f8c1a05
commit
1a4573641d
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.javapoet.support.MultiStatement;
|
||||
|
||||
/**
|
||||
* A code contribution that gathers the code, the {@linkplain RuntimeHints
|
||||
* runtime hints}, and the {@linkplain ProtectedElement protected elements}
|
||||
* that are necessary to execute it.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
public interface CodeContribution {
|
||||
|
||||
/**
|
||||
* Return the {@linkplain MultiStatement statements} that can be used to
|
||||
* append code.
|
||||
* @return the statements instance to use to contribute code
|
||||
*/
|
||||
MultiStatement statements();
|
||||
|
||||
/**
|
||||
* Return the {@linkplain RuntimeHints hints} to use to register
|
||||
* potential optimizations for contributed code.
|
||||
* @return the runtime hints
|
||||
*/
|
||||
RuntimeHints runtimeHints();
|
||||
|
||||
/**
|
||||
* Return the {@linkplain ProtectedAccess protected access} to use to
|
||||
* analyze any privileged access, if necessary.
|
||||
* @return the protected access
|
||||
*/
|
||||
ProtectedAccess protectedAccess();
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.javapoet.support.MultiStatement;
|
||||
|
||||
/**
|
||||
* A default {@link CodeContribution} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
public class DefaultCodeContribution implements CodeContribution {
|
||||
|
||||
private final MultiStatement statements;
|
||||
|
||||
private final RuntimeHints runtimeHints;
|
||||
|
||||
private final ProtectedAccess protectedAccess;
|
||||
|
||||
|
||||
protected DefaultCodeContribution(MultiStatement statements, RuntimeHints runtimeHints,
|
||||
ProtectedAccess protectedAccess) {
|
||||
|
||||
this.statements = statements;
|
||||
this.runtimeHints = runtimeHints;
|
||||
this.protectedAccess = protectedAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance with the {@link RuntimeHints} instance to use.
|
||||
* @param runtimeHints the runtime hints instance to use
|
||||
*/
|
||||
public DefaultCodeContribution(RuntimeHints runtimeHints) {
|
||||
this(new MultiStatement(), runtimeHints, new ProtectedAccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiStatement statements() {
|
||||
return this.statements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeHints runtimeHints() {
|
||||
return this.runtimeHints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtectedAccess protectedAccess() {
|
||||
return this.protectedAccess;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Gather the need of non-public access and determine the priviledged package
|
||||
* to use, if necessary.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ProtectedAccess {
|
||||
|
||||
private final List<ProtectedElement> elements;
|
||||
|
||||
public ProtectedAccess() {
|
||||
this.elements = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the protected elements registered in this instance are
|
||||
* accessible from the specified package name.
|
||||
* @param packageName the target package name
|
||||
* @return {@code true} if the registered access can be safely used from
|
||||
* the specified package name
|
||||
*/
|
||||
public boolean isAccessible(String packageName) {
|
||||
return getProtectedElements(packageName).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the privileged package name to use for the specified package
|
||||
* name, or {@code null} if none is required.
|
||||
* @param packageName the target package name to use
|
||||
* @return the privileged package name to use, or {@code null}
|
||||
* @throws ProtectedAccessException if a single privileged package cannot
|
||||
* be identified
|
||||
* @see #isAccessible(String)
|
||||
*/
|
||||
@Nullable
|
||||
public String getPrivilegedPackageName(String packageName) throws ProtectedAccessException {
|
||||
List<ProtectedElement> protectedElements = getProtectedElements(packageName);
|
||||
if (protectedElements.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<String> packageNames = protectedElements.stream()
|
||||
.map(element -> element.getType().toClass().getPackageName())
|
||||
.distinct().toList();
|
||||
if (packageNames.size() == 1) {
|
||||
return packageNames.get(0);
|
||||
}
|
||||
throw new ProtectedAccessException("Multiple packages require a privileged access: "
|
||||
+ packageNames, protectedElements);
|
||||
}
|
||||
|
||||
private List<ProtectedElement> getProtectedElements(String packageName) {
|
||||
List<ProtectedElement> matches = new ArrayList<>();
|
||||
for (ProtectedElement element : this.elements) {
|
||||
if (!element.getType().toClass().getPackage().getName().equals(packageName)) {
|
||||
matches.add(element);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze the specified {@linkplain ResolvableType type}, including its
|
||||
* full type signature.
|
||||
* @param type the type to analyze
|
||||
*/
|
||||
public void analyze(ResolvableType type) {
|
||||
if (isProtected(type)) {
|
||||
registerProtectedType(type, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze accessing the specified {@link Member} using the default
|
||||
* {@linkplain Options#DEFAULTS options}.
|
||||
* @param member the member to analyze
|
||||
*/
|
||||
public void analyze(Member member) {
|
||||
analyze(member, Options.DEFAULTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze accessing the specified {@link Member} using the specified
|
||||
* {@link Options options}.
|
||||
* @param member the member to analyze
|
||||
* @param options the options to use
|
||||
*/
|
||||
public void analyze(Member member, Options options) {
|
||||
if (isProtected(member.getDeclaringClass())) {
|
||||
registerProtectedType(member.getDeclaringClass(), member);
|
||||
}
|
||||
if (!options.useReflection && isProtected(member.getModifiers())) {
|
||||
registerProtectedType(member.getDeclaringClass(), member);
|
||||
}
|
||||
if (member instanceof Field field) {
|
||||
ResolvableType fieldType = ResolvableType.forField(field);
|
||||
if (options.assignReturnType && isProtected(fieldType)) {
|
||||
registerProtectedType(fieldType, field);
|
||||
}
|
||||
}
|
||||
else if (member instanceof Constructor<?> constructor) {
|
||||
analyzeParameterTypes(constructor, i ->
|
||||
ResolvableType.forConstructorParameter(constructor, i));
|
||||
}
|
||||
else if (member instanceof Method method) {
|
||||
ResolvableType returnType = ResolvableType.forMethodReturnType(method);
|
||||
if (!options.assignReturnType && isProtected(returnType)) {
|
||||
registerProtectedType(returnType, method);
|
||||
}
|
||||
analyzeParameterTypes(method, i -> ResolvableType.forMethodParameter(method, i));
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeParameterTypes(Executable executable, Function<Integer,
|
||||
ResolvableType> parameterTypeFactory) {
|
||||
|
||||
for (int i = 0; i < executable.getParameters().length; i++) {
|
||||
ResolvableType parameterType = parameterTypeFactory.apply(i);
|
||||
if (isProtected(parameterType)) {
|
||||
registerProtectedType(parameterType, executable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isProtected(ResolvableType resolvableType) {
|
||||
return isProtected(new HashSet<>(), resolvableType);
|
||||
}
|
||||
|
||||
private boolean isProtected(Set<ResolvableType> seen, ResolvableType target) {
|
||||
if (seen.contains(target)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(target);
|
||||
ResolvableType nonProxyTarget = target.as(ClassUtils.getUserClass(target.toClass()));
|
||||
if (isProtected(nonProxyTarget.toClass())) {
|
||||
return true;
|
||||
}
|
||||
Class<?> declaringClass = nonProxyTarget.toClass().getDeclaringClass();
|
||||
if (declaringClass != null) {
|
||||
if (isProtected(declaringClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (nonProxyTarget.hasGenerics()) {
|
||||
for (ResolvableType generic : nonProxyTarget.getGenerics()) {
|
||||
return isProtected(seen, generic);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isProtected(Class<?> type) {
|
||||
Class<?> candidate = ClassUtils.getUserClass(type);
|
||||
return isProtected(candidate.getModifiers());
|
||||
}
|
||||
|
||||
private boolean isProtected(int modifiers) {
|
||||
return !Modifier.isPublic(modifiers);
|
||||
}
|
||||
|
||||
private void registerProtectedType(ResolvableType type, @Nullable Member member) {
|
||||
this.elements.add(ProtectedElement.of(type, member));
|
||||
}
|
||||
|
||||
private void registerProtectedType(Class<?> type, Member member) {
|
||||
registerProtectedType(ResolvableType.forClass(type), member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options to use to analyze if invoking a {@link Member} requires
|
||||
* privileged access.
|
||||
*/
|
||||
public static class Options {
|
||||
|
||||
/**
|
||||
* Default options that does fallback to reflection and does not
|
||||
* assign the default type.
|
||||
*/
|
||||
public static final Options DEFAULTS = new Options();
|
||||
|
||||
private final boolean useReflection;
|
||||
|
||||
private final boolean assignReturnType;
|
||||
|
||||
/**
|
||||
* Create a new instance with the specified options.
|
||||
* @param useReflection whether the writer can automatically use
|
||||
* reflection to invoke a protected member if it is not public
|
||||
* @param assignReturnType whether the writer needs to assign the
|
||||
* return type, or if it is irrelevant
|
||||
*/
|
||||
public Options(boolean useReflection, boolean assignReturnType) {
|
||||
this.useReflection = useReflection;
|
||||
this.assignReturnType = assignReturnType;
|
||||
}
|
||||
|
||||
private Options() {
|
||||
this(true, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Thrown when a code block requires privileged access on multiple packages.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ProtectedAccessException extends RuntimeException {
|
||||
|
||||
private final List<ProtectedElement> protectedElements;
|
||||
|
||||
public ProtectedAccessException(String message, List<ProtectedElement> protectedElements) {
|
||||
super(message);
|
||||
this.protectedElements = protectedElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@linkplain ProtectedElement protected elements}.
|
||||
* @return the protected access
|
||||
*/
|
||||
public List<ProtectedElement> getProtectedElements() {
|
||||
return this.protectedElements;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link Member} that is non-public, with the related type.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ProtectedElement {
|
||||
|
||||
private final ResolvableType type;
|
||||
|
||||
@Nullable
|
||||
private final Member target;
|
||||
|
||||
|
||||
private ProtectedElement(ResolvableType type, @Nullable Member member) {
|
||||
this.type = type;
|
||||
this.target = member;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ResolvableType type} that is non-public. For a plain
|
||||
* protected {@link Member member} access, the type of the declaring class
|
||||
* is used. Otherwise, the type in the member signature, such as a parameter
|
||||
* type for an executable, or the return type of a field is used.
|
||||
* @return the type that is not public
|
||||
*/
|
||||
public ResolvableType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Member} that is not publicly accessible.
|
||||
* @return the member
|
||||
*/
|
||||
@Nullable
|
||||
public Member getMember() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
static ProtectedElement of(ResolvableType type, @Nullable Member member) {
|
||||
return new ProtectedElement(type, member);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Support classes for components that contribute generated code equivalent
|
||||
* to a runtime behavior.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultCodeContribution}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DefaultCodeContributionTests {
|
||||
|
||||
@Test
|
||||
void newCodeContributionIsEmpty() {
|
||||
CodeContribution contribution = new DefaultCodeContribution(new RuntimeHints());
|
||||
assertThat(contribution.statements().isEmpty()).isTrue();
|
||||
assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void codeContributionReusesRuntimeHints() {
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
CodeContribution contribution = new DefaultCodeContribution(runtimeHints);
|
||||
assertThat(contribution.runtimeHints()).isSameAs(runtimeHints);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.generator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generator.ProtectedAccess.Options;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.testfixture.aot.generator.visibility.ProtectedParameter;
|
||||
import org.springframework.core.testfixture.aot.generator.visibility.PublicFactoryBean;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ProtectedAccess}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProtectedAccessTests {
|
||||
|
||||
private final ProtectedAccess protectedAccess = new ProtectedAccess();
|
||||
|
||||
@Test
|
||||
void analyzeWithPublicConstructor() throws NoSuchMethodException {
|
||||
this.protectedAccess.analyze(PublicClass.class.getConstructor());
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateConstructorAndDefaultOptions() {
|
||||
this.protectedAccess.analyze(ProtectedAccessor.class.getDeclaredConstructors()[0]);
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateConstructorAndReflectionDisabled() {
|
||||
this.protectedAccess.analyze(ProtectedAccessor.class.getDeclaredConstructors()[0],
|
||||
new Options(false, true));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedAccessor.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedAccessor.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateClass() {
|
||||
this.protectedAccess.analyze(ProtectedClass.class.getDeclaredConstructors()[0]);
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedClass.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedClass.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateDeclaringType() {
|
||||
this.protectedAccess.analyze(method(ProtectedClass.class, "stringBean"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedClass.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedClass.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateConstructorParameter() {
|
||||
this.protectedAccess.analyze(ProtectedParameter.class.getConstructors()[0]);
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedParameter.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedParameter.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateMethod() {
|
||||
this.protectedAccess.analyze(method(PublicClass.class, "getProtectedMethod"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateMethodAndReflectionDisabled() {
|
||||
this.protectedAccess.analyze(method(PublicClass.class, "getProtectedMethod"),
|
||||
new Options(false, false));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateMethodReturnType() {
|
||||
this.protectedAccess.analyze(method(ProtectedAccessor.class, "methodWithProtectedReturnType"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedAccessor.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedAccessor.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateMethodParameter() {
|
||||
this.protectedAccess.analyze(method(ProtectedAccessor.class, "methodWithProtectedParameter",
|
||||
ProtectedClass.class));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedClass.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedClass.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateField() {
|
||||
this.protectedAccess.analyze(field(PublicClass.class, "protectedField"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateFieldAndReflectionDisabled() {
|
||||
this.protectedAccess.analyze(field(PublicClass.class, "protectedField"),
|
||||
new Options(false, true));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(PublicClass.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(PublicClass.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPublicFieldAndProtectedType() {
|
||||
this.protectedAccess.analyze(field(PublicClass.class, "protectedClassField"),
|
||||
new Options(false, true));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedClass.class.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(ProtectedClass.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithPackagePrivateGenericArgument() {
|
||||
this.protectedAccess.analyze(method(PublicFactoryBean.class, "protectedTypeFactoryBean"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.isAccessible(PublicFactoryBean.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeTypeWithProtectedGenericArgument() {
|
||||
this.protectedAccess.analyze(PublicFactoryBean.resolveToProtectedGenericParameter());
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.isAccessible(PublicFactoryBean.class.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWithRecursiveType() {
|
||||
assertThat(this.protectedAccess.isProtected(ResolvableType.forClassWithGenerics(
|
||||
SelfReference.class, SelfReference.class))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedPackageWithPublicAccess() throws NoSuchMethodException {
|
||||
this.protectedAccess.analyze(PublicClass.class.getConstructor());
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedPackageWithProtectedAccessInOnePackage() {
|
||||
this.protectedAccess.analyze(method(PublicFactoryBean.class, "protectedTypeFactoryBean"));
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(PublicFactoryBean.class.getPackageName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedPackageWithProtectedAccessInSeveralPackages() {
|
||||
Method protectedMethodFirstPackage = method(PublicFactoryBean.class, "protectedTypeFactoryBean");
|
||||
Method protectedMethodSecondPackage = method(ProtectedAccessor.class, "methodWithProtectedParameter",
|
||||
ProtectedClass.class);
|
||||
this.protectedAccess.analyze(protectedMethodFirstPackage);
|
||||
this.protectedAccess.analyze(protectedMethodSecondPackage);
|
||||
assertThatThrownBy(() -> this.protectedAccess.getPrivilegedPackageName("com.example"))
|
||||
.isInstanceOfSatisfying(ProtectedAccessException.class, ex ->
|
||||
assertThat(ex.getProtectedElements().stream().map(ProtectedElement::getMember))
|
||||
.containsOnly(protectedMethodFirstPackage, protectedMethodSecondPackage));
|
||||
}
|
||||
|
||||
private static Method method(Class<?> type, String name, Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(type, name, parameterTypes);
|
||||
assertThat(method).isNotNull();
|
||||
return method;
|
||||
}
|
||||
|
||||
private static Field field(Class<?> type, String name) {
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class PublicClass {
|
||||
|
||||
String protectedField;
|
||||
|
||||
public ProtectedClass protectedClassField;
|
||||
|
||||
String getProtectedMethod() {
|
||||
return this.protectedField;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class ProtectedAccessor {
|
||||
|
||||
ProtectedAccessor() {
|
||||
}
|
||||
|
||||
public String methodWithProtectedParameter(ProtectedClass type) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
public ProtectedClass methodWithProtectedReturnType() {
|
||||
return new ProtectedClass();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class ProtectedClass {
|
||||
|
||||
public ProtectedClass() {
|
||||
}
|
||||
|
||||
public String stringBean() {
|
||||
return "public";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SelfReference<T extends SelfReference<T>> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T getThis() {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.testfixture.aot.generator.visibility;
|
||||
|
||||
public class ProtectedParameter {
|
||||
|
||||
public ProtectedParameter(ProtectedType type) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.testfixture.aot.generator.visibility;
|
||||
|
||||
class ProtectedType {
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.testfixture.aot.generator.visibility;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
public class PublicFactoryBean<T> {
|
||||
|
||||
private final Class<T> type;
|
||||
|
||||
public PublicFactoryBean(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static PublicFactoryBean<ProtectedType> protectedTypeFactoryBean() {
|
||||
return new PublicFactoryBean<>(ProtectedType.class);
|
||||
}
|
||||
|
||||
public static ResolvableType resolveToProtectedGenericParameter() {
|
||||
return ResolvableType.forClassWithGenerics(PublicFactoryBean.class, ProtectedType.class);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue