diff --git a/core/src/main/java/org/wildfly/glow/DataSourceDefinitionInfo.java b/core/src/main/java/org/wildfly/glow/DataSourceDefinitionInfo.java new file mode 100644 index 00000000..870d66cf --- /dev/null +++ b/core/src/main/java/org/wildfly/glow/DataSourceDefinitionInfo.java @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2024 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wildfly.glow; + + +public class DataSourceDefinitionInfo { + private final String name; + private final String url; + private final String className; + private final Layer driverLayer; + public DataSourceDefinitionInfo(String name, String url, String className, Layer driverLayer) { + this.name = name; + this.url = url; + this.className = className; + this.driverLayer = driverLayer; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the url + */ + public String getUrl() { + return url; + } + + /** + * @return the className + */ + public String getClassName() { + return className; + } + + /** + * @return the driverLayer + */ + public Layer getDriverLayer() { + return driverLayer; + } +} diff --git a/core/src/main/java/org/wildfly/glow/DeploymentScanner.java b/core/src/main/java/org/wildfly/glow/DeploymentScanner.java index 6935fd90..1790b7ca 100644 --- a/core/src/main/java/org/wildfly/glow/DeploymentScanner.java +++ b/core/src/main/java/org/wildfly/glow/DeploymentScanner.java @@ -140,7 +140,7 @@ public void scan(LayerMapping mapping, Set layers, Map all } } - errorSession.collectEndOfScanErrors(verbose, ctx.resourceInjectionJndiInfos, ctx.contextLookupInfos, ctx.allClasses); + errorSession.collectEndOfScanErrors(verbose, ctx.resourceInjectionJndiInfos, ctx.contextLookupInfos, ctx.dataSourceDefinitionInfos, ctx.allClasses); } private void scan(DeploymentScanContext ctx) throws Exception { @@ -216,6 +216,7 @@ private void scanAnnotations(DeploymentScanContext ctx) throws IOException { } } } + handleDataSourceDefinitionAnnotations(ai, ctx, foundLayer); } } } @@ -284,6 +285,18 @@ private void handleResourceInjectionAnnotations(AnnotationInstance annotationIns } } + private void handleDataSourceDefinitionAnnotations(AnnotationInstance annotationInstance, DeploymentScanContext ctx, Layer foundLayer) { + if (annotationInstance.name().toString().equals("jakarta.annotation.sql.DataSourceDefinition")) { + String name = getAnnotationValue(annotationInstance, "name"); + String url = getAnnotationValue(annotationInstance, "url"); + String className = getAnnotationValue(annotationInstance, "className"); + if (name != null) { + DataSourceDefinitionInfo di = new DataSourceDefinitionInfo(name, url, className, foundLayer); + ctx.dataSourceDefinitionInfos.put(name, di); + } + } + } + private boolean isSetter(MethodInfo methodInfo) { return methodInfo.name().startsWith("set") && methodInfo.parameterTypes().size() == 1; } @@ -864,6 +877,7 @@ static class DeploymentScanContext { private final ErrorIdentificationSession errorSession; private final Set allClasses = new HashSet<>(); private final Map resourceInjectionJndiInfos = new HashMap<>(); + private final Map dataSourceDefinitionInfos = new HashMap<>(); public Set contextLookupInfos = new HashSet<>(); private DeploymentScanContext(LayerMapping mapping, Set layers, Map allLayers, ErrorIdentificationSession errorSession) { diff --git a/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java b/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java index a98614cd..85639823 100644 --- a/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java +++ b/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java @@ -37,6 +37,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; +import org.wildfly.glow.DataSourceDefinitionInfo; import org.wildfly.glow.Env; import static org.wildfly.glow.Utils.getAddOnFix; @@ -51,9 +52,11 @@ public class DatasourceErrorIdentification implements ErrorIdentification { private static final String UNBOUND_DATASOURCES_ERROR = "unbound-datasources"; private static final String NO_DEFAULT_DATASOURCE_ERROR = "no-default-datasource"; + private static final String UNKNOWN_DRIVER_ERROR = "unknown-driver"; private static final String UNBOUND_DATASOURCES_ERROR_DESCRIPTION = "unbound datasources error"; private static final String NO_DEFAULT_DATASOURCE_ERROR_DESCRIPTION = "no default datasource found error"; Map> errors = new HashMap<>(); + private Map datasourceDefinitionInfos; @Override public void collectErrors(Path rootPath) throws Exception { @@ -130,32 +133,46 @@ public Map> refreshErrors(Set allBaseLayers) throws Excep if (unboundDatasourcesErrors != null) { for (IdentifiedError error : unboundDatasourcesErrors) { UnboundDatasourceError uds = (UnboundDatasourceError) error; - for (Layer l : allBaseLayers) { - if (l.getBringDatasources().contains(uds.unboundDatasource)) { - // The error is directly handled, we can remove it. - toRemove.add(uds.unboundDatasource); - break; - } else { - if (l.getAddOn() != null) { - Fix fix = l.getAddOn().getFixes().get(error.getId()); - if (fix != null) { - String content = null; - if (!l.getBringDatasources().contains(uds.unboundDatasource)) { - content = fix.getContent(); - if (content != null) { - content = content.replaceAll("##ITEM##", uds.unboundDatasource); - } - if (fix.isEnv()) { - Set envs = ret.get(l); - if (envs == null) { - envs = new HashSet<>(); - ret.put(l, envs); + if (datasourceDefinitionInfos.containsKey(uds.unboundDatasource)) { + toRemove.add(uds.unboundDatasource); + DataSourceDefinitionInfo di = datasourceDefinitionInfos.get(uds.unboundDatasource); + if (di.getDriverLayer() == null) { + Set errs = errors.get(UNKNOWN_DRIVER_ERROR); + if (errs == null) { + errs = new HashSet<>(); + errors.put(UNKNOWN_DRIVER_ERROR, errs); + } + errs.add(new IdentifiedError("Unknown driver", "The driver located in the URL [" + di.getUrl() + "] for the datasource " + uds.unboundDatasource + + " injected thanks to the jakarta.annotation.sql.DataSourceDefinition annotation is not known.", ErrorLevel.WARN)); + } + } else { + for (Layer l : allBaseLayers) { + if (l.getBringDatasources().contains(uds.unboundDatasource)) { + // The error is directly handled, we can remove it. + toRemove.add(uds.unboundDatasource); + break; + } else { + if (l.getAddOn() != null) { + Fix fix = l.getAddOn().getFixes().get(error.getId()); + if (fix != null) { + String content = null; + if (!l.getBringDatasources().contains(uds.unboundDatasource)) { + content = fix.getContent(); + if (content != null) { + content = content.replaceAll("##ITEM##", uds.unboundDatasource); + } + if (fix.isEnv()) { + Set envs = ret.get(l); + if (envs == null) { + envs = new HashSet<>(); + ret.put(l, envs); + } + envs.add(new Env(fix.getEnvName(), Fix.getEnvValue(content), false, true, false)); } - envs.add(new Env(fix.getEnvName(), Fix.getEnvValue(content), false, true, false)); } + String errorMessage = getAddOnFix(l.getAddOn(), content); + error.setFixed(errorMessage); } - String errorMessage = getAddOnFix(l.getAddOn(), content); - error.setFixed(errorMessage); } } } @@ -203,4 +220,8 @@ public List getErrors() { } return ret; } + + void setDataSourceDefinitionInfos(Map datasourceDefinitionInfos) { + this.datasourceDefinitionInfos = datasourceDefinitionInfos; + } } diff --git a/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java b/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java index 62d1e1f5..2e46edd5 100644 --- a/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java +++ b/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.wildfly.glow.DataSourceDefinitionInfo; import org.wildfly.glow.Env; /** @@ -52,8 +53,10 @@ public void collectEndOfScanErrors( boolean verbose, Map resourceInjectionInfos, Set initialContextLookupInfos, + Map datasourceDefinitionInfos, Set allClasses) { jndiErrorIdentification.collectErrors(verbose, resourceInjectionInfos, initialContextLookupInfos, allClasses); + ds.setDataSourceDefinitionInfos(datasourceDefinitionInfos); } public Map> refreshErrors(Set allBaseLayers, LayerMapping mapping, Set enabledAddOns) throws Exception { diff --git a/tests/glow-tests/src/test/java/org/wildfly/glow/test/core/datasource/DataSourceDefinitionTestCase.java b/tests/glow-tests/src/test/java/org/wildfly/glow/test/core/datasource/DataSourceDefinitionTestCase.java new file mode 100644 index 00000000..bc233d80 --- /dev/null +++ b/tests/glow-tests/src/test/java/org/wildfly/glow/test/core/datasource/DataSourceDefinitionTestCase.java @@ -0,0 +1,57 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2024 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.glow.test.core.datasource; + +import jakarta.annotation.sql.DataSourceDefinition; +import org.junit.Assert; +import org.junit.Test; +import org.wildfly.glow.ScanResults; +import org.wildfly.glow.error.IdentifiedError; +import org.wildfly.glow.test.core.TestPackager; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.Ignore; + +/** + * Tests that timer service etc. are considered strongly typed + */ +@DataSourceDefinition(name="java:jboss/datasources/batch-processingDS", + className="org.h2.jdbcx.JdbcDataSource", + url="jdbc:h2:mem:batch-processing;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1", + user="sa", + password="sa" +) +// Will un-ignore this test for WildFly 35 Final +@Ignore +public class DataSourceDefinitionTestCase { + private final TestPackager testPackager = new TestPackager(); + + @Test + public void h2DriverUsage() throws Exception { + ScanResults scanResults = testPackager.packageTestAsArchiveAndScan(DataSourceDefinitionTestCase.class); + List errors = scanResults.getErrorSession().getErrors(); + Assert.assertEquals(0, errors.size()); + + Set layers = scanResults.getDiscoveredLayers().stream().map(l -> l.getName()).collect(Collectors.toSet()); + Assert.assertTrue(layers.contains("h2-driver")); + + } + +}