-
Notifications
You must be signed in to change notification settings - Fork 33
Merge feature branch for auto-generation of Liberty features GA update #511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3ba3b8e
5646c12
8159bbd
8a9d6c5
b04e985
495a118
5877cc8
ba2777d
fea5c1c
b94075a
c89b196
a14c7ec
b0a2ab8
c73102d
a6f7106
52fe283
49cbf35
4c0052a
b8679df
bc93cb1
3ae5530
67d7b34
980d066
502d3db
1c6fb16
16d3050
a42ffc8
d384f65
5905974
2fd65f3
1de62ee
877e147
2e59317
5c0f9b2
f1261b5
22403f6
1eb9ff4
7b2bf2f
79a1d16
6167b7e
bde6685
e8eba65
7f137d0
515e3a8
e43e66e
4a34e59
0d1c691
8f717ff
ab17bc3
7d5552a
a3d7d38
3aa3cb8
a1230dd
72e02b9
fc4b382
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /** | ||
| * (C) Copyright IBM Corporation 2021, 2025. | ||
| * (C) Copyright IBM Corporation 2021, 2026 | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
|
|
@@ -23,17 +23,50 @@ | |
| import java.net.URLClassLoader; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| public abstract class BinaryScannerUtil { | ||
|
|
||
| public static final String BINARY_SCANNER_MAVEN_GROUP_ID = "com.ibm.websphere.appmod.tools"; | ||
| public static final String BINARY_SCANNER_MAVEN_ARTIFACT_ID = "binary-app-scanner"; | ||
| public static final String BINARY_SCANNER_MAVEN_TYPE = "jar"; | ||
| public static final String BINARY_SCANNER_MAVEN_VERSION = "[25.0.0.2,)"; | ||
| public static final String BINARY_SCANNER_MAVEN_VERSION = "[25.0.0.2.2]"; | ||
|
|
||
| // The coordinates to use for Open Liberty versions 25.0.0.7 and up | ||
| public static final String OL_FEATURELIST_GROUP_ID = "io.openliberty.features"; | ||
| public static final String OL_FEATURELIST_ARTIFACT_ID = "open_liberty_featurelist"; | ||
| public static final String OL_FEATURELIST_TYPE = "xml"; | ||
| // the version number is generated at the point of use | ||
| // The following key is used when passing the feature list file to the scanner | ||
| public static final String OL_FEATURELIST_KEY = "openLiberty"; | ||
|
|
||
| // The coordinates to use for WebSphere Liberty versions 25.0.0.7 to 25.0.0.9 | ||
| // We will use them for releases prior to 25.0.0.7 | ||
| // io.openliberty.features:websphere_liberty_base__featurelist:xml:25.0.0.7 | ||
| // io.openliberty.features:websphere_liberty_core__featurelist:xml:25.0.0.7 | ||
|
|
||
| // The coordinates to use for WebSphere Liberty versions 25.0.0.10 and up | ||
| // Publishing stopped with 25.0.0.12 so this value is used for 26.0.0.1 and up. | ||
| // com.ibm.websphere.appserver.features:websphere_liberty_base__featurelist:xml:25.0.0.xx | ||
| // com.ibm.websphere.appserver.features:websphere_liberty_core__featurelist:xml:25.0.0.xx | ||
|
|
||
| // These coordinates are used in different combinations to access WebSphere Liberty feature lists 25.0.0.7 and up | ||
| public static final String WS1_FEATURELIST_GROUP_ID = "io.openliberty.features"; | ||
| public static final String WS2_FEATURELIST_GROUP_ID = "com.ibm.websphere.appserver.features"; | ||
| public static final String WSBASE_FEATURELIST_ARTIFACT_ID = "websphere_liberty_base__featurelist"; | ||
| public static final String WSCORE_FEATURELIST_ARTIFACT_ID = "websphere_liberty_core__featurelist"; | ||
| public static final String WS_FEATURELIST_TYPE = "xml"; | ||
| // The following keys are used when passing the feature list files to the scanner | ||
| public static final String WSBASE_FEATURELIST_KEY = "liberty"; | ||
| public static final String WSCORE_FEATURELIST_KEY = "libertyCore"; | ||
|
|
||
|
|
||
| public static final String GENERATED_FEATURES_FILE_NAME = "generated-features.xml"; | ||
| public static final String GENERATED_FEATURES_FILE_PATH = "configDropins/overrides/" + GENERATED_FEATURES_FILE_NAME; | ||
| public static final String GENERATED_FEATURES_DIR_PATH = "configDropins/overrides/"; | ||
| public static final String GENERATED_FEATURES_FILE_PATH = GENERATED_FEATURES_DIR_PATH + GENERATED_FEATURES_FILE_NAME; | ||
| public static final String GENERATED_FEATURES_TEMP_DIR = ".libertyFeatures"; | ||
| public static final String GENERATED_FEATURES_TEMP_PATH = GENERATED_FEATURES_TEMP_DIR + File.separator + GENERATED_FEATURES_FILE_PATH; | ||
| private static final String FEATURE_MODIFIED_EXCEPTION = "com.ibm.websphere.binary.cmdline.exceptions.RequiredFeatureModifiedException"; | ||
| private static final String FEATURE_CONFLICT_EXCEPTION = "com.ibm.websphere.binary.cmdline.exceptions.FeatureConflictException"; | ||
| private static final String PROVIDED_FEATURE_EXCEPTION = "com.ibm.websphere.binary.cmdline.exceptions.ProvidedFeatureConflictException"; | ||
|
|
@@ -79,13 +112,13 @@ public abstract class BinaryScannerUtil { | |
| public abstract boolean isDebugEnabled(); | ||
|
|
||
| // The jar file containing the binary scanner code | ||
| private File binaryScanner; | ||
| private File binaryScannerJar; | ||
| private URLClassLoader binaryScannerClassLoader = null; | ||
| private Class binaryScannerClass = null; | ||
| private Method binaryScannerMethod = null; | ||
|
|
||
| public BinaryScannerUtil(File binaryScanner) { | ||
| this.binaryScanner = binaryScanner; | ||
| this.binaryScannerJar = binaryScanner; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -118,11 +151,11 @@ public BinaryScannerUtil(File binaryScanner) { | |
| * scanner when used in combination with each other. E.g. EE 7 and MP 2.1 | ||
| */ | ||
| public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> classFiles, Set<String> allClassesDirectories, | ||
| String logLocation, String targetJavaEE, String targetMicroProfile, boolean optimize) | ||
| String logLocation, String targetJavaEE, String targetMicroProfile, Map featureListFileMap, boolean optimize) | ||
| throws PluginExecutionException, NoRecommendationException, RecommendationSetException, FeatureModifiedException, | ||
| FeatureUnavailableException, IllegalTargetException, IllegalTargetComboException { | ||
| Set<String> featureList = null; | ||
| if (binaryScanner != null && binaryScanner.exists()) { | ||
| Set<String> generatedFeatureList = null; | ||
| if (binaryScannerJar != null && binaryScannerJar.exists()) { | ||
| // if we are already generating features for all class files (optimize=true) and | ||
| // we are not passing any user specified features (currentFeatureSet is empty) | ||
| // we do not need to rerun the binary scanner if it fails | ||
|
|
@@ -138,17 +171,18 @@ public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> | |
| logLevel = null; | ||
| logLocation = null; | ||
| } | ||
| debug("Calling " + binaryScanner.getName() + " with the following inputs...\n" + | ||
| debug("Calling " + binaryScannerJar.getName() + " with the following inputs...\n" + | ||
| " binaryInputs: " + binaryInputs + "\n" + | ||
| " targetJavaEE: " + targetJavaEE + "\n" + | ||
| " targetMicroP: " + targetMicroProfile + "\n" + | ||
| " currentFeatures: " + currentFeatureSet + "\n" + | ||
| " featureListFileMap: " + featureListFileMap + "\n" + | ||
| " logLocation: " + logLocation + "\n" + | ||
| " logLevel: " + logLevel + "\n" + | ||
| " locale: " + java.util.Locale.getDefault()); | ||
| featureList = (Set<String>) generateFeatureSetMethod.invoke(null, binaryInputs, targetJavaEE, targetMicroProfile, | ||
| currentFeatureSet, logLocation, logLevel, java.util.Locale.getDefault()); | ||
| for (String s : featureList) {debug(s);}; | ||
| generatedFeatureList = (Set<String>) generateFeatureSetMethod.invoke(null, binaryInputs, targetJavaEE, targetMicroProfile, | ||
| currentFeatureSet, featureListFileMap, logLocation, logLevel, java.util.Locale.getDefault()); | ||
| for (String s : generatedFeatureList) {debug(s);}; | ||
| } catch (InvocationTargetException ite) { | ||
| // This is the exception from the JVM that indicates there was an exception in the method we | ||
| // called through reflection. We must extract the actual exception from the 'cause' field. | ||
|
|
@@ -168,7 +202,7 @@ public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> | |
| // The list of features from the app is passed in but it contains conflicts | ||
| Set<String> conflicts = getFeatures(scannerException); | ||
| // always rerun binary scanner in this scenario, this exception only occurs if a current feature list is passed to binary scanner | ||
| Set<String> sampleFeatureList = reRunBinaryScanner(allClassesDirectories, logLocation, targetJavaEE, targetMicroProfile); | ||
| Set<String> sampleFeatureList = reRunBinaryScanner(allClassesDirectories, logLocation, targetJavaEE, targetMicroProfile, featureListFileMap); | ||
| if (sampleFeatureList == null) { | ||
| throw new NoRecommendationException(conflicts); | ||
| } else { | ||
|
|
@@ -178,7 +212,7 @@ public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> | |
| // The scanned files conflict with each other or with current features | ||
| Set<String> conflicts = getFeatures(scannerException); | ||
| // rerun binary scanner with all class files and without the current feature set to get feature recommendations | ||
| Set<String> sampleFeatureList = reRunIfFailed ? reRunBinaryScanner(allClassesDirectories, logLocation, targetJavaEE, targetMicroProfile): null; | ||
| Set<String> sampleFeatureList = reRunIfFailed ? reRunBinaryScanner(allClassesDirectories, logLocation, targetJavaEE, targetMicroProfile, featureListFileMap): null; | ||
| if (sampleFeatureList == null) { | ||
| throw new NoRecommendationException(conflicts); | ||
| } else { | ||
|
|
@@ -188,7 +222,7 @@ public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> | |
| // The scanned files conflict and the scanner suggests modifying some features | ||
| Set<String> modifications = getFeatures(scannerException); | ||
| // rerun binary scanner with all class files and without the current feature set | ||
| Set<String> sampleFeatureList = reRunIfFailed ? reRunBinaryScanner(allClassesDirectories, logLocation, targetJavaEE, targetMicroProfile) : null; | ||
| Set<String> sampleFeatureList = reRunIfFailed ? reRunBinaryScanner(allClassesDirectories, logLocation, targetJavaEE, targetMicroProfile, featureListFileMap) : null; | ||
| throw new FeatureModifiedException(modifications, | ||
| (sampleFeatureList == null) ? getNoSampleFeatureList() : sampleFeatureList, scannerException.getLocalizedMessage()); | ||
| } else if (scannerException.getClass().getName().equals(FEATURE_NOT_AVAILABLE_EXCEPTION)) { | ||
|
|
@@ -229,13 +263,13 @@ public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> | |
| throw new PluginExecutionException("An error occurred when trying to call the binary scanner jar: " + loadingException.toString()); | ||
| } | ||
| } else { | ||
| if (binaryScanner == null) { | ||
| if (binaryScannerJar == null) { | ||
| throw new PluginExecutionException("The binary scanner jar location is not defined."); | ||
| } else { | ||
| throw new PluginExecutionException("Could not find the binary scanner jar at " + binaryScanner.getAbsolutePath()); | ||
| throw new PluginExecutionException("Could not find the binary scanner jar at " + binaryScannerJar.getAbsolutePath()); | ||
| } | ||
| } | ||
| return featureList; | ||
| return generatedFeatureList; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -252,9 +286,9 @@ public Set<String> runBinaryScanner(Set<String> currentFeatureSet, List<String> | |
| * @return - a set of features that will allow the application to run in a Liberty server | ||
| * @throws PluginExecutionException - any exception that prevents the scanner from running | ||
| */ | ||
| public Set<String> reRunBinaryScanner(Set<String> allClassesDirectories, String logLocation, String targetJavaEE, String targetMicroProfile) | ||
| throws PluginExecutionException { | ||
| Set<String> featureList = null; | ||
| public Set<String> reRunBinaryScanner(Set<String> allClassesDirectories, String logLocation, String targetJavaEE, String targetMicroProfile, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can see that runBinaryScanner and rerunBinaryScanner handle the same binary scanner exceptions completely differently example
rerunBinaryScanner approach: Logs debug message: "RuntimeException from re-run of binary scanner"
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is intentional. The contexts to call |
||
| Map featureListFileMap) throws PluginExecutionException { | ||
| Set<String> generatedFeatureList = null; | ||
| try { | ||
| Method generateFeatureSetMethod = getScannerMethod(); | ||
| Set<String> binaryInputs = allClassesDirectories; | ||
|
|
@@ -274,21 +308,21 @@ public Set<String> reRunBinaryScanner(Set<String> allClassesDirectories, String | |
| " logLocation: " + logLocation + "\n" + | ||
| " logLevel: " + logLevel + "\n" + | ||
| " locale: " + java.util.Locale.getDefault()); | ||
| featureList = (Set<String>) generateFeatureSetMethod.invoke(null, binaryInputs, targetJavaEE, targetMicroProfile, | ||
| currentFeaturesSet, logLocation, logLevel, java.util.Locale.getDefault()); | ||
| for (String s : featureList) {debug(s);}; | ||
| generatedFeatureList = (Set<String>) generateFeatureSetMethod.invoke(null, binaryInputs, targetJavaEE, targetMicroProfile, | ||
| currentFeaturesSet, featureListFileMap, logLocation, logLevel, java.util.Locale.getDefault()); | ||
| for (String s : generatedFeatureList) {debug(s);}; | ||
| } catch (InvocationTargetException ite) { | ||
| Throwable scannerException = ite.getCause(); | ||
| if (scannerException.getClass().getName().equals(PROVIDED_FEATURE_EXCEPTION)) { | ||
| // this happens when the list of features passed in contains conflicts so now no recommendation possible | ||
| debug("RuntimeException from re-run of binary scanner", scannerException); // shouldn't happen | ||
| featureList = null; | ||
| generatedFeatureList = null; | ||
| } else if (scannerException.getClass().getName().equals(FEATURE_CONFLICT_EXCEPTION)) { | ||
| // The features in the scanned files conflict with each other, no recommendation possible | ||
| featureList = getNoSampleFeatureList(); | ||
| generatedFeatureList = getNoSampleFeatureList(); | ||
| } else if (scannerException.getClass().getName().equals(FEATURE_MODIFIED_EXCEPTION)) { | ||
| // The features in the scanned files conflict with each other, no recommendation possible | ||
| featureList = getNoSampleFeatureList(); | ||
| generatedFeatureList = getNoSampleFeatureList(); | ||
| } else { | ||
| debug("Exception from rerunning binary scanner.", scannerException); | ||
| throw new PluginExecutionException("Error scanning the application for Liberty feature recommendations: " + scannerException.toString()); | ||
|
|
@@ -299,9 +333,9 @@ public Set<String> reRunBinaryScanner(Set<String> allClassesDirectories, String | |
| debug("Caused by exception2:"+loadingException.getCause().getClass().getName()); | ||
| debug("Caused by exception message2:"+loadingException.getCause().getMessage()); | ||
| } | ||
| throw new PluginExecutionException("An error occurred when trying to call the binary scanner jar for recommendations: " + loadingException.toString()); | ||
| throw new PluginExecutionException("An error occurred when trying to call the binary scanner jar for Liberty feature recommendations: " + loadingException.toString()); | ||
| } | ||
| return featureList; | ||
| return generatedFeatureList; | ||
| } | ||
|
|
||
| private Set<String> getNoSampleFeatureList() { | ||
|
|
@@ -314,7 +348,7 @@ private Set<String> getNoSampleFeatureList() { | |
| private ClassLoader getScannerClassLoader() throws MalformedURLException { | ||
| if (binaryScannerClassLoader == null) { | ||
| ClassLoader cl = this.getClass().getClassLoader(); | ||
| binaryScannerClassLoader = new URLClassLoader(new URL[] { binaryScanner.toURI().toURL() }, cl); | ||
| binaryScannerClassLoader = new URLClassLoader(new URL[] { binaryScannerJar.toURI().toURL() }, cl); | ||
| } | ||
| return binaryScannerClassLoader; | ||
| } | ||
|
|
@@ -330,10 +364,12 @@ private Class getScannerClass() throws MalformedURLException, ClassNotFoundExcep | |
| private Method getScannerMethod() throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, PluginExecutionException, SecurityException { | ||
| if (binaryScannerMethod == null) { | ||
| Class driveScan = getScannerClass(); | ||
| // args: Set<String>, String, String, Set<String>, String, String, Locale | ||
| // names: binaryInputs, targetJavaEE, targetMicroProfile, currentFeatures, logLocation, logLevel, locale | ||
| // Method name and return type: Set<String> generateFeatureList() | ||
| // arg types: Set<String>, String, String, Set<String>, Map, String, String, Locale | ||
| // arg names: binaryInputs, targetJavaEE, targetMicroProfile, currentFeatures, featureListFileMap, logLocation, logLevel, locale | ||
|
|
||
| binaryScannerMethod = driveScan.getMethod("generateFeatureList", Set.class, String.class, String.class, | ||
| Set.class, String.class, String.class, java.util.Locale.class); | ||
| Set.class, Map.class, String.class, String.class, java.util.Locale.class); | ||
| if (binaryScannerMethod == null) { | ||
| throw new PluginExecutionException("Error finding binary scanner method using reflection"); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we going to use a fixed version always?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
per release, yes it will be a fixed version