-
Notifications
You must be signed in to change notification settings - Fork 51
feat(QTDI-1914): Sample dynamic schema connector. #1114
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: master
Are you sure you want to change the base?
Conversation
…ement, better clazz location.
…l loaded... to continue...
…:Talend/component-runtime into ypiel/QTDI-1914_sample_connector_dyndeps
# sample-parent/sample-features/dynamic-dependencies/dynamic-dependencies-common/src/main/java/org/talend/sdk/component/sample/feature/dynamicdependencies/config/Dependency.java # sample-parent/sample-features/dynamic-dependencies/dynamic-dependencies-common/src/main/java/org/talend/sdk/component/sample/feature/dynamicdependencies/service/AbstractDynamicDependenciesService.java
# Conflicts: # sample-parent/sample-features/pom.xml
…st log + add an error messae in returned strings.
…st log + add an error messae in returned strings.
…SPI is not loaded.
…Don't fail if SPI can't be loaded.
…to transmit its dynamic dependencies.
…hSPI connector output records.
…cy-common module.
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.
Pull request overview
This PR adds a comprehensive sample connector module demonstrating dynamic dependency loading capabilities. The implementation showcases how to use the @DynamicDependencies annotation with various configuration types (datastore, dataset, and custom configuration) and validates SPI loading from different dependency scopes.
Changes:
- Added new
dynamic-dependenciesmodule with multiple sample connectors demonstrating@DynamicDependenciesfeature - Introduced
DynamicDependenciesConfigurationannotation for marking configuration types that compute dynamic dependencies - Created supporting modules for SPI testing and classloader validation
Reviewed changes
Copilot reviewed 99 out of 110 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| sample-parent/sample-features/pom.xml | Added dynamic-dependencies module to the build |
| component-api/src/main/java/.../DynamicDependenciesConfiguration.java | New annotation for marking dynamic dependency configuration types |
| sample-parent/sample-features/dynamic-dependencies/pom.xml | Parent POM defining the dynamic-dependencies module structure |
| sample-parent/sample-features/dynamic-dependencies/README.md | Comprehensive documentation explaining the dynamic dependencies feature and test scenarios |
| sample-parent/sample-features/dynamic-dependencies/*/pom.xml | Module-specific POM files for various sample connectors |
| sample-parent/sample-features/dynamic-dependencies//src/main/java/**/.java | Implementation classes for sample connectors, services, and configuration |
| sample-parent/sample-features/dynamic-dependencies//src/test/java/**/.java | Test classes validating dynamic dependency loading |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...nt/sample/feature/dynamicdependencies/withspi/input/DynamicDependenciesWithSPIInputTest.java
Outdated
Show resolved
Hide resolved
.../main/java/org/talend/sdk/component/sample/feature/dynamicdependencies/config/Connector.java
Outdated
Show resolved
Hide resolved
...t/sample/feature/dynamicdependencies/withDynamicDependenciesConfiguration/config/Config.java
Outdated
Show resolved
Hide resolved
...dk/component/sample/feature/dynamicdependencies/withDataprepRunAnnotation/config/Config.java
Outdated
Show resolved
Hide resolved
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.
Pull request overview
Copilot reviewed 99 out of 110 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public List<String> getDynamicDependencies(@Option("theDataset") final Dataset dataset) { | ||
| String dep = "org.talend.sdk.samplefeature.dynamicdependencies:service-provider-from-dynamic-dependency:" | ||
| + loadVersion(); | ||
| List<String> strings = Collections.singletonList(dep); | ||
| log.info("Dynamic dependencies with SPI: {}", strings.stream().collect(Collectors.joining(";"))); | ||
| return strings; | ||
| } |
Copilot
AI
Feb 9, 2026
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.
The returned Maven coordinate uses a groupId (org.talend.sdk.samplefeature.dynamicdependencies) that doesn’t match the groupId declared by the modules in this PR (org.talend.sdk.component.loading-analysis). This will make the runtime/designtime resolver look for a non-existent artifact. Update the coordinate to match the actual groupId/artifactId of service-provider-from-dynamic-dependency.
| return Stream.of( | ||
| // Implementation should come from a dynamic dependency | ||
| "org.talend.sdk.component.sample.feature.dynamicdependencies.classloadertestlibrary.serviceInterfaces.StringProviderSPIAsDependency", | ||
| // Implementation should come from runtime | ||
| "org.talend.sdk.component.sample.feature.dynamicdependencies.classloadertestlibrary.serviceInterfaces.StringProviderSPIAsDynamicDependency"); |
Copilot
AI
Feb 9, 2026
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.
The returned class names reference ...sample.feature.dynamicdependencies... but the actual packages added in this PR are ...sample.feature.loadinganalysis.... With the current values, the customizer won’t match the intended classes/packages, so the classloader customization won’t take effect. Replace these strings with the correct org.talend.sdk.component.sample.feature.loadinganalysis... FQNs.
| } | ||
|
|
||
| log.info( | ||
| "org.talend.sdk.component.sample.feature.dynamicdependencies.withspi.service.CustomizeClassLoader is enable,\n" |
Copilot
AI
Feb 9, 2026
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.
Correct grammar in the log message: “is enable” → “is enabled”.
| "org.talend.sdk.component.sample.feature.dynamicdependencies.withspi.service.CustomizeClassLoader is enable,\n" | |
| "org.talend.sdk.component.sample.feature.dynamicdependencies.withspi.service.CustomizeClassLoader is enabled,\n" |
| private static String loadVersion() { | ||
| if (version == null) { | ||
| try (InputStream is = DynamicDependenciesWithSPIService.class.getClassLoader() | ||
| .getResourceAsStream("version.properties")) { | ||
| Properties props = new Properties(); | ||
| props.load(is); | ||
| version = props.getProperty("version"); | ||
| } catch (IOException e) { | ||
| throw new ComponentException("Unable to load project version", e); | ||
| } | ||
| } | ||
| return version; | ||
| } |
Copilot
AI
Feb 9, 2026
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.
getResourceAsStream("version.properties") can return null (missing resource or wrong filtering), and props.load(is) will throw a NullPointerException which is not caught here. Throw a ComponentException with a clear message when is == null, and consider also handling missing "version" property.
|
|
||
| Properties prop = new Properties(); | ||
| prop.load(resourceStreamFromDependency); | ||
| return prop.getProperty(property); |
Copilot
AI
Feb 9, 2026
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.
prop.getProperty(property) can return null when the property key is missing, and later JsonObjectBuilder.add(key, value) will throw if value is null. Return a non-null fallback (similar to the “resource not found” branch) or throw a ComponentException when the property is absent.
| return prop.getProperty(property); | |
| final String value = prop.getProperty(property); | |
| if (value == null) { | |
| return "The property '%s' has not been found in resource '%s', it can't retrieve its value." | |
| .formatted(property, resource); | |
| } | |
| return value; |
|
|
||
| private final Optional<S> spiImpl; | ||
|
|
||
| protected AbstractSPIConsumer(final Class clazz) { |
Copilot
AI
Feb 9, 2026
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.
This uses a raw Class type, losing type safety (and it will generate warnings). Change the signature to accept a parameterized type (for example, Class<S>) and pass that through to ServiceLoader.load(...) so the consumer can be used safely without unchecked operations.
| protected AbstractSPIConsumer(final Class clazz) { | |
| protected AbstractSPIConsumer(final Class<S> clazz) { |
|
|
||
| public static final String ENTRY_RUNTIME_CLASSPATH = "runtime_classpath"; | ||
|
|
||
| public static final String ENTRY_WORKING_DIRECTORY = "Working_directory"; |
Copilot
AI
Feb 9, 2026
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.
This entry name is inconsistent with the rest of the schema keys (ENTRY_RUNTIME_CLASSPATH, ENTRY_ROOT_REPOSITORY, etc.) which are lower snake-case. Consider changing it to working_directory (and keep README/tests aligned) to avoid surprising API consumers and reduce schema/key mismatches.
| public static final String ENTRY_WORKING_DIRECTORY = "Working_directory"; | |
| public static final String ENTRY_WORKING_DIRECTORY = "working_directory"; |
|
|
||
| @Slf4j | ||
| @Service | ||
| public class DynamicDependenciesWithDynamicependenciesConfigurationService extends AbstractDynamicDependenciesService |
Copilot
AI
Feb 9, 2026
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.
The class name contains a typo (“Dynamicependencies”). Since this is part of a sample/public surface area, it will propagate into generated docs, logs, and code references. Rename to DynamicDependenciesWithDynamicDependenciesConfigurationService (and align related test/class references) to keep naming consistent and searchable.
| public class DynamicDependenciesWithDynamicependenciesConfigurationService extends AbstractDynamicDependenciesService | |
| public class DynamicDependenciesWithDynamicDependenciesConfigurationService extends AbstractDynamicDependenciesService |
| @Target(TYPE) | ||
| @Retention(RUNTIME) | ||
| @ConfigurationType("configuration") | ||
| @Documentation("Copy/past of the annotation from tDataprepRun." + |
Copilot
AI
Feb 9, 2026
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.
Typo in documentation text: “Copy/past” → “Copy/paste”.
| @Documentation("Copy/past of the annotation from tDataprepRun." + | |
| @Documentation("Copy/paste of the annotation from tDataprepRun." + |
| ``` | ||
|
|
||
| ## Check SPI and resource loading | ||
| The `dynamic-depdencies-with-spi` provides an input connector that will try to load: |
Copilot
AI
Feb 9, 2026
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.
Typo in module name: “dynamic-depdencies-with-spi” → “dynamic-dependencies-with-spi”.
| The `dynamic-depdencies-with-spi` provides an input connector that will try to load: | |
| The `dynamic-dependencies-with-spi` provides an input connector that will try to load: |

Requirements
https://qlik-dev.atlassian.net/browse/QTDI-1914
Why this PR is needed?
What does this PR adds (design/code thoughts)?