From 939ab4f607929d572527bd9bfa52a78f6a969ca8 Mon Sep 17 00:00:00 2001 From: David Stephan Date: Fri, 13 Mar 2026 16:06:39 +0100 Subject: [PATCH 1/6] SED-4497 Referencing a Keyword from an AP library doesn't work --- pom.xml | 2 +- .../packages/JavaAutomationPackageReader.java | 38 ++++++++++++++----- .../JavaAutomationPackageArchive.java | 8 ++-- .../plugins/java/GeneralScriptFunction.java | 29 +++++++++++--- 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 96dee20e5..6835357ba 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 11 0.0.0-MASTER-SNAPSHOT - 0.0.0-MASTER-SNAPSHOT + '0.0.0-SED-4497-SNAPSHOT 5.0.4 diff --git a/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java b/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java index f8898bd7b..1a4dbe66e 100644 --- a/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java +++ b/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java @@ -24,6 +24,8 @@ import java.io.*; import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -31,6 +33,8 @@ import java.util.List; import java.util.Set; +import static step.plugins.java.GeneralScriptFunction.$_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY; + public class JavaAutomationPackageReader extends AutomationPackageReader { protected final StepClassParser stepClassParser; @@ -60,7 +64,8 @@ protected void fillAutomationPackageWithAnnotatedKeywordsAndPlans(JavaAutomation try (AnnotationScanner annotationScanner = archive.createAnnotationScanner()) { // this code duplicates the StepJarParser, but here we don't set the scriptFile and librariesFile to GeneralScriptFunctions // instead of this we keep the scriptFile blank and fill it further in AutomationPackageKeywordsAttributesApplier (after we upload the jar file as resource) - List scannedKeywords = extractAnnotatedKeywords(annotationScanner, null, null); + // Exception: keywords originating from the library JAR get the library path as scriptFile immediately (librariesFile left empty). + List scannedKeywords = extractAnnotatedKeywords(annotationScanner, archive); if (!scannedKeywords.isEmpty()) { log.info("{} annotated keywords found in automation package {}", scannedKeywords.size(), StringUtils.defaultString(archive.getAutomationPackageName())); } @@ -165,7 +170,13 @@ private static List getPlanFromPlansAnnotation(Annotation return result; } - private static List extractAnnotatedKeywords(AnnotationScanner annotationScanner, String scriptFile, String librariesFile) throws JsonSchemaPreparationException { + /** + * Extract annotated Keywords from provided annotationScanner and AP archive. Keywords declared in the libraries should be created using the library as script JAR + * Note that setting the script and library resource is done at a later stage as this extraction is done before creating the related Step resources + * @param annotationScanner the scanner backed by a classloader containing both the main AP JAR and the library JAR + * @param archive Automation Package (AP archive and its dependency) from which we are extracting Keywords + */ + private static List extractAnnotatedKeywords(AnnotationScanner annotationScanner, AutomationPackageArchive archive) throws JsonSchemaPreparationException { List scannedKeywords = new ArrayList<>(); Set methods = annotationScanner.getMethodsWithAnnotation(Keyword.class); if (!methods.isEmpty()) { @@ -187,13 +198,22 @@ private static List extractAnnotatedKeywords(Ann function.setAttributes(new HashMap<>()); function.getAttributes().put(AbstractOrganizableObject.NAME, functionName); - // to be filled by AutomationPackageKeywordsAttributesApplier - if (scriptFile != null) { - function.setScriptFile(new DynamicValue<>(scriptFile)); - } - - if (librariesFile != null) { - function.setLibrariesFile(new DynamicValue<>(librariesFile)); + // Determine whether this keyword originates from the library JAR. + // If so, the library JAR is its own "main" script file and it has no additional libraries. + // Because the actual script and libraries are set later while preparing the staging in applyAutomationPackageContext, + // we mark the function with a flag here. + // We use ClassGraph's scan metadata (via annotationScanner) to determine the source + File keywordLibFile = archive.getKeywordLibFile(); + if (keywordLibFile != null) { + try { + URI libraryJarUri = keywordLibFile.toURI(); + URL classpathElementUrl = annotationScanner.getClasspathElementUrl(m.getDeclaringClass().getName()); + if (classpathElementUrl != null && libraryJarUri.equals(classpathElementUrl.toURI())) { + function.addCustomField($_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY, true); + } + } catch (URISyntaxException e) { + log.warn("Could not determine classpath element URI for method {}, skipping library JAR check", m.getName(), e); + } } function.getCallTimeout().setValue(annotation.timeout()); diff --git a/step-core/src/main/java/step/automation/packages/JavaAutomationPackageArchive.java b/step-core/src/main/java/step/automation/packages/JavaAutomationPackageArchive.java index 41eb5f009..21256d92d 100644 --- a/step-core/src/main/java/step/automation/packages/JavaAutomationPackageArchive.java +++ b/step-core/src/main/java/step/automation/packages/JavaAutomationPackageArchive.java @@ -40,7 +40,7 @@ public class JavaAutomationPackageArchive extends AutomationPackageArchive { public static final List METADATA_FILES = List.of("automation-package.yml", "automation-package.yaml"); private final ClassLoader classLoaderForMainApFile; - private final ClassLoader classLoaderForApAndLibraries; + private final URLClassLoader classLoaderForApAndLibraries; private boolean internalClassLoader = false; private final ResourcePathMatchingResolver pathMatchingResourceResolver; @@ -81,7 +81,9 @@ public JavaAutomationPackageArchive(File automationPackageFile, File keywordLibF this.pathMatchingResourceResolver = new ResourcePathMatchingResolver(classLoaderForMainApFile); // IMPORTANT!!! The class loader used to scan plans and keywords by annotations should contain all the classes from AP file and keyword lib - // (inclusive the parent classloader) + // 1. because Keywords declared in the AP file but using classes from the library will throw java.lang.NoClassDefFoundError otherwise + // 2. because we also want to include Keywords and Plans declared as code in the library. + // However, Keyword from the library will be created using the library as main Jar file this.classLoaderForApAndLibraries = createClassloaderForApWithKeywordLib(automationPackageFile, keywordLibFile); } catch (MalformedURLException ex) { throw new AutomationPackageReadingException("Unable to read automation package", ex); @@ -162,7 +164,7 @@ public ClassLoader getClassLoaderForApAndLibraries() { } public AnnotationScanner createAnnotationScanner() { - return AnnotationScanner.forSpecificJarFromURLClassLoader((URLClassLoader) getClassLoaderForApAndLibraries()); + return AnnotationScanner.forSpecificJarFromURLClassLoader(classLoaderForApAndLibraries); } @Override diff --git a/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java b/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java index 6fd0d215b..a26bed66a 100644 --- a/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java +++ b/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java @@ -33,6 +33,8 @@ */ public class GeneralScriptFunction extends Function implements AutomationPackageContextual { + public static final String $_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY = "$markAsKeywordFromAutomationPackageLibrary"; + DynamicValue scriptFile = new DynamicValue<>(""); DynamicValue scriptLanguage = new DynamicValue<>(""); @@ -91,15 +93,30 @@ public void setErrorHandlerFile(DynamicValue errorHandlerFile) { @Override public GeneralScriptFunction applyAutomationPackageContext(StagingAutomationPackageContext context) { + //Only process function without script file set (i.e. Keywords from scanned annotations) if (getScriptFile().get() == null || getScriptFile().get().isEmpty()) { AutomationPackage ap = context.getAutomationPackage(); - if (ap != null && ap.getAutomationPackageResourceRevision() != null && !ap.getAutomationPackageResourceRevision().isEmpty()) { - setScriptFile(new DynamicValue<>(ap.getAutomationPackageResourceRevision())); - } else { - throw new RuntimeException("General script functions can only be used within automation package archive"); + if (ap == null) { + throw new RuntimeException("General script functions defined in Automation Packages must either be declared in the descriptor providing an explicit script file or with Keyword annotation."); } - if (ap != null && ap.getAutomationPackageLibraryResourceRevision() != null && !ap.getAutomationPackageLibraryResourceRevision().isEmpty()) { - setLibrariesFile(new DynamicValue<>(ap.getAutomationPackageLibraryResourceRevision())); + //Handle Keywords declared in AP library, the library is used as script file for them + if (getCustomField($_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY) != null) { + getCustomFields().remove($_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY); + if (ap.getAutomationPackageLibraryResourceRevision() != null && !ap.getAutomationPackageLibraryResourceRevision().isEmpty()) { + setScriptFile(new DynamicValue<>(ap.getAutomationPackageLibraryResourceRevision())); + } else { + throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package Library, but the library resource does not exists."); + } + } else { + //Keyword annotated in main AP file + if (ap.getAutomationPackageResourceRevision() != null && !ap.getAutomationPackageResourceRevision().isEmpty()) { + setScriptFile(new DynamicValue<>(ap.getAutomationPackageResourceRevision())); + } else { + throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package, but the package resource does not exists."); + } + if (ap.getAutomationPackageLibraryResourceRevision() != null && !ap.getAutomationPackageLibraryResourceRevision().isEmpty()) { + setLibrariesFile(new DynamicValue<>(ap.getAutomationPackageLibraryResourceRevision())); + } } } return this; From a7334a77d7a34fde36b3b86af4fe8ae0e093511e Mon Sep 17 00:00:00 2001 From: David Stephan Date: Fri, 13 Mar 2026 16:10:58 +0100 Subject: [PATCH 2/6] SED-4497 Referencing a Keyword from an AP library doesn't work --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6835357ba..1574f9809 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 11 0.0.0-MASTER-SNAPSHOT - '0.0.0-SED-4497-SNAPSHOT + 0.0.0-SED-4497-SNAPSHOT 5.0.4 From 890d1069f8c72c3e3bfb237519e6dd28d4ff2e4e Mon Sep 17 00:00:00 2001 From: David Stephan Date: Fri, 13 Mar 2026 17:02:08 +0100 Subject: [PATCH 3/6] SED-4497 adapting Junit tests --- .../automation/packages/AutomationPackageManagerOSTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java index b189900fc..a3b6e45c9 100644 --- a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java +++ b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java @@ -1521,6 +1521,10 @@ private SampleUploadingResult uploadSample1WithAsserts(ObjectId explicitOldId, A assertNotEquals(generalScriptFunction.getScriptFile().get(), r.storedPackage.getAutomationPackageLibraryResource()); } else if (automationPackageFileSource == null) { assertEquals("", generalScriptFunction.getLibrariesFile().get()); + } else if ("KeywordInLib".equals(kwName)) { + //this is a KW declared in a library, its script file should be the AP library + assertEquals(r.storedPackage.getAutomationPackageLibraryResourceRevision(), generalScriptFunction.getScriptFile().get()); + assertTrue(generalScriptFunction.getLibrariesFile().get().isEmpty()); } else { assertEquals(r.storedPackage.getAutomationPackageResourceRevision(), generalScriptFunction.getScriptFile().get()); assertEquals(r.storedPackage.getAutomationPackageLibraryResourceRevision(), generalScriptFunction.getLibrariesFile().get()); From b706a9912749ea9e7a2984b548c3ad0ad89ae00a Mon Sep 17 00:00:00 2001 From: David Stephan Date: Mon, 16 Mar 2026 11:52:53 +0100 Subject: [PATCH 4/6] SED-4497 light refactoring, improving javadoc --- .../packages/JavaAutomationPackageReader.java | 25 +++++++++++-------- .../packages/AutomationPackageArchive.java | 14 ++++------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java b/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java index 1a4dbe66e..f9d9cf6e3 100644 --- a/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java +++ b/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java @@ -8,7 +8,6 @@ import step.core.dynamicbeans.DynamicValue; import step.core.plans.Plan; import step.core.scanner.AnnotationScanner; -import step.engine.plugins.LocalFunctionPlugin; import step.functions.Function; import step.functions.manager.FunctionManagerImpl; import step.handlers.javahandler.Keyword; @@ -62,9 +61,6 @@ public List getSupportedFileTypes() { @Override protected void fillAutomationPackageWithAnnotatedKeywordsAndPlans(JavaAutomationPackageArchive archive, AutomationPackageContent res) throws AutomationPackageReadingException { try (AnnotationScanner annotationScanner = archive.createAnnotationScanner()) { - // this code duplicates the StepJarParser, but here we don't set the scriptFile and librariesFile to GeneralScriptFunctions - // instead of this we keep the scriptFile blank and fill it further in AutomationPackageKeywordsAttributesApplier (after we upload the jar file as resource) - // Exception: keywords originating from the library JAR get the library path as scriptFile immediately (librariesFile left empty). List scannedKeywords = extractAnnotatedKeywords(annotationScanner, archive); if (!scannedKeywords.isEmpty()) { log.info("{} annotated keywords found in automation package {}", scannedKeywords.size(), StringUtils.defaultString(archive.getAutomationPackageName())); @@ -171,10 +167,17 @@ private static List getPlanFromPlansAnnotation(Annotation } /** - * Extract annotated Keywords from provided annotationScanner and AP archive. Keywords declared in the libraries should be created using the library as script JAR - * Note that setting the script and library resource is done at a later stage as this extraction is done before creating the related Step resources - * @param annotationScanner the scanner backed by a classloader containing both the main AP JAR and the library JAR - * @param archive Automation Package (AP archive and its dependency) from which we are extracting Keywords + * Extracts annotated Keywords from the provided annotation scanner and AP archive. + * + *

Note: script and library resources are assigned to the Keywords at a later stage, + * after the related Step resources have been created. Keywords declared in a library + * are marked with the custom field {@code $_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY} + * and will use that library as their script JAR. + * + * @param annotationScanner the scanner backed by a classloader containing both the main AP JAR + * and the (optional) library JAR + * @param archive the Automation Package (AP archive and its library) from which + * Keywords are being extracted */ private static List extractAnnotatedKeywords(AnnotationScanner annotationScanner, AutomationPackageArchive archive) throws JsonSchemaPreparationException { List scannedKeywords = new ArrayList<>(); @@ -203,10 +206,10 @@ private static List extractAnnotatedKeywords(Ann // Because the actual script and libraries are set later while preparing the staging in applyAutomationPackageContext, // we mark the function with a flag here. // We use ClassGraph's scan metadata (via annotationScanner) to determine the source - File keywordLibFile = archive.getKeywordLibFile(); - if (keywordLibFile != null) { + File libraryFile = archive.getLibraryFile(); + if (libraryFile != null) { try { - URI libraryJarUri = keywordLibFile.toURI(); + URI libraryJarUri = libraryFile.toURI(); URL classpathElementUrl = annotationScanner.getClasspathElementUrl(m.getDeclaringClass().getName()); if (classpathElementUrl != null && libraryJarUri.equals(classpathElementUrl.toURI())) { function.addCustomField($_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY, true); diff --git a/step-core/src/main/java/step/automation/packages/AutomationPackageArchive.java b/step-core/src/main/java/step/automation/packages/AutomationPackageArchive.java index c21da6031..f295fc5cd 100644 --- a/step-core/src/main/java/step/automation/packages/AutomationPackageArchive.java +++ b/step-core/src/main/java/step/automation/packages/AutomationPackageArchive.java @@ -18,9 +18,6 @@ ******************************************************************************/ package step.automation.packages; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.*; import java.net.URL; import java.util.List; @@ -29,16 +26,15 @@ public abstract class AutomationPackageArchive implements Closeable { - private static final Logger log = LoggerFactory.getLogger(AutomationPackageArchive.class); public static final List METADATA_FILES = List.of("automation-package.yml", "automation-package.yaml"); public static final String NULL_TYPE_ERROR_MSG = "The type of the AutomationPackageArchive must not be null"; private final File originalFile; - private final File keywordLibFile; + private final File libraryFile; private final String type; private final String archiveName; - public AutomationPackageArchive(File automationPackageFile, File keywordLibFile, String type, String archiveName) throws AutomationPackageReadingException { + public AutomationPackageArchive(File automationPackageFile, File libraryFile, String type, String archiveName) throws AutomationPackageReadingException { Objects.requireNonNull(automationPackageFile, "The automationPackageFile must not be null"); Objects.requireNonNull(automationPackageFile, NULL_TYPE_ERROR_MSG); this.archiveName = archiveName; @@ -46,7 +42,7 @@ public AutomationPackageArchive(File automationPackageFile, File keywordLibFile, throw new AutomationPackageReadingException("Automation package " + automationPackageFile.getName() + " doesn't exist"); } this.originalFile = automationPackageFile; - this.keywordLibFile = keywordLibFile; + this.libraryFile = libraryFile; this.type = type; } @@ -69,8 +65,8 @@ public String getAutomationPackageName() { abstract public List getResourcesByPattern(String resourcePathPattern); - public File getKeywordLibFile() { - return keywordLibFile; + public File getLibraryFile() { + return libraryFile; } public File getOriginalFile() { From 017f686c9583f3009819567d18b5e4fec0553ff4 Mon Sep 17 00:00:00 2001 From: David Stephan Date: Mon, 16 Mar 2026 12:43:47 +0100 Subject: [PATCH 5/6] SED-4497 PR feedback --- .../step/plugins/java/GeneralScriptFunction.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java b/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java index a26bed66a..f87be6a85 100644 --- a/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java +++ b/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java @@ -99,23 +99,25 @@ public GeneralScriptFunction applyAutomationPackageContext(StagingAutomationPack if (ap == null) { throw new RuntimeException("General script functions defined in Automation Packages must either be declared in the descriptor providing an explicit script file or with Keyword annotation."); } + String automationPackageLibraryResourceRevision = ap.getAutomationPackageLibraryResourceRevision(); + boolean hasLibrary = automationPackageLibraryResourceRevision != null && !automationPackageLibraryResourceRevision.isEmpty(); //Handle Keywords declared in AP library, the library is used as script file for them if (getCustomField($_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY) != null) { getCustomFields().remove($_MARK_AS_KEYWORD_FROM_AUTOMATION_PACKAGE_LIBRARY); - if (ap.getAutomationPackageLibraryResourceRevision() != null && !ap.getAutomationPackageLibraryResourceRevision().isEmpty()) { - setScriptFile(new DynamicValue<>(ap.getAutomationPackageLibraryResourceRevision())); + if (hasLibrary) { + setScriptFile(new DynamicValue<>(automationPackageLibraryResourceRevision)); } else { - throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package Library, but the library resource does not exists."); + throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package Library, but the library resource does not exist."); } } else { //Keyword annotated in main AP file if (ap.getAutomationPackageResourceRevision() != null && !ap.getAutomationPackageResourceRevision().isEmpty()) { setScriptFile(new DynamicValue<>(ap.getAutomationPackageResourceRevision())); } else { - throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package, but the package resource does not exists."); + throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package, but the package resource does not exist."); } - if (ap.getAutomationPackageLibraryResourceRevision() != null && !ap.getAutomationPackageLibraryResourceRevision().isEmpty()) { - setLibrariesFile(new DynamicValue<>(ap.getAutomationPackageLibraryResourceRevision())); + if (hasLibrary) { + setLibrariesFile(new DynamicValue<>(automationPackageLibraryResourceRevision)); } } } From fcd0e40946d853da31775c81a863927f4cec8409 Mon Sep 17 00:00:00 2001 From: David Stephan Date: Mon, 16 Mar 2026 12:44:39 +0100 Subject: [PATCH 6/6] SED-4497 PR feedback --- .../main/java/step/plugins/java/GeneralScriptFunction.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java b/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java index f87be6a85..c4547c798 100644 --- a/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java +++ b/step-functions-plugins/step-functions-plugins-java/step-functions-plugins-java-def/src/main/java/step/plugins/java/GeneralScriptFunction.java @@ -111,8 +111,9 @@ public GeneralScriptFunction applyAutomationPackageContext(StagingAutomationPack } } else { //Keyword annotated in main AP file - if (ap.getAutomationPackageResourceRevision() != null && !ap.getAutomationPackageResourceRevision().isEmpty()) { - setScriptFile(new DynamicValue<>(ap.getAutomationPackageResourceRevision())); + String automationPackageResourceRevision = ap.getAutomationPackageResourceRevision(); + if (automationPackageResourceRevision != null && !automationPackageResourceRevision.isEmpty()) { + setScriptFile(new DynamicValue<>(automationPackageResourceRevision)); } else { throw new RuntimeException("Inconsistent state: the annotated Keyword '" + this.getAttribute(NAME) + "' was detected in an Automation Package, but the package resource does not exist."); }