This is an automated email from the ASF dual-hosted git repository.

tkobayas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie.git


The following commit(s) were added to refs/heads/main by this push:
     new 78ae3a02c05 [incubator-kie#7213] Consolidate type annotation 
processing into TypeDeclarationFactory (#7216)
78ae3a02c05 is described below

commit 78ae3a02c0511e0ba67a789403679d4027b499ea
Author: Toshiya Kobayashi <[email protected]>
AuthorDate: Fri Oct 30 09:31:38 2026 +0900

    [incubator-kie#7213] Consolidate type annotation processing into 
TypeDeclarationFactory (#7216)
    
    Annotation merging for type declarations was spread across three places,
    causing @Role and @PropertyChangeSupport to be processed multiple times.
    
    - Build a single merged Annotated (DRL + Java fallback) in
      processAnnotations() and handle @Timestamp/@Duration attribute names
      in processTypeAnnotations()
    - Simplify wireFieldAccessors() to only wire extractors from values
      already set on TypeDeclaration
    - Stop re-reading @Duration/@Timestamp in exec-model TypeDeclarationUtil
    - Apply processAnnotations() to the retained declaration in
      redeclaration scenarios to preserve @classReactive, @expires, etc.
    - Wrap annotation processing errors as TypeDeclarationError in
      exec-model and impact analysis callers
    - Pass configured PropertySpecificOption instead of hardcoding ALWAYS
    
    Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../java/org/drools/base/rule/TypeDeclaration.java | 19 ++++-
 .../builder/impl/TypeDeclarationCache.java         |  7 +-
 .../builder/impl/TypeDeclarationConfigurator.java  | 77 ++++--------------
 .../builder/impl/TypeDeclarationFactory.java       | 45 +++--------
 .../parser/internal/ImpactModelBuilderImpl.java    | 14 +++-
 .../processors/DeclaredTypeCompilationPhase.java   |  3 +-
 .../processors/DeclaredTypeRegistrationPhase.java  | 18 ++++-
 .../tool/ExplicitCanonicalModelCompiler.java       |  3 +-
 .../model/codegen/execmodel/DeclaredTypesTest.java | 17 ++++
 .../modelcompiler/util/TypeDeclarationUtil.java    | 29 ++-----
 .../compiler/compiler/TypeDeclarationTest.java     | 91 ++++++++++++++++++++++
 11 files changed, 193 insertions(+), 130 deletions(-)

diff --git 
a/drools-base/src/main/java/org/drools/base/rule/TypeDeclaration.java 
b/drools-base/src/main/java/org/drools/base/rule/TypeDeclaration.java
index d1448c742a9..d0aebe5f405 100644
--- a/drools-base/src/main/java/org/drools/base/rule/TypeDeclaration.java
+++ b/drools-base/src/main/java/org/drools/base/rule/TypeDeclaration.java
@@ -35,11 +35,13 @@ import org.drools.base.util.PropertyReactivityUtil;
 import org.drools.base.util.TimeIntervalParser;
 import org.drools.util.ClassUtils;
 import org.kie.api.definition.type.ClassReactive;
+import org.kie.api.definition.type.Duration;
 import org.kie.api.definition.type.Expires;
 import org.kie.api.definition.type.Expires.Policy;
 import org.kie.api.definition.type.PropertyChangeSupport;
 import org.kie.api.definition.type.PropertyReactive;
 import org.kie.api.definition.type.Role;
+import org.kie.api.definition.type.Timestamp;
 import org.kie.api.io.Resource;
 import org.kie.api.prototype.Prototype;
 import org.kie.internal.builder.conf.PropertySpecificOption;
@@ -567,6 +569,8 @@ public class TypeDeclaration
         configureExpirationOffset( type, annotated );
         configurePropertyReactivity( type, annotated, propertySpecificOption );
         configurePropertyChangeSupport( type, annotated );
+        configureTimestampAttribute( type, annotated );
+        configureDurationAttribute( type, annotated );
     }
 
     private static void configureRole( TypeDeclaration type, Annotated 
annotated ) {
@@ -594,9 +598,22 @@ public class TypeDeclaration
     }
 
     private static void configurePropertyChangeSupport( TypeDeclaration type, 
Annotated annotated ) {
-        // only ever switch it on: a DRL declaration may already have made the 
type dynamic
         if ( annotated.hasAnnotation( PropertyChangeSupport.class ) ) {
             type.setDynamic( true );
         }
     }
+
+    private static void configureTimestampAttribute( TypeDeclaration type, 
Annotated annotated ) {
+        Timestamp timestamp = annotated.getTypedAnnotation(Timestamp.class);
+        if (timestamp != null) {
+            type.setTimestampAttribute( timestamp.value() );
+        }
+    }
+
+    private static void configureDurationAttribute( TypeDeclaration type, 
Annotated annotated ) {
+        Duration duration = annotated.getTypedAnnotation(Duration.class);
+        if (duration != null) {
+            type.setDurationAttribute( duration.value() );
+        }
+    }
 }
diff --git 
a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationCache.java
 
b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationCache.java
index 2c83b241273..332c61bd501 100644
--- 
a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationCache.java
+++ 
b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationCache.java
@@ -31,10 +31,10 @@ import java.util.Set;
 import org.drools.base.factmodel.ClassDefinition;
 import org.drools.base.factmodel.FieldDefinition;
 import org.drools.base.factmodel.traits.Thing;
-import org.drools.base.rule.Annotated;
 import org.drools.base.rule.TypeDeclaration;
 import org.drools.compiler.compiler.PackageRegistry;
 import org.drools.compiler.compiler.TypeDeclarationError;
+import org.drools.drl.ast.descr.BaseDescr;
 import org.drools.drl.ast.descr.PackageDescr;
 import org.drools.util.ClassUtils;
 import org.kie.api.definition.type.Position;
@@ -245,13 +245,12 @@ public class TypeDeclarationCache {
     }
 
     private TypeDeclaration createTypeDeclarationForBean(Class<?> cls) {
-        Annotated annotated = new Annotated.ClassAdapter(cls);
-        TypeDeclaration typeDeclaration = 
TypeDeclaration.createTypeDeclarationForBean(cls, annotated, 
context.getBuilderConfiguration().getOption(PropertySpecificOption.KEY));
+        TypeDeclaration typeDeclaration = 
TypeDeclaration.createTypeDeclarationForBean(cls, 
context.getBuilderConfiguration().getOption(PropertySpecificOption.KEY));
 
         String namespace = ClassUtils.getPackage( cls );
         PackageRegistry pkgRegistry = context.getOrCreatePackageRegistry( new 
PackageDescr(namespace) );
 
-        processMvelBasedAccessors(context, results, pkgRegistry, annotated, 
typeDeclaration );
+        processMvelBasedAccessors(context, results, pkgRegistry, new 
BaseDescr(), typeDeclaration );
         return typeDeclaration;
     }
 
diff --git 
a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationConfigurator.java
 
b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationConfigurator.java
index 8b6b13061b1..d9b62ec6aaa 100644
--- 
a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationConfigurator.java
+++ 
b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationConfigurator.java
@@ -21,7 +21,6 @@ package org.drools.compiler.builder.impl;
 import java.util.Map;
 
 import org.drools.base.definitions.InternalKnowledgePackage;
-import org.drools.base.rule.Annotated;
 import org.drools.base.rule.TypeDeclaration;
 import org.drools.compiler.compiler.AnalysisResult;
 import org.drools.compiler.compiler.BoundIdentifiers;
@@ -31,14 +30,8 @@ import org.drools.compiler.compiler.TypeDeclarationError;
 import org.drools.compiler.rule.builder.PackageBuildContext;
 import org.drools.drl.ast.descr.AbstractClassTypeDeclarationDescr;
 import org.drools.drl.ast.descr.BaseDescr;
-import org.kie.api.definition.type.Duration;
-import org.kie.api.definition.type.Role;
-import org.kie.api.definition.type.Timestamp;
 import org.kie.internal.builder.conf.PropertySpecificOption;
 
-import static org.drools.base.rule.TypeDeclaration.processTypeAnnotations;
-import static 
org.drools.compiler.rule.builder.util.AnnotationFactory.toAnnotated;
-
 public class TypeDeclarationConfigurator {
 
     protected final TypeDeclarationContext context;
@@ -58,24 +51,8 @@ public class TypeDeclarationConfigurator {
             if ( oldType == null ) {
                 pkgRegistry.getPackage().addTypeDeclaration( type );
             } else {
-                if (type.getRole() == Role.Type.EVENT) {
-                    oldType.setRole(Role.Type.EVENT);
-                    if ( type.getDurationAttribute() != null ) {
-                        oldType.setDurationAttribute( 
type.getDurationAttribute() );
-                        oldType.setDurationExtractor( 
type.getDurationExtractor() );
-                    }
-                    if ( type.getTimestampAttribute() != null ) {
-                        oldType.setTimestampAttribute( 
type.getTimestampAttribute() );
-                        oldType.setTimestampExtractor( 
type.getTimestampExtractor() );
-                    }
-                    if ( type.getExpirationOffset() >= 0 ) {
-                        oldType.setExpirationOffset( 
type.getExpirationOffset() );
-                        oldType.setExpirationType( type.getExpirationPolicy() 
);
-                    }
-                }
-                if (type.isPropertyReactive()) {
-                    oldType.setPropertyReactive(true);
-                }
+                // the existing declaration is kept, so apply the annotations 
of this redeclaration to it
+                TypeDeclarationFactory.processAnnotations( typeDescr, oldType, 
context.getBuilderConfiguration().getOption( PropertySpecificOption.KEY ) );
             }
         }
     }
@@ -99,62 +76,34 @@ public class TypeDeclarationConfigurator {
             }
         }
 
-        // Annotations were pre-populated from Java and overridden by DRL in 
processAnnotations().
-        // The composite Annotated here re-applies them with DRL priority for 
@Timestamp/@Duration
-        // extractor wiring, which requires compiler infrastructure 
unavailable in earlier steps.
-        Annotated annotatedType = toAnnotated(typeDescr);
-        if (type.getTypeClass() != null) {
-            annotatedType = Annotated.withFallback(annotatedType, new 
Annotated.ClassAdapter(type.getTypeClass()));
-        }
-        processMvelBasedAccessors(context, results, pkgRegistry, 
annotatedType, type );
-        processTypeAnnotations( type, annotatedType, 
context.getBuilderConfiguration().getOption(PropertySpecificOption.KEY));
+        processMvelBasedAccessors(context, results, pkgRegistry, typeDescr, 
type );
         return true;
     }
 
