From 8a2b500719e1ae3360c26c6e30a6771faa828a24 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Wed, 2 Aug 2023 16:20:39 +0900 Subject: [PATCH] [DROOLS-7518] NullPointerException in MemoryFileSystem when kbase.name is empty in kmodule.xml (#5414) --- .../compiler/kproject/KieModuleException.java | 24 ++++ .../kproject/models/KieBaseModelImpl.java | 4 + .../kproject/models/KieSessionModelImpl.java | 4 + .../mvel/integrationtests/KmoduleXmlTest.java | 121 ++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 drools-compiler/src/main/java/org/drools/compiler/kproject/KieModuleException.java create mode 100644 drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KmoduleXmlTest.java diff --git a/drools-compiler/src/main/java/org/drools/compiler/kproject/KieModuleException.java b/drools-compiler/src/main/java/org/drools/compiler/kproject/KieModuleException.java new file mode 100644 index 00000000000..8af0ad82a84 --- /dev/null +++ b/drools-compiler/src/main/java/org/drools/compiler/kproject/KieModuleException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * 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.drools.compiler.kproject; + +public class KieModuleException extends RuntimeException { + + public KieModuleException(String message) { + super(message); + } +} diff --git a/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieBaseModelImpl.java b/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieBaseModelImpl.java index 7d6224792bd..f674f6e06e0 100644 --- a/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieBaseModelImpl.java +++ b/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieBaseModelImpl.java @@ -30,6 +30,7 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import org.drools.compiler.kproject.KieModuleException; import org.drools.core.util.AbstractXStreamConverter; import org.drools.core.util.StringUtils; import org.kie.api.builder.model.KieBaseModel; @@ -432,6 +433,9 @@ public Object unmarshal(HierarchicalStreamReader reader, String kbaseName = reader.getAttribute( "name" ); kBase.name = kbaseName != null ? kbaseName : StringUtils.uuid(); + if (kBase.name.isEmpty()) { + throw new KieModuleException("kbase name is empty in kmodule.xml"); + } kBase.setDefault( "true".equals(reader.getAttribute( "default" )) ); diff --git a/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieSessionModelImpl.java b/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieSessionModelImpl.java index fcd3de49fed..ed35125d209 100644 --- a/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieSessionModelImpl.java +++ b/drools-compiler/src/main/java/org/drools/compiler/kproject/models/KieSessionModelImpl.java @@ -25,6 +25,7 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import org.drools.compiler.kproject.KieModuleException; import org.drools.core.BeliefSystemType; import org.drools.core.util.AbstractXStreamConverter; import org.kie.api.builder.model.ChannelModel; @@ -340,6 +341,9 @@ public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingCo public Object unmarshal(HierarchicalStreamReader reader, final UnmarshallingContext context) { final KieSessionModelImpl kSession = new KieSessionModelImpl(); kSession.name = reader.getAttribute("name"); + if (kSession.name.isEmpty()) { + throw new KieModuleException("ksession name is empty in kmodule.xml"); + } kSession.setDefault( "true".equals(reader.getAttribute( "default" )) ); kSession.setDirectFiring( "true".equals(reader.getAttribute( "directFiring" )) ); kSession.setThreadSafe( "true".equals(reader.getAttribute( "threadSafe" )) ); diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KmoduleXmlTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KmoduleXmlTest.java new file mode 100644 index 00000000000..2a07fdd0b28 --- /dev/null +++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/KmoduleXmlTest.java @@ -0,0 +1,121 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * 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.drools.mvel.integrationtests; + +import java.util.Collection; +import java.util.List; + +import org.drools.testcoverage.common.util.KieBaseTestConfiguration; +import org.drools.testcoverage.common.util.KieUtil; +import org.drools.testcoverage.common.util.TestParametersUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.kie.api.KieServices; +import org.kie.api.builder.KieBuilder; +import org.kie.api.builder.KieFileSystem; +import org.kie.api.builder.Message; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Parameterized.class) +public class KmoduleXmlTest { + + enum Element { + KBASE, + KSESSION + } + + private final KieBaseTestConfiguration kieBaseTestConfiguration; + + public KmoduleXmlTest(final KieBaseTestConfiguration kieBaseTestConfiguration) { + this.kieBaseTestConfiguration = kieBaseTestConfiguration; + } + + @Parameterized.Parameters(name = "KieBase type={0}") + public static Collection getParameters() { + return TestParametersUtil.getKieBaseCloudConfigurations(true); + } + + @Test + public void kbaseEmptyName() throws Exception { + List errors = buildKmoduleWithEmptyValue("name", Element.KBASE); + + assertThat(errors).isNotEmpty(); + assertThat(errors.get(0).getText()).contains("kbase name is empty in kmodule.xml"); + } + + @Test + public void kbaseEmptyIncludes() throws Exception { + List errors = buildKmoduleWithEmptyValue("includes", Element.KBASE); + + assertThat(errors).as("Empty includes is fine. It's ignored") + .isEmpty(); + } + + @Test + public void kbaseEmptyPackages() throws Exception { + List errors = buildKmoduleWithEmptyValue("packages", Element.KBASE); + + assertThat(errors).as("Empty packages is fine. It means the default package") + .isEmpty(); + } + + @Test + public void ksessionEmptyName() throws Exception { + List errors = buildKmoduleWithEmptyValue("name", Element.KSESSION); + + assertThat(errors).isNotEmpty(); + assertThat(errors.get(0).getText()).contains("ksession name is empty in kmodule.xml"); + } + + private List buildKmoduleWithEmptyValue(String emptyAttribute, Element element) throws Exception { + String drl = + "package org.example\n" + + "rule R1 when\n" + + "then\n" + + "end\n"; + + KieServices ks = KieServices.Factory.get(); + + KieFileSystem kfs = ks.newKieFileSystem(); + kfs.write("src/main/resources/org/example/r1.drl", drl); + kfs.write("src/main/resources/META-INF/kmodule.xml", getKmoduleString(emptyAttribute, element)); + final KieBuilder kieBuilder = KieUtil.getKieBuilderFromKieFileSystem(kieBaseTestConfiguration, kfs, false); + + return kieBuilder.getResults().getMessages(Message.Level.ERROR); + } + + private String getKmoduleString(String emptyAttribute, Element element) { + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + if (element == Element.KBASE) { + // if kbase name is omitted, UUID is given to its name + sb.append("\n"); + sb.append(" \n"); + } else if (element == Element.KSESSION) { + sb.append("\n"); + // if you test an attribute other than "name" in ksession, you need to add "name" attribute as it's required + sb.append(" \n"); + } else { + throw new IllegalArgumentException("Unsupported element : " + element); + } + sb.append("\n"); + sb.append("\n"); + return sb.toString(); + } +}