-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: loader for extensions in custom base packages (#2081)
* extensions loader Signed-off-by: Pablo Hernán Carle <[email protected]> * unit tests Signed-off-by: Pablo Hernán Carle <[email protected]> * logs Signed-off-by: Pablo Hernán Carle <[email protected]> * fix typo and fixes Signed-off-by: Pablo Hernán Carle <[email protected]> * public modifier Signed-off-by: Pablo Hernán Carle <[email protected]> * coverage Signed-off-by: Pablo Hernán Carle <[email protected]> * jupiter Signed-off-by: Pablo Hernán Carle <[email protected]> * tests Signed-off-by: Pablo Hernán Carle <[email protected]> * test Signed-off-by: Pablo Hernán Carle <[email protected]> * add case and logs Signed-off-by: Pablo Hernán Carle <[email protected]> * fix deserialization Signed-off-by: Pablo Hernán Carle <[email protected]> * remove log Signed-off-by: Pablo Hernán Carle <[email protected]> * simplify constructors Signed-off-by: Pablo Hernán Carle <[email protected]> * pr review Signed-off-by: Pablo Hernán Carle <[email protected]> * refactor tests Signed-off-by: Pablo Hernán Carle <[email protected]> Co-authored-by: Pablo Hernán Carle <[email protected]>
- Loading branch information
1 parent
1382f24
commit 9a4be5a
Showing
16 changed files
with
592 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
dependencies { | ||
compile(project(':common-service-core')) | ||
compileOnly libraries.spring_boot_starter_web | ||
|
||
implementation libraries.jackson_dataformat_yaml | ||
|
||
compileOnly libraries.lombok | ||
annotationProcessor libraries.lombok | ||
|
||
testImplementation libraries.spring_boot_starter_test | ||
|
||
testCompileOnly libraries.lombok | ||
testAnnotationProcessor libraries.lombok | ||
} |
75 changes: 75 additions & 0 deletions
75
apiml-extension-loader/src/main/java/org/zowe/apiml/extension/ExtensionConfigReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Copyright Contributors to the Zowe Project. | ||
*/ | ||
package org.zowe.apiml.extension; | ||
|
||
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; | ||
|
||
import java.io.File; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.stream.Collectors; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; | ||
|
||
import org.zowe.apiml.extension.ExtensionDefinition.ApimlServices; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
public class ExtensionConfigReader { | ||
|
||
private final ZoweRuntimeEnvironment environment; | ||
|
||
public ExtensionConfigReader(ZoweRuntimeEnvironment environment) { | ||
this.environment = environment; | ||
} | ||
|
||
public String[] getBasePackages() { | ||
return getEnabledExtensions() | ||
.stream() | ||
.map(ExtensionDefinition::getApimlServices) | ||
.filter(Objects::nonNull) | ||
.map(ApimlServices::getBasePackage) | ||
.filter(Objects::nonNull) | ||
.collect(Collectors.toList()) | ||
.toArray(new String[0]); | ||
} | ||
|
||
private List<ExtensionDefinition> getEnabledExtensions() { | ||
List<String> installedComponents = environment.getInstalledComponents(); | ||
List<String> enabledComponents = environment.getEnabledComponents(); | ||
List<ExtensionDefinition> extensions = new ArrayList<>(); | ||
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()).configure(FAIL_ON_UNKNOWN_PROPERTIES, false); | ||
ObjectMapper jsonMapper = new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false); | ||
|
||
for (String installedComponent : installedComponents) { | ||
if (enabledComponents.contains(installedComponent)) { | ||
String parentPath = environment.getExtensionDirectory() + File.separator + installedComponent; | ||
Path manifestYamlPath = Paths.get(parentPath + "/manifest.yaml"); | ||
Path manifestJsonPath = Paths.get(parentPath + "/manifest.json"); | ||
try { | ||
if (Files.exists(manifestYamlPath)) { | ||
extensions.add(yamlMapper.readValue(Files.readAllBytes(manifestYamlPath), ExtensionDefinition.class)); | ||
} else if (Files.exists(manifestJsonPath)) { | ||
extensions.add(jsonMapper.readValue(Files.readAllBytes(manifestJsonPath), ExtensionDefinition.class)); | ||
} | ||
} catch (Exception e) { | ||
log.error("Failed reading component manifests", e); | ||
} | ||
} | ||
} | ||
return extensions; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
apiml-extension-loader/src/main/java/org/zowe/apiml/extension/ExtensionDefinition.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Copyright Contributors to the Zowe Project. | ||
*/ | ||
package org.zowe.apiml.extension; | ||
|
||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
/** | ||
* Definition based on https://docs.zowe.org/stable/extend/packaging-zos-extensions/#zowe-component-manifest | ||
*/ | ||
@Data | ||
@NoArgsConstructor | ||
public class ExtensionDefinition { | ||
|
||
private String name; | ||
private ApimlServices apimlServices; | ||
|
||
@Data | ||
@NoArgsConstructor | ||
public static class ApimlServices { | ||
|
||
private String basePackage; | ||
|
||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
apiml-extension-loader/src/main/java/org/zowe/apiml/extension/ExtensionsLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Copyright Contributors to the Zowe Project. | ||
*/ | ||
package org.zowe.apiml.extension; | ||
|
||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; | ||
import org.springframework.boot.context.event.ApplicationContextInitializedEvent; | ||
import org.springframework.context.ApplicationListener; | ||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; | ||
|
||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
/** | ||
* Loader of extensions | ||
* | ||
*/ | ||
@Slf4j | ||
@RequiredArgsConstructor | ||
public class ExtensionsLoader implements ApplicationListener<ApplicationContextInitializedEvent> { | ||
|
||
@NonNull | ||
private final ExtensionConfigReader configReader; | ||
|
||
@Override | ||
public void onApplicationEvent(ApplicationContextInitializedEvent event) { | ||
if (!(event.getApplicationContext() instanceof BeanDefinitionRegistry)) { | ||
log.error("Expected Spring context to be a BeanDefinitionRegistry. Extensions are not loaded"); | ||
} else { | ||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) event.getApplicationContext(); | ||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry); | ||
|
||
String[] extensionsBasePackages = configReader.getBasePackages(); | ||
if (extensionsBasePackages.length > 0) { | ||
try { | ||
scanner.scan(configReader.getBasePackages()); | ||
|
||
String[] beanNames = scanner.getRegistry().getBeanDefinitionNames(); | ||
for (String name : beanNames) { | ||
if (!registry.containsBeanDefinition(name)) { | ||
registry.registerBeanDefinition(name, scanner.getRegistry().getBeanDefinition(name)); | ||
} else { | ||
log.info("Bean with name " + name + " is already registered in the context"); | ||
} | ||
} | ||
} catch (Exception e) { | ||
log.error("Failed loading extensions", e); | ||
} | ||
} | ||
} | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
apiml-extension-loader/src/main/java/org/zowe/apiml/extension/ZoweRuntimeEnvironment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Copyright Contributors to the Zowe Project. | ||
*/ | ||
package org.zowe.apiml.extension; | ||
|
||
import static java.util.Collections.emptyList; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
|
||
import lombok.NoArgsConstructor; | ||
|
||
@NoArgsConstructor | ||
public class ZoweRuntimeEnvironment { | ||
|
||
static final String INSTALLED_EXTENSIONS_ENV = "ZWE_INSTALLED_COMPONENTS"; | ||
static final String ENABLED_EXTENSIONS_ENV = "ZWE_ENABLED_COMPONENTS"; | ||
|
||
static final String WORKSPACE_DIR_ENV = "ZWE_zowe_workspaceDirectory"; | ||
static final String COMPONENTS_APP_SERVER_DIR_ENV = "ZWE_components_app_server_pluginsDir"; | ||
static final String PLUGINS_DIR_ENV = "ZWED_pluginsDir"; | ||
static final String EXTENSION_DIR_ENV = "ZWE_zowe_extensionDirectory"; | ||
|
||
public static ZoweRuntimeEnvironment defaultEnv() { | ||
return new ZoweRuntimeEnvironment(); | ||
} | ||
|
||
Optional<String> getPluginsDir() { | ||
String pluginsDir = System.getenv(PLUGINS_DIR_ENV); | ||
String componentsAppServerPluginsDir = System.getenv(COMPONENTS_APP_SERVER_DIR_ENV); | ||
String workspaceDir = System.getenv(WORKSPACE_DIR_ENV); | ||
|
||
if (StringUtils.isNotEmpty(pluginsDir)) { | ||
return Optional.of(pluginsDir); | ||
} else if (StringUtils.isNotEmpty(componentsAppServerPluginsDir)) { | ||
return Optional.of(componentsAppServerPluginsDir); | ||
} else if (StringUtils.isNotEmpty(workspaceDir)) { | ||
return Optional.of(workspaceDir); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
private List<String> getComponents(String env) { | ||
return Optional.ofNullable(env) | ||
.map(installed -> installed.split(",")) | ||
.map(Arrays::asList) | ||
.orElse(emptyList()); | ||
} | ||
|
||
List<String> getInstalledComponents() { | ||
return getComponents(System.getenv(INSTALLED_EXTENSIONS_ENV)); | ||
} | ||
|
||
List<String> getEnabledComponents() { | ||
return getComponents(System.getenv(ENABLED_EXTENSIONS_ENV)); | ||
} | ||
|
||
String getExtensionDirectory() { | ||
return System.getenv(EXTENSION_DIR_ENV); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
apiml-extension-loader/src/test/java/org/zowe/CustomBean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Copyright Contributors to the Zowe Project. | ||
*/ | ||
package org.zowe; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
/** | ||
* Intended as a test bean located outside of the default component scan | ||
* | ||
*/ | ||
@Component | ||
public class CustomBean { | ||
|
||
} |
83 changes: 83 additions & 0 deletions
83
apiml-extension-loader/src/test/java/org/zowe/apiml/extension/ExtensionConfigReaderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Copyright Contributors to the Zowe Project. | ||
*/ | ||
package org.zowe.apiml.extension; | ||
|
||
import static java.util.Collections.singletonList; | ||
import static org.junit.jupiter.api.Assertions.assertArrayEquals; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.Collections; | ||
|
||
import com.google.common.io.Resources; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Nested; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class ExtensionConfigReaderTest { | ||
|
||
@Mock | ||
private ZoweRuntimeEnvironment environment; | ||
|
||
private ExtensionConfigReader configReader; | ||
|
||
@BeforeEach | ||
public void setUp() { | ||
this.configReader = new ExtensionConfigReader(environment); | ||
} | ||
|
||
private String getTestResourcesPath() { | ||
String path = Resources.getResource("apimlextension").getPath(); | ||
return path.substring(0, path.lastIndexOf('/')); | ||
} | ||
|
||
@Nested | ||
class WhenGettingExtensionsConfiguration { | ||
|
||
@Nested | ||
class GivenNoManifestIsAvailable { | ||
@Test | ||
void itReturnsNoPackagesToScan() { | ||
when(environment.getExtensionDirectory()).thenReturn("."); | ||
when(environment.getInstalledComponents()).thenReturn(singletonList("apimlextension")); | ||
when(environment.getEnabledComponents()).thenReturn(singletonList("apimlextension")); | ||
|
||
assertArrayEquals(new String[]{}, configReader.getBasePackages()); | ||
} | ||
} | ||
|
||
@Nested | ||
class GivenAnExtensionIsDefined { | ||
@Test | ||
void itReturnsPackageNameToScan() { | ||
when(environment.getExtensionDirectory()).thenReturn(getTestResourcesPath()); | ||
when(environment.getInstalledComponents()).thenReturn(singletonList("apimlextension")); | ||
when(environment.getEnabledComponents()).thenReturn(singletonList("apimlextension")); | ||
|
||
assertArrayEquals(new String[]{ "org.zowe" }, configReader.getBasePackages()); | ||
} | ||
} | ||
|
||
@Nested | ||
class GivenNoComponentsAreInstalled { | ||
@Test | ||
void itReturnsNoPackagesToScan() { | ||
when(environment.getInstalledComponents()).thenReturn(Collections.emptyList()); | ||
when(environment.getEnabledComponents()).thenReturn(Collections.emptyList()); | ||
|
||
assertArrayEquals(new String[]{}, configReader.getBasePackages()); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.