Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit f556a23

Browse files
committed
new controls for property build time evaluation
1 parent a8e8855 commit f556a23

File tree

7 files changed

+470
-62
lines changed

7 files changed

+470
-62
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2020 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+
* http://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.boot;
17+
18+
import java.net.URL;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.springframework.graalvm.extension.SpringFactoriesProcessor;
23+
24+
/**
25+
* If using actuators purely for a health endpoint, there is a high memory cost to pay
26+
* for micrometer related infrastructure that is created that is not needed for that.
27+
*/
28+
public class ActuatorMetricsEliminator implements SpringFactoriesProcessor {
29+
30+
private final static boolean active;
31+
32+
static {
33+
String value = System.getProperty("spring.native.factories.no-actuator-metrics","false");
34+
active = Boolean.valueOf(value.equalsIgnoreCase("true"));
35+
if (active) {
36+
System.out.println("ActuatorMetricsEliminator: active");
37+
}
38+
}
39+
40+
@Override
41+
public boolean filter(URL springFactoryResource, String key, List<String> values) {
42+
if (!active) {
43+
return false;
44+
}
45+
if (key.equals(SpringFactoriesProcessor.enableAutoConfigurationKey)) {
46+
List<String> toRemove = new ArrayList<>();
47+
for (String value: values) {
48+
if (value.startsWith("org.springframework.boot.actuate.autoconfigure.metrics.")) {
49+
toRemove.add(value);
50+
}
51+
}
52+
if (toRemove.size()>0) {
53+
System.out.println("ActuatorMetricsEliminator: removing "+toRemove.size()+" configurations from "+enableAutoConfigurationKey);
54+
values.removeAll(toRemove);
55+
return true;
56+
}
57+
}
58+
return false;
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
org.springframework.boot.BackgroundPreInitializerDeactivator
1+
org.springframework.boot.BackgroundPreInitializerDeactivator
2+
org.springframework.boot.ActuatorMetricsEliminator

spring-graalvm-native-feature/src/main/java/org/springframework/graalvm/extension/SpringFactoriesProcessor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
public interface SpringFactoriesProcessor {
2626

27+
public String enableAutoConfigurationKey = "org.springframework.boot.autoconfigure.EnableAutoConfiguration";
2728
public String applicationListenerKey = "org.springframework.context.ApplicationListener";
2829

2930
boolean filter(URL springFactoriesResource, String key, List<String> values);

spring-graalvm-native-feature/src/main/java/org/springframework/graalvm/support/ConfigOptions.java

Lines changed: 118 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.io.IOException;
1919
import java.io.InputStream;
20+
import java.util.ArrayList;
21+
import java.util.List;
2022
import java.util.Map;
2123

2224
import org.graalvm.nativeimage.hosted.Feature.DuringSetupAccess;
@@ -35,6 +37,12 @@
3537
public abstract class ConfigOptions {
3638

3739
private final static boolean IGNORE_HINTS_ON_EXCLUDED_CONFIG;
40+
41+
private static List<String> BUILD_TIME_PROPERTIES_CHECKS;
42+
43+
private static List<String> IGNORED_TYPES;
44+
45+
private final static boolean BUILD_TIME_PROPERTIES_MATCH_IF_MISSING;
3846

3947
private final static boolean REMOVE_UNUSED_AUTOCONFIG;
4048

@@ -46,8 +54,6 @@ public abstract class ConfigOptions {
4654

4755
private final static boolean REMOVE_YAML_SUPPORT;
4856

49-
private final static boolean EVALUATE_COP;
50-
5157
private final static String DUMP_CONFIG;
5258

5359
private final static boolean VERBOSE;
@@ -69,6 +75,34 @@ public abstract class ConfigOptions {
6975
private static Boolean SPRING_INIT_ACTIVE = null;
7076

7177
static {
78+
String ignoredTypes = System.getProperty("spring.native.ignore-types");
79+
if (ignoredTypes != null) {
80+
String[] splits = ignoredTypes.split(",");
81+
if (splits != null) {
82+
IGNORED_TYPES = new ArrayList<>();
83+
for (String split: splits) {
84+
IGNORED_TYPES.add(split);
85+
}
86+
}
87+
}
88+
String propChecks = System.getProperty("spring.native.build-time-properties-checks");
89+
if (propChecks != null) {
90+
// default-include-all/default-exclude-all and then a series of patterns
91+
String[] splits = propChecks.split(",");
92+
if (splits != null) {
93+
BUILD_TIME_PROPERTIES_CHECKS = new ArrayList<>();
94+
for (String split: splits) {
95+
BUILD_TIME_PROPERTIES_CHECKS.add(split);
96+
}
97+
}
98+
}
99+
if (BUILD_TIME_PROPERTIES_CHECKS != null) {
100+
System.out.println("System is matching some properties at build time: "+propChecks);
101+
}
102+
BUILD_TIME_PROPERTIES_MATCH_IF_MISSING = Boolean.valueOf(System.getProperty("spring.native.build-time-properties-match-if-missing","true"));
103+
if (!BUILD_TIME_PROPERTIES_MATCH_IF_MISSING) {
104+
System.out.println("For matchIfMissing property checks system will assume if the property is not specified the check will fail");
105+
}
72106
IGNORE_HINTS_ON_EXCLUDED_CONFIG = Boolean.valueOf(System.getProperty("spring.native.ignore-hints-on-excluded-config","true"));
73107
if (!IGNORE_HINTS_ON_EXCLUDED_CONFIG) {
74108
System.out.println("Currently not processing any spring.autoconfigure.exclude property in application.properties)");
@@ -90,10 +124,6 @@ public abstract class ConfigOptions {
90124
}
91125
System.out.println("Feature operating in "+MODE+" mode");
92126
}
93-
EVALUATE_COP = Boolean.valueOf(System.getProperty("spring.native.evaluate-cop", "false"));
94-
if(EVALUATE_COP) {
95-
System.out.println("Considering ConditionalOnProperty during configuration analysis");
96-
}
97127
REMOVE_UNUSED_AUTOCONFIG = Boolean.valueOf(System.getProperty("spring.native.remove-unused-autoconfig", "true"));
98128
if(REMOVE_UNUSED_AUTOCONFIG) {
99129
System.out.println("Removing unused configurations");
@@ -220,10 +250,6 @@ public static boolean isSkipAtBeanSignatureTypes() {
220250
return SKIP_AT_BEAN_SIGNATURE_TYPES;
221251
}
222252

223-
public static boolean isEvaluateCOP() {
224-
return EVALUATE_COP;
225-
}
226-
227253
public static Mode getMode() {
228254
return MODE;
229255
}
@@ -299,4 +325,86 @@ private static boolean exists(String typename) {
299325
}
300326
return exists;
301327
}
328+
329+
public static boolean isBuildTimePropertyChecking() {
330+
if (BUILD_TIME_PROPERTIES_CHECKS == null) {
331+
return false;
332+
}
333+
if (BUILD_TIME_PROPERTIES_CHECKS.size()>0) {
334+
return true;
335+
}
336+
return false;
337+
}
338+
339+
public static boolean shouldRespectMatchIfMissing() {
340+
return BUILD_TIME_PROPERTIES_MATCH_IF_MISSING==true;
341+
}
342+
343+
/**
344+
* Determine if the specified property should be checked at build time.
345+
*
346+
* @param key the property key (e.g. spring.application.name)
347+
* @return true if the property should be checked at build time
348+
*/
349+
public static boolean buildTimeCheckableProperty(String key) {
350+
if (!isBuildTimePropertyChecking()) {
351+
return false;
352+
}
353+
boolean defaultResult = true;
354+
int maxExplicitExclusionMatchLength = -1;
355+
int maxExplicitInclusionMatchLength = -1;
356+
for (String btpcPattern: BUILD_TIME_PROPERTIES_CHECKS) {
357+
if (btpcPattern.equals("default-include-all")) {
358+
defaultResult = true;
359+
} else if (btpcPattern.equals("default-exclude-all")) {
360+
defaultResult = false;
361+
} else if (btpcPattern.startsWith("!")) {
362+
// Exclusion: e.g. !management.foo.bar.
363+
if (key.startsWith(btpcPattern.substring(1))) {
364+
if ((btpcPattern.length()-1)>maxExplicitExclusionMatchLength) {
365+
maxExplicitExclusionMatchLength = btpcPattern.length()-1;
366+
}
367+
}
368+
} else {
369+
// Inclusion: e.g. spring.foo.
370+
if (key.startsWith(btpcPattern)) {
371+
if ((btpcPattern.length())>maxExplicitInclusionMatchLength) {
372+
maxExplicitInclusionMatchLength = btpcPattern.length();
373+
}
374+
}
375+
}
376+
}
377+
if (maxExplicitExclusionMatchLength==-1 && maxExplicitInclusionMatchLength==-1) {
378+
return defaultResult;
379+
}
380+
if (maxExplicitExclusionMatchLength>maxExplicitInclusionMatchLength) {
381+
// Explicit exclusion match was more specific
382+
return false;
383+
} else {
384+
return true;
385+
}
386+
}
387+
388+
public static List<String> getIgnoredTypes() {
389+
return IGNORED_TYPES;
390+
}
391+
392+
public static boolean shouldIgnoreType(String typename) {
393+
if (IGNORED_TYPES==null) {
394+
return false;
395+
}
396+
for (String ignoredType: IGNORED_TYPES) {
397+
if (ignoredType.endsWith(".")) {
398+
// its a package
399+
if (typename.startsWith(ignoredType)) {
400+
return true;
401+
}
402+
} else {
403+
if (ignoredType.equals(typename)) {
404+
return true;
405+
}
406+
}
407+
}
408+
return false;
409+
}
302410
}

spring-graalvm-native-feature/src/main/java/org/springframework/graalvm/support/ResourcesHandler.java

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,7 @@ private void registerTypeReferencedBySpringFactoriesKey(String s) {
753753
}
754754

755755
private void processSpringFactory(TypeSystem ts, URL springFactory) {
756+
SpringFeature.log("processing spring factory file "+springFactory);
756757
List<SpringFactoriesProcessor> springFactoriesProcessors = ts.getSpringFactoryProcessors();
757758
List<String> forRemoval = new ArrayList<>();
758759
Properties p = new Properties();
@@ -761,7 +762,6 @@ private void processSpringFactory(TypeSystem ts, URL springFactory) {
761762
Enumeration<Object> factoryKeys = p.keys();
762763
boolean modified = false;
763764

764-
765765
if (!ConfigOptions.isAgentMode()) {
766766
Properties filteredProperties = new Properties();
767767
for (Map.Entry<Object, Object> factoriesEntry : p.entrySet()) {
@@ -772,7 +772,9 @@ private void processSpringFactory(TypeSystem ts, URL springFactory) {
772772
values.add(value);
773773
}
774774
for (SpringFactoriesProcessor springFactoriesProcessor : springFactoriesProcessors) {
775+
int len = values.size();
775776
if (springFactoriesProcessor.filter(springFactory, key, values)) {
777+
SpringFeature.log("Spring factory filtered by "+springFactoriesProcessor.getClass().getName()+" removing "+(len-values.size())+" entries");
776778
modified = true;
777779
}
778780
}
@@ -1045,7 +1047,7 @@ private boolean checkJmxConstraint(Type type, ProcessingContext pc) {
10451047

10461048

10471049
private boolean checkConditionalOnClass(Type type) {
1048-
boolean isOK = true;//type.testAnyConditionalOnClass();
1050+
boolean isOK = type.testAnyConditionalOnClass();
10491051
if (!isOK) {
10501052
SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnClass check - returning FALSE");
10511053
return false;
@@ -1054,41 +1056,72 @@ private boolean checkConditionalOnClass(Type type) {
10541056
}
10551057

10561058
private boolean checkConditionalOnBean(Type type) {
1057-
boolean isOK = true;//type.testAnyConditionalOnBean();
1059+
boolean isOK = type.testAnyConditionalOnBean();
10581060
if (!isOK) {
10591061
SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnBean check - returning FALSE");
10601062
return false;
10611063
}
10621064
return true;
10631065
}
10641066

1065-
private boolean checkConditionalOnEnabledMetricsExport(Type type) {
1066-
boolean isOK = true;//type.testAnyConditionalOnEnabledMetricsExport();
1067+
private boolean checkConditionalOnMissingBean(Type type) {
1068+
boolean isOK = type.testAnyConditionalOnMissingBean();
10671069
if (!isOK) {
1068-
SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnEnabledMetricsExport check - returning FALSE");
1070+
SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnMissingBean check - returning FALSE");
10691071
return false;
10701072
}
10711073
return true;
10721074
}
1075+
1076+
// private boolean checkConditionalOnEnabledMetricsExport(Type type) {
1077+
// boolean isOK = type.testAnyConditionalOnEnabledMetricsExport();
1078+
// if (!isOK) {
1079+
// SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnEnabledMetricsExport check - returning FALSE");
1080+
// return false;
1081+
// }
1082+
// return true;
1083+
// }
1084+
1085+
List<String> failedPropertyChecks = new ArrayList<>();
10731086

1074-
private boolean checkConditionalOnPropertyAndConditionalOnAvailableEndpointConstraints(Type type) {
1075-
if (ConfigOptions.isEvaluateCOP()) {
1076-
boolean isOK = type.testAnyConditionalOnProperty();
1077-
if (!isOK) {
1078-
SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnProperty check - returning FALSE");
1087+
/**
1088+
* It is possible to ask for property checks to be done at build time - this enables chunks of code to be discarded early
1089+
* and not included in the image.
1090+
*
1091+
* @param type the type that may have property related conditions on it
1092+
* @return true if checks pass, false if one fails and the type should be considered inactive
1093+
*/
1094+
private boolean checkPropertyRelatedConditions(Type type) {
1095+
if (ConfigOptions.isBuildTimePropertyChecking()) {
1096+
String testResult = type.testAnyConditionalOnProperty();
1097+
if (testResult != null) {
1098+
String message = type.getDottedName()+" FAILED ConditionalOnProperty property check: "+testResult;
1099+
failedPropertyChecks.add(message);
1100+
SpringFeature.log(message);
10791101
return false;
10801102
}
10811103
// These are like a ConditionalOnProperty check but using a special condition to check the property
1082-
isOK = type.testAnyConditionalOnAvailableEndpoint();
1083-
if (!isOK) {
1084-
SpringFeature.log(type.getDottedName()+" FAILED ConditionalOnAvailableEndpoint check - returning FALSE");
1085-
return false;
1104+
testResult = type.testAnyConditionalOnAvailableEndpoint();
1105+
if (testResult != null) {
1106+
String message = type.getDottedName()+" FAILED ConditionalOnAvailableEndpoint property check: "+testResult;
1107+
failedPropertyChecks.add(message);
1108+
SpringFeature.log(message);
1109+
return false;
1110+
}
1111+
testResult = type.testAnyConditionalOnEnabledMetricsExport();
1112+
if (testResult != null) {
1113+
String message = type.getDottedName()+" FAILED ConditionalOnEnabledMetricsExport property check: "+testResult;
1114+
failedPropertyChecks.add(message);
1115+
SpringFeature.log(message);
1116+
return false;
1117+
}
1118+
testResult = type.testAnyConditionalOnEnabledHealthIndicator();
1119+
if (testResult != null) {
1120+
String message = type.getDottedName()+" FAILED ConditionalOnEnabledHealthIndicator property check: "+testResult;
1121+
failedPropertyChecks.add(message);
1122+
SpringFeature.log(message);
1123+
return false;
10861124
}
1087-
// isOK = type.testAnyConditionalOnEnabledHealthIndicator();
1088-
// if (!isOK) {
1089-
// SpringFeature.log(type.getDottedName()+" FAILED due to ConditionalOnEnabledHealthIndicator check - returning FALSE");
1090-
// return false;
1091-
// }
10921125
}
10931126
return true;
10941127
}
@@ -1206,7 +1239,7 @@ private boolean processType(ProcessingContext pc, Type type, ReachedBy reachedBy
12061239
return false;
12071240
}
12081241

1209-
if (!checkConditionalOnPropertyAndConditionalOnAvailableEndpointConstraints(type)) {
1242+
if (!checkPropertyRelatedConditions(type)) {
12101243
pc.pop();
12111244
return false;
12121245
}
@@ -1215,6 +1248,11 @@ private boolean processType(ProcessingContext pc, Type type, ReachedBy reachedBy
12151248
pc.pop();
12161249
return false;
12171250
}
1251+
1252+
// if (!checkConditionalOnBean(type) || !checkConditionalOnMissingBean(type) || !checkConditionalOnClass(type)) {
1253+
// pc.pop();
1254+
// return false;
1255+
// }
12181256

12191257
checkMissingAnnotationsOnType(type);
12201258

0 commit comments

Comments
 (0)