-    static void processMvelBasedAccessors(TypeDeclarationContext context, 
BuildResultCollector results, PackageRegistry pkgRegistry, Annotated annotated, 
TypeDeclaration type ) {
-        wireTimestampAccessor(context, results, annotated, type, pkgRegistry );
-        wireDurationAccessor(context, results, annotated, type, pkgRegistry );
+    static void processMvelBasedAccessors(TypeDeclarationContext context, 
BuildResultCollector results, PackageRegistry pkgRegistry, BaseDescr typeDescr, 
TypeDeclaration type ) {
+        wireTimestampAccessor(context, results, typeDescr, type, pkgRegistry );
+        wireDurationAccessor(context, results, typeDescr, type, pkgRegistry );
     }
 
-    private static void wireTimestampAccessor(TypeDeclarationContext context, 
BuildResultCollector resultCollector, Annotated annotated, TypeDeclaration 
type, PackageRegistry pkgRegistry ) {
-        Timestamp timestamp = annotated.getTypedAnnotation(Timestamp.class);
-        if ( timestamp != null ) {
-            BaseDescr typeDescr = annotated instanceof BaseDescr ? ( 
(BaseDescr) annotated ) : new BaseDescr();
-            String timestampField;
-            try {
-                timestampField = timestamp.value();
-            } catch (Exception e) {
-                resultCollector.addBuilderResult(new 
TypeDeclarationError(typeDescr, e.getMessage()));
-                return;
-            }
-            type.setTimestampAttribute( timestampField );
+    private static void wireTimestampAccessor(TypeDeclarationContext context, 
BuildResultCollector resultCollector, BaseDescr typeDescr, TypeDeclaration 
type, PackageRegistry pkgRegistry ) {
+        String timestampField = type.getTimestampAttribute();
+        if ( timestampField != null ) {
             InternalKnowledgePackage pkg = pkgRegistry.getPackage();
-
             AnalysisResult results = getMvelAnalysisResult(context, typeDescr, 
type, pkgRegistry, timestampField, pkg );
             if (results != null) {
                 type.setTimestampExtractor(pkg.getFieldExtractor( type, 
timestampField, results.getReturnType() ));
             } else {
                 resultCollector.addBuilderResult(new 
TypeDeclarationError(typeDescr,
-                                                                   "Error 
creating field accessors for timestamp field '" + timestamp +
+                                                                   "Error 
creating field accessors for timestamp field '" + timestampField +
                                                                    "' for type 
'" + type.getTypeName() + "'"));
             }
         }
     }
 
-    private static void wireDurationAccessor(TypeDeclarationContext context, 
BuildResultCollector resultCollector, Annotated annotated, TypeDeclaration 
type, PackageRegistry pkgRegistry ) {
-        Duration duration = annotated.getTypedAnnotation(Duration.class);
-        if (duration != null) {
-            BaseDescr typeDescr = annotated instanceof BaseDescr ? ( 
(BaseDescr) annotated ) : new BaseDescr();
-            String durationField;
-            try {
-                durationField = duration.value();
-            } catch (Exception e) {
-                resultCollector.addBuilderResult(new 
TypeDeclarationError(typeDescr, e.getMessage()));
-                return;
-            }
-            type.setDurationAttribute(durationField);
+    private static void wireDurationAccessor(TypeDeclarationContext context, 
BuildResultCollector resultCollector, BaseDescr typeDescr, TypeDeclaration 
type, PackageRegistry pkgRegistry ) {
+        String durationField = type.getDurationAttribute();
+        if (durationField != null) {
             InternalKnowledgePackage pkg = pkgRegistry.getPackage();
-
             AnalysisResult results = getMvelAnalysisResult(context, typeDescr, 
type, pkgRegistry, durationField, pkg );
             if (results != null) {
                 type.setDurationExtractor(pkg.getFieldExtractor( type, 
durationField, results.getReturnType() ));
diff --git 
a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationFactory.java
 
b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationFactory.java
index 72564ce5159..50d722d2e0a 100644
--- 
a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationFactory.java
+++ 
b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/TypeDeclarationFactory.java
@@ -38,14 +38,13 @@ import org.drools.drl.ast.descr.EnumDeclarationDescr;
 import org.drools.drl.ast.descr.TypeDeclarationDescr;
 import org.drools.drl.ast.descr.TypeFieldDescr;
 import org.kie.api.definition.type.FactField;
-import org.kie.api.definition.type.PropertyChangeSupport;
-import org.kie.api.definition.type.Role;
 import org.kie.api.definition.type.TypeSafe;
 import org.kie.internal.builder.conf.PropertySpecificOption;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static 
org.drools.compiler.rule.builder.util.AnnotationFactory.getTypedAnnotation;
+import static 
org.drools.compiler.rule.builder.util.AnnotationFactory.toAnnotated;
 
 public class TypeDeclarationFactory {
     private static final Logger LOG = 
LoggerFactory.getLogger(TypeDeclarationFactory.class);
@@ -66,45 +65,28 @@ public class TypeDeclarationFactory {
             type = new TypeDeclaration( typeDescr.getTypeName() );
             type.setResource( typeDescr.getResource() );
 
-            // if is not new, search the already existing declaration and
-            // compare them o see if they are at least compatibles
-            // check whether it is necessary to build the class or not
             Class<?> existingClass = 
TypeDeclarationUtils.getExistingDeclarationClass( typeDescr, pkgRegistry );
             type.setTypeClass( existingClass );
             type.setNovel( existingClass == null );
             type.setNature( existingClass == null ? 
TypeDeclaration.Nature.DEFINITION : TypeDeclaration.Nature.DECLARATION );
-
-            // Pre-populate from Java class annotations so that 
finalizeConfigurator()
-            // (which runs before wireFieldAccessors) sees the correct role, 
expiration,
-            // reactivity, and dynamic settings. DRL annotations applied next 
will override.
-            if ( existingClass != null ) {
-                TypeDeclaration.processTypeAnnotations( type,
-                        new Annotated.ClassAdapter( existingClass ),
-                        context.getBuilderConfiguration().getOption( 
PropertySpecificOption.KEY ) );
-            }
         }
 
-        processTypeAnnotations(typeDescr, type);
-        return type;
-    }
-
-    private void processTypeAnnotations( AbstractClassTypeDeclarationDescr 
typeDescr, TypeDeclaration type ) {
         try {
-            processAnnotations( typeDescr, type );
+            processAnnotations( typeDescr, type, 
context.getBuilderConfiguration().getOption( PropertySpecificOption.KEY ) );
         } catch (Exception e) {
             results.addBuilderResult(new TypeDeclarationError(typeDescr, 
e.getMessage() ) );
         }
+        return type;
     }
 
-    // Processes annotations from the DRL descriptor, overriding values 
pre-populated from
-    // Java class annotations in processTypeDeclaration(). Runs before 
finalizeConfigurator(),
-    // which needs @Role and @PropertyChangeSupport already set.
-    public static void processAnnotations( AbstractClassTypeDeclarationDescr 
typeDescr, TypeDeclaration type ) {
-        Role role = getTypedAnnotation(typeDescr, Role.class);
-        if (role != null) {
-            type.setRole(role.value());
+    public static void processAnnotations( AbstractClassTypeDeclarationDescr 
typeDescr, TypeDeclaration type, PropertySpecificOption propertySpecificOption 
) {
+        Annotated annotated = toAnnotated(typeDescr);
+        if (type.getTypeClass() != null) {
+            annotated = Annotated.withFallback(annotated, new 
Annotated.ClassAdapter(type.getTypeClass()));
         }
+        TypeDeclaration.processTypeAnnotations( type, annotated, 
propertySpecificOption );
 
+        // @TypeSafe is read from DRL only, not from the Java class (see 
AlignAnnotationTest)
         TypeSafe typeSafe = getTypedAnnotation(typeDescr, TypeSafe.class);
         if (typeSafe != null) {
             type.setTypesafe(typeSafe.value());
@@ -115,10 +97,6 @@ public class TypeDeclarationFactory {
         } else if (typeDescr instanceof TypeDeclarationDescr && 
((TypeDeclarationDescr)typeDescr).isTrait()) {
             type.setKind(TypeDeclaration.Kind.TRAIT);
         }
-
-        if (typeDescr.hasAnnotation(PropertyChangeSupport.class)) {
-            type.setDynamic(true);
-        }
     }
 
     protected void checkRedeclaration( AbstractClassTypeDeclarationDescr 
typeDescr, TypeDeclaration type, PackageRegistry pkgRegistry ) {
@@ -253,8 +231,9 @@ public class TypeDeclarationFactory {
                 }
         }
 
-        //different duration -> Incompatible
-        if (!nullSafeEqualityComparison(oldDeclaration.getDurationAttribute(), 
newDeclaration.getDurationAttribute())) {
+        //different duration -> Incompatible (a redeclaration may add a 
duration, but not change or remove it)
+        if (oldDeclaration.getDurationAttribute() != null
+                && 
!nullSafeEqualityComparison(oldDeclaration.getDurationAttribute(), 
newDeclaration.getDurationAttribute())) {
             throw new IncompatibleClassChangeError("Type Declaration " + 
newDeclaration.getTypeName() + " has a different"
                                                    + " duration: " + 
newDeclaration.getDurationAttribute()
                                                    + " != " + 
oldDeclaration.getDurationAttribute());
diff --git 
a/drools-impact-analysis/drools-impact-analysis-parser/src/main/java/org/drools/impact/analysis/parser/internal/ImpactModelBuilderImpl.java
 
b/drools-impact-analysis/drools-impact-analysis-parser/src/main/java/org/drools/impact/analysis/parser/internal/ImpactModelBuilderImpl.java
index ba5c8377876..5668e627538 100644
--- 
a/drools-impact-analysis/drools-impact-analysis-parser/src/main/java/org/drools/impact/analysis/parser/internal/ImpactModelBuilderImpl.java
+++ 
b/drools-impact-analysis/drools-impact-analysis-parser/src/main/java/org/drools/impact/analysis/parser/internal/ImpactModelBuilderImpl.java
@@ -28,6 +28,7 @@ import 
org.drools.compiler.builder.impl.TypeDeclarationFactory;
 import org.drools.compiler.builder.impl.processors.AnnotationNormalizer;
 import 
org.drools.compiler.builder.impl.processors.OtherDeclarationCompilationPhase;
 import org.drools.compiler.compiler.PackageRegistry;
+import org.drools.compiler.compiler.TypeDeclarationError;
 import org.drools.compiler.lang.descr.CompositePackageDescr;
 import org.drools.base.definitions.InternalKnowledgePackage;
 import org.drools.base.rule.TypeDeclaration;
@@ -45,6 +46,7 @@ import org.drools.util.StringUtils;
 import org.kie.api.builder.ReleaseId;
 import org.kie.api.io.Resource;
 import org.kie.internal.builder.conf.LanguageLevelOption;
+import org.kie.internal.builder.conf.PropertySpecificOption;
 
 import static java.util.Collections.emptyList;
 import static 
org.drools.compiler.builder.impl.ClassDefinitionFactory.createClassDefinition;
@@ -197,16 +199,24 @@ public class ImpactModelBuilderImpl extends 
KnowledgeBuilderImpl {
             if (hasMvel()) {
                 type.setTypeClassDef( createClassDefinition( typeClass, 
typeDescr.getResource() ) );
             }
-            TypeDeclarationFactory.processAnnotations(typeDescr, type);
+            processAnnotations(typeDescr, type);
             getOrCreatePackageRegistry(new 
PackageDescr(typePkg)).getPackage().addTypeDeclaration(type );
         } catch (ClassNotFoundException e) {
             TypeDeclaration type = new TypeDeclaration( 
typeDescr.getTypeName() );
             type.setResource( typeDescr.getResource() );
-            TypeDeclarationFactory.processAnnotations(typeDescr, type);
+            processAnnotations(typeDescr, type);
             pkg.addTypeDeclaration( type );
         }
     }
 
+    private void processAnnotations(AbstractClassTypeDeclarationDescr 
typeDescr, TypeDeclaration type) {
+        try {
+            TypeDeclarationFactory.processAnnotations(typeDescr, type, 
getBuilderConfiguration().getOption(PropertySpecificOption.KEY));
+        } catch (Exception e) {
+            addBuilderResult(new TypeDeclarationError(typeDescr, 
e.getMessage()));
+        }
+    }
+
     protected void buildRules(Collection<CompositePackageDescr> packages) {
         if (hasErrors()) { // if Error while generating pojo do not try 
compile rule as they very likely depends hence fail too.
             return;
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
index e72d2405b3a..0b3bfaecbfb 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeCompilationPhase.java
@@ -33,6 +33,7 @@ import 
org.drools.model.codegen.execmodel.CanonicalModelBuildContext;
 import org.drools.model.codegen.execmodel.PackageModelManager;
 import org.drools.model.codegen.execmodel.generator.declaredtype.POJOGenerator;
 import org.kie.internal.builder.KnowledgeBuilderResult;
+import org.kie.internal.builder.conf.PropertySpecificOption;
 
 import static java.util.Arrays.asList;
 
@@ -59,7 +60,7 @@ public class DeclaredTypeCompilationPhase implements 
CompilationPhase {
     @Override
     public void process() {
         List<CompilationPhase> phases = asList(
-                iteratingPhase((reg, acc) -> new 
DeclaredTypeRegistrationPhase(reg, acc, pkgRegistryManager)),
+                iteratingPhase((reg, acc) -> new 
DeclaredTypeRegistrationPhase(reg, acc, pkgRegistryManager, 
buildConfiguration.getOption(PropertySpecificOption.KEY))),
                 iteratingPhase((reg, acc) ->
                         new POJOGenerator(reg.getPackage(), acc, 
packageModelManager.getPackageModel(acc, reg, reg.getPackage().getName()), 
packages, pkgRegistryManager)),
                 new GeneratedPojoCompilationPhase(
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeRegistrationPhase.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeRegistrationPhase.java
index cd37a419e75..94789eaa9a4 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeRegistrationPhase.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/processors/DeclaredTypeRegistrationPhase.java
@@ -23,6 +23,7 @@ import 
org.drools.compiler.builder.impl.TypeDeclarationFactory;
 import 
org.drools.compiler.builder.impl.processors.AbstractPackageCompilationPhase;
 import org.drools.compiler.builder.impl.processors.AnnotationNormalizer;
 import org.drools.compiler.compiler.PackageRegistry;
+import org.drools.compiler.compiler.TypeDeclarationError;
 import org.drools.base.definitions.InternalKnowledgePackage;
 import org.drools.base.rule.TypeDeclaration;
 import org.drools.drl.ast.descr.AbstractClassTypeDeclarationDescr;
@@ -32,6 +33,7 @@ import org.drools.drl.ast.descr.PackageDescr;
 import org.drools.drl.ast.descr.TypeDeclarationDescr;
 import org.drools.model.codegen.execmodel.errors.UnsupportedFeatureError;
 import org.drools.util.TypeResolver;
+import org.kie.internal.builder.conf.PropertySpecificOption;
 
 import static 
org.drools.compiler.builder.impl.ClassDefinitionFactory.createClassDefinition;
 import static org.drools.base.util.Drools.hasMvel;
@@ -39,10 +41,12 @@ import static org.drools.base.util.Drools.hasMvel;
 public class DeclaredTypeRegistrationPhase extends 
AbstractPackageCompilationPhase {
 
     private final PackageRegistryManager pkgRegistryManager;
+    private final PropertySpecificOption propertySpecificOption;
 
-    public DeclaredTypeRegistrationPhase(PackageRegistry pkgRegistry, 
PackageDescr packageDescr, PackageRegistryManager pkgRegistryManager) {
+    public DeclaredTypeRegistrationPhase(PackageRegistry pkgRegistry, 
PackageDescr packageDescr, PackageRegistryManager pkgRegistryManager, 
PropertySpecificOption propertySpecificOption) {
         super(pkgRegistry, packageDescr);
         this.pkgRegistryManager = pkgRegistryManager;
+        this.propertySpecificOption = propertySpecificOption;
     }
 
     @Override
@@ -68,7 +72,7 @@ public class DeclaredTypeRegistrationPhase extends 
AbstractPackageCompilationPha
             if (hasMvel()) {
                 type.setTypeClassDef( createClassDefinition( typeClass, 
typeDescr.getResource() ) );
             }
-            TypeDeclarationFactory.processAnnotations(typeDescr, type);
+            processAnnotations(typeDescr, type);
             if (!type.isTypesafe()) {
                 results.addBuilderResult(new 
UnsupportedFeatureError("@typesafe(false) is not supported in executable model 
: " + type));
             }
@@ -77,11 +81,19 @@ public class DeclaredTypeRegistrationPhase extends 
AbstractPackageCompilationPha
         } catch (ClassNotFoundException e) {
             TypeDeclaration type = new TypeDeclaration( 
typeDescr.getTypeName() );
             type.setResource( typeDescr.getResource() );
-            TypeDeclarationFactory.processAnnotations(typeDescr, type);
+            processAnnotations(typeDescr, type);
             pkg.addTypeDeclaration( type );
         }
     }
 
+    private void processAnnotations(AbstractClassTypeDeclarationDescr 
typeDescr, TypeDeclaration type) {
+        try {
+            TypeDeclarationFactory.processAnnotations(typeDescr, type, 
propertySpecificOption);
+        } catch (Exception e) {
+            results.addBuilderResult(new TypeDeclarationError(typeDescr, 
e.getMessage()));
+        }
+    }
+
     protected void normalizeAnnotations(AnnotatedBaseDescr 
annotationsContainer, TypeResolver typeResolver, boolean isStrict) {
         AnnotationNormalizer annotationNormalizer =
                 AnnotationNormalizer.of(
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
index 6fdbc70abf0..62f06961349 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/tool/ExplicitCanonicalModelCompiler.java
@@ -55,6 +55,7 @@ import 
org.drools.model.codegen.execmodel.processors.GeneratedPojoCompilationPha
 import org.drools.model.codegen.execmodel.processors.ModelGeneratorPhase;
 import org.drools.model.codegen.execmodel.processors.PojoStoragePhase;
 import org.drools.model.codegen.execmodel.processors.SourceCodeGenerationPhase;
+import org.kie.internal.builder.conf.PropertySpecificOption;
 import org.kie.util.maven.support.ReleaseIdImpl;
 
 /**
@@ -129,7 +130,7 @@ public class ExplicitCanonicalModelCompiler<T extends 
PackageSources> {
     public void process() {
         List<CompilationPhase> phases = new ArrayList<>();
 
-        phases.add(iteratingPhase((reg, acc) -> new 
DeclaredTypeRegistrationPhase(reg, acc, pkgRegistryManager)));
+        phases.add(iteratingPhase((reg, acc) -> new 
DeclaredTypeRegistrationPhase(reg, acc, pkgRegistryManager, 
configuration.getOption(PropertySpecificOption.KEY))));
         phases.add(iteratingPhase((reg, acc) ->
                 new POJOGenerator(reg.getPackage(), acc, 
packageModelManager.getPackageModel(acc, reg, reg.getPackage().getName()), 
packages, pkgRegistryManager)));
         phases.add(new GeneratedPojoCompilationPhase(
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
index a599fad156b..a0d4ae3a559 100644
--- 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/DeclaredTypesTest.java
@@ -912,4 +912,21 @@ public class DeclaredTypesTest extends BaseModelTest {
                 .containsExactlyInAnyOrder("Unknown annotation property yyy",
                                            "Unknown annotation property zzz");
     }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void testInvalidAnnotationValueReportsBuildError(RUN_TYPE runType) {
+        String str = "package org.example.invalid; \n" +
+                "declare Foo\n" +
+                "    @role( event )\n" +
+                "    @expires( abc )\n" +
+                "    name : String\n" +
+                "end";
+
+        Results results = createKieBuilder(runType, str).getResults();
+
+        assertThat(results.getMessages(Message.Level.ERROR))
+                .extracting(Message::getText)
+                .anyMatch(text -> text.contains("Error parsing time string: [ 
abc ]"));
+    }
 }
\ No newline at end of file
diff --git 
a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
 
b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
index be2c5611d87..a298feeca50 100644
--- 
a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
+++ 
b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/util/TypeDeclarationUtil.java
@@ -39,11 +39,9 @@ import 
org.drools.modelcompiler.constraints.LambdaFieldReader;
 import org.drools.modelcompiler.constraints.LambdaReadAccessor;
 import org.drools.util.ClassUtils;
 import org.drools.util.TypeResolver;
-import org.kie.api.definition.type.Duration;
 import org.kie.api.definition.type.Expires;
 import org.kie.api.definition.type.Position;
 import org.kie.api.definition.type.Role;
-import org.kie.api.definition.type.Timestamp;
 import org.kie.internal.builder.conf.PropertySpecificOption;
 
 import static 
org.drools.base.rule.TypeDeclaration.createTypeDeclarationForBean;
@@ -63,7 +61,7 @@ public class TypeDeclarationUtil {
                 new AccessibleClassDefinition( typeClass, typeResolver ) :
                 new DynamicClassDefinition( typeClass ) );
 
-        wireClassAnnotations( typeClass, typeDeclaration );
+        wireTimeAccessors( typeDeclaration );
         wireMetaTypeAnnotations( metaType, typeDeclaration );
         wireFields(typeClass, typeDeclaration);
 
@@ -157,15 +155,13 @@ public class TypeDeclarationUtil {
         }
     }
 
-    private static void wireClassAnnotations( Class<?> typeClass, 
TypeDeclaration typeDeclaration ) {
-        Duration duration = typeClass.getAnnotation( Duration.class );
-        if (duration != null) {
-            wireDurationAccessor( duration.value(), typeDeclaration );
+    // the attribute names were already set from the class annotations by 
createTypeDeclarationForBean()
+    private static void wireTimeAccessors( TypeDeclaration typeDeclaration ) {
+        if (typeDeclaration.getDurationAttribute() != null) {
+            wireDurationAccessor( typeDeclaration.getDurationAttribute(), 
typeDeclaration );
         }
-
-        Timestamp timestamp = typeClass.getAnnotation( Timestamp.class );
-        if (timestamp != null) {
-            wireTimestampAccessor( timestamp.value(), typeDeclaration );
+        if (typeDeclaration.getTimestampAttribute() != null) {
+            wireTimestampAccessor( typeDeclaration.getTimestampAttribute(), 
typeDeclaration );
         }
     }
 
@@ -179,16 +175,7 @@ public class TypeDeclarationUtil {
 
     public static TypeDeclaration createTypeDeclaration(Class<?> cls, 
PropertySpecificOption propertySpecificOption) {
         TypeDeclaration typeDeclaration = createTypeDeclarationForBean( cls, 
propertySpecificOption );
-
-        Duration duration = cls.getAnnotation( Duration.class );
-        if (duration != null) {
-            wireDurationAccessor( duration.value(), typeDeclaration );
-        }
-        Timestamp timestamp = cls.getAnnotation( Timestamp.class );
-        if (timestamp != null) {
-            wireTimestampAccessor( timestamp.value(), typeDeclaration );
-        }
-
+        wireTimeAccessors( typeDeclaration );
         return typeDeclaration;
     }
 
diff --git 
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/compiler/TypeDeclarationTest.java
 
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/compiler/TypeDeclarationTest.java
index 9a4f1ca37a9..1dc9bd547ed 100644
--- 
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/compiler/TypeDeclarationTest.java
+++ 
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/compiler/TypeDeclarationTest.java
@@ -40,6 +40,7 @@ import org.drools.testcoverage.common.util.KieBaseUtil;
 import org.drools.testcoverage.common.util.KieUtil;
 import org.drools.testcoverage.common.util.TestConstants;
 import org.drools.testcoverage.common.util.TestParametersUtil2;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.kie.api.KieBase;
@@ -149,6 +150,96 @@ public class TypeDeclarationTest {
 
     }
 
+    private static final String EVENT_WITH_DURATION =
+            "package org.kie \n" +
+            "declare org.kie.EventA \n" +
+            "    @role (event) \n" +
+            "    @duration (duration) \n" +
+            "    name : String \n" +
+            "    duration : Long \n" +
+            "    duration2 : Long \n" +
+            "end \n";
+
+    @Test
+    public void testDurationRedeclarationWithSameDuration() {
+        KnowledgeBuilder kbuilder = buildTwice(EVENT_WITH_DURATION, 
EVENT_WITH_DURATION);
+
+        
assertThat(kbuilder.hasErrors()).as(kbuilder.getErrors().toString()).isFalse();
+        TypeDeclaration typeDeclaration = 
((KnowledgePackageImpl)kbuilder.getKnowledgePackages().iterator().next()).getTypeDeclaration("EventA");
+        
assertThat(typeDeclaration.getDurationAttribute()).isEqualTo("duration");
+    }
+
+    @Test
+    public void testDurationRedeclarationWithoutDurationKeepsIt() {
+        String str2 = EVENT_WITH_DURATION.replace("    @duration (duration) 
\n", "");
+
+        KnowledgeBuilder kbuilder = buildTwice(EVENT_WITH_DURATION, str2);
+
+        
assertThat(kbuilder.hasErrors()).as(kbuilder.getErrors().toString()).isFalse();
+        TypeDeclaration typeDeclaration = 
((KnowledgePackageImpl)kbuilder.getKnowledgePackages().iterator().next()).getTypeDeclaration("EventA");
+        
assertThat(typeDeclaration.getDurationAttribute()).isEqualTo("duration");
+    }
+
+    @Test
+    public void testDurationRedeclarationWithDifferentDuration() {
+        String str2 = EVENT_WITH_DURATION.replace("@duration (duration)", 
"@duration (duration2)");
+
+        KnowledgeBuilder kbuilder = buildTwice(EVENT_WITH_DURATION, str2);
+
+        assertThat(kbuilder.getErrors().toString()).contains("has a different 
duration: duration2 != duration");
+    }
+
+    private static final String FACT_FOO =
+            "package org.kie \n" +
+            "declare org.kie.Foo \n" +
+            "    name : String \n" +
+            "end \n";
+
+    private static String redeclareFoo(String annotations) {
+        return "package org.kie \n" +
+               "declare org.kie.Foo \n" +
+               "    " + annotations + " \n" +
+               "end \n";
+    }
+
+    private static TypeDeclaration getFooAfterRedeclaration(String drl1, 
String drl2) {
+        KnowledgeBuilder kbuilder = buildTwice(drl1, drl2);
+        
assertThat(kbuilder.hasErrors()).as(kbuilder.getErrors().toString()).isFalse();
+        return 
((KnowledgePackageImpl)kbuilder.getKnowledgePackages().iterator().next()).getTypeDeclaration("Foo");
+    }
+
+    @Test
+    public void testRedeclarationWithClassReactive() {
+        TypeDeclaration typeDeclaration = getFooAfterRedeclaration(FACT_FOO, 
redeclareFoo("@classReactive"));
+        assertThat(typeDeclaration.isPropertyReactive()).isFalse();
+    }
+
+    @Test
+    public void testRedeclarationWithPropertyChangeSupport() {
+        TypeDeclaration typeDeclaration = getFooAfterRedeclaration(FACT_FOO, 
redeclareFoo("@propertyChangeSupport"));
+        assertThat(typeDeclaration.isDynamic()).isTrue();
+    }
+
+    @Test
+    public void testRedeclarationWithRoleFact() {
+        String eventFoo = FACT_FOO.replace("declare org.kie.Foo \n", "declare 
org.kie.Foo \n    @role (event) \n");
+        TypeDeclaration typeDeclaration = getFooAfterRedeclaration(eventFoo, 
redeclareFoo("@role(fact)"));
+        assertThat(typeDeclaration.getRole()).isEqualTo(Role.Type.FACT);
+    }
+
+    @Test
+    public void testRedeclarationWithExpiresWithoutRole() {
+        TypeDeclaration typeDeclaration = getFooAfterRedeclaration(FACT_FOO, 
redeclareFoo("@expires(10s)"));
+        assertThat(typeDeclaration.getExpirationOffset()).isEqualTo(10_000L);
+    }
+
+    private static KnowledgeBuilder buildTwice(String drl1, String drl2) {
+        KnowledgeBuilder kbuilder = 
KnowledgeBuilderFactory.newKnowledgeBuilder();
+        kbuilder.add( ResourceFactory.newByteArrayResource( drl1.getBytes() ), 
ResourceType.DRL );
+        kbuilder.add( ResourceFactory.newByteArrayResource( drl2.getBytes() ), 
ResourceType.DRL );
+        return kbuilder;
+    }
+
     @ParameterizedTest(name = "KieBase type={0}")
        @MethodSource("parameters")
     public void testNoAnnotationUpdateIfError(){


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to