Skip to content

Commit 7de1ffd

Browse files
Guard auditing runtime hints.
See: #2497
1 parent 453f879 commit 7de1ffd

File tree

4 files changed

+179
-7
lines changed

4 files changed

+179
-7
lines changed

Diff for: spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java renamed to spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* @author Christoph Strobl
3232
* @since 3.0
3333
*/
34-
public class DataJpaRuntimeHints implements RuntimeHintsRegistrar {
34+
public class JpaRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
3535

3636
@Override
3737
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
@@ -46,12 +46,12 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader)
4646
hints.reflection().registerType(
4747
TypeReference.of("org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"), hint -> hint
4848
.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
49-
}
5049

51-
hints.reflection().registerTypes(Arrays.asList( //
52-
TypeReference.of(AuditingBeanFactoryPostProcessor.class), //
53-
TypeReference.of(AuditingEntityListener.class)),
54-
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
50+
hints.reflection().registerTypes(Arrays.asList( //
51+
TypeReference.of(AuditingBeanFactoryPostProcessor.class), //
52+
TypeReference.of(AuditingEntityListener.class)),
53+
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
54+
}
5555

5656
hints.reflection().registerType(TypeReference.of(SimpleJpaRepository.class),
5757
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS));
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
org.springframework.aot.hint.RuntimeHintsRegistrar=\
2-
org.springframework.data.jpa.aot.DataJpaRuntimeHints
2+
org.springframework.data.jpa.aot.JpaRuntimeHintsRegistrar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.aot;
17+
18+
import static org.assertj.core.api.AssertionsForClassTypes.*;
19+
import static org.springframework.aot.hint.RuntimeHintsPredicates.*;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.springframework.aot.hint.RuntimeHints;
23+
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
24+
import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor;
25+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
26+
import org.springframework.data.jpa.util.HidingClassLoader;
27+
28+
/**
29+
* @author Christoph Strobl
30+
*/
31+
class JpaRuntimeHintsRegistrarUnitTests {
32+
33+
@Test // GH-2497
34+
void registersAuditing() {
35+
36+
RuntimeHints hints = new RuntimeHints();
37+
38+
JpaRuntimeHintsRegistrar registrar = new JpaRuntimeHintsRegistrar();
39+
registrar.registerHints(hints, null);
40+
41+
assertThat(hints).matches(reflection().onType(AnnotationBeanConfigurerAspect.class))
42+
.matches(reflection().onType(AuditingEntityListener.class))
43+
.matches(reflection().onType(AuditingBeanFactoryPostProcessor.class));
44+
}
45+
46+
@Test // GH-2497
47+
void skipsAuditingHintsIfAspectjNotPresent() {
48+
49+
RuntimeHints hints = new RuntimeHints();
50+
51+
JpaRuntimeHintsRegistrar registrar = new JpaRuntimeHintsRegistrar();
52+
registrar.registerHints(hints, HidingClassLoader.hidePackages("org.springframework.beans.factory.aspectj"));
53+
54+
assertThat(hints).matches(reflection().onType(AnnotationBeanConfigurerAspect.class).negate())
55+
.matches(reflection().onType(AuditingEntityListener.class).negate())
56+
.matches(reflection().onType(AuditingBeanFactoryPostProcessor.class).negate());
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2017-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.util;
17+
18+
import java.net.URLClassLoader;
19+
import java.util.Arrays;
20+
import java.util.Collection;
21+
import java.util.stream.Collectors;
22+
23+
import org.springframework.instrument.classloading.ShadowingClassLoader;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* is intended for testing code that depends on the presence/absence of certain classes. Classes can be:
28+
* <ul>
29+
* <li>shadowed: reloaded by this classloader no matter if they are loaded already by the SystemClassLoader</li>
30+
* <li>hidden: not loaded by this classloader no matter if they are loaded already by the SystemClassLoader. Trying to
31+
* load these classes results in a {@link ClassNotFoundException}</li>
32+
* <li>all other classes get loaded by the SystemClassLoader</li>
33+
* </ul>
34+
*
35+
* @author Jens Schauder
36+
* @author Oliver Gierke
37+
* @author Christoph Strobl
38+
*/
39+
public class HidingClassLoader extends ShadowingClassLoader {
40+
41+
private final Collection<String> hidden;
42+
43+
HidingClassLoader(Collection<String> hidden) {
44+
45+
super(URLClassLoader.getSystemClassLoader(), false);
46+
47+
this.hidden = hidden;
48+
}
49+
50+
/**
51+
* Creates a new {@link HidingClassLoader} with the packages of the given classes hidden.
52+
*
53+
* @param packages must not be {@literal null}.
54+
* @return
55+
*/
56+
public static HidingClassLoader hide(Class<?>... packages) {
57+
58+
Assert.notNull(packages, "Packages must not be null");
59+
60+
return new HidingClassLoader(Arrays.stream(packages)//
61+
.map(it -> it.getPackage().getName())//
62+
.collect(Collectors.toList()));
63+
}
64+
65+
/**
66+
* Creates a new {@link HidingClassLoader} with the packages of the given classes hidden.
67+
*
68+
* @param packages must not be {@literal null}.
69+
* @return
70+
*/
71+
public static HidingClassLoader hidePackages(String... packages) {
72+
73+
Assert.notNull(packages, "Packages must not be null");
74+
75+
return new HidingClassLoader(Arrays.asList(packages));
76+
}
77+
78+
public static HidingClassLoader hideTypes(Class<?>... types) {
79+
80+
Assert.notNull(types, "Types must not be null!");
81+
82+
return new HidingClassLoader(Arrays.stream(types)//
83+
.map(it -> it.getName())//
84+
.collect(Collectors.toList()));
85+
}
86+
87+
@Override
88+
public Class<?> loadClass(String name) throws ClassNotFoundException {
89+
90+
Class<?> loaded = super.loadClass(name);
91+
checkIfHidden(loaded);
92+
return loaded;
93+
}
94+
95+
@Override
96+
protected boolean isEligibleForShadowing(String className) {
97+
return isExcluded(className);
98+
}
99+
100+
@Override
101+
protected Class<?> findClass(String name) throws ClassNotFoundException {
102+
103+
Class<?> loaded = super.findClass(name);
104+
checkIfHidden(loaded);
105+
return loaded;
106+
}
107+
108+
private void checkIfHidden(Class<?> type) throws ClassNotFoundException {
109+
110+
if (hidden.stream().anyMatch(it -> type.getName().startsWith(it))) {
111+
throw new ClassNotFoundException();
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)