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

ahuber pushed a commit to branch 3969-jpa.#safeguard
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 730a93b98f9eeb33232134bed0972f56778448e3
Author: andi-huber <[email protected]>
AuthorDate: Wed Feb 18 13:58:18 2026 +0100

    CAUSEWAY-3969: adds #safeguard (stubs)
    
    Task-Url: https://issues.apache.org/jira/browse/CAUSEWAY-3969
---
 .../core/config/CausewayConfiguration.java         | 45 +++++++++++++----
 .../CausewayModulePersistenceJpaIntegration.java   |  4 +-
 .../integration/services/JpaWeavingSafeguard.java  | 59 ++++++++++++++++++++++
 .../services/JpaWeavingSafeguardService.java       | 54 ++++++++++++++++++++
 4 files changed, 151 insertions(+), 11 deletions(-)

diff --git 
a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
 
b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
index ce719995e52..19ff68a02d3 100644
--- 
a/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
+++ 
b/core/config/src/main/java/org/apache/causeway/core/config/CausewayConfiguration.java
@@ -90,6 +90,8 @@
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.base._StableValue;
 import org.apache.causeway.commons.internal.context._Context;
+import org.apache.causeway.core.config.CausewayConfiguration.Core;
+import org.apache.causeway.core.config.CausewayConfiguration.Viewer;
 import org.apache.causeway.core.config.metamodel.facets.ActionConfigOptions;
 import 
org.apache.causeway.core.config.metamodel.facets.AssociationLayoutConfigOptions;
 import 
org.apache.causeway.core.config.metamodel.facets.CollectionLayoutConfigOptions;
@@ -1030,7 +1032,7 @@ public CssClass(
 
                             "approve.*:btn-success",
                             "reject.*:btn-danger",
-                        })
+                        }) final
                         String[] patterns) {
                         this(patterns, new _StableValue<>());
                     }
@@ -1161,7 +1163,7 @@ public CssClassFa(
 
                             "wizard.*:fa-solid fa-wand-magic-sparkles"
 
-                        })
+                        }) final
                         String[] patterns) {
                         this(patterns, new _StableValue<>());
                     }
@@ -1993,7 +1995,9 @@ public record Persistence(
         @DefaultValue
         Commons commons,
         @DefaultValue
-        Schema schema) {
+        Schema schema,
+        @DefaultValue
+        Weaving weaving) {
 
         public record Commons(
             @DefaultValue
@@ -2084,6 +2088,28 @@ public record Schema(
             @DefaultValue("CREATE SCHEMA IF NOT EXISTS %S")
             String createSchemaSqlTemplate) {
         }
+
+        public record Weaving(
+                @DefaultValue("REQUIRE_WEAVED_WHEN_ANY_SUB_IS_WEAVED")
+                SafeguardMode safeguardMode) {
+            public enum SafeguardMode {
+                /**
+                 * Safeguard only logs warnings, but otherwise does not 
prevent an application from launching.
+                 */
+                LOG_ONLY,
+                /**
+                 * (Default) Requires for any entity type hierarchy that when 
classes are weaved,
+                 * their super classes also need to be weaved.
+                 *
+                 * <p>Prevents entity type hierarchies from failing later at 
runtime.
+                 */
+                REQUIRE_WEAVED_WHEN_ANY_SUB_IS_WEAVED,
+                /**
+                 * Enforces weaving on all encountered entity type hierarchies.
+                 */
+                REQUIRE_WEAVED
+            }
+        }
     }
 
     public record Prototyping(
@@ -3167,21 +3193,21 @@ public record DevelopmentUtilities(
                 @DefaultValue("false")
                 boolean enable) {
             }
-            
+
             public record FileUpload(
                     /**
-                     * If left empty, the default allows ['image', 'html', 
'text', 'video', 'audio', 'flash', 'object'], 
+                     * If left empty, the default allows ['image', 'html', 
'text', 'video', 'audio', 'flash', 'object'],
                      * where 'object' enables fallback behavior. We remove 
this here.
-                     *  
+                     *
                      * @see 
https://plugins.krajee.com/file-input/plugin-options#disabledPreviewTypes
                      */
                     @DefaultValue({"object"})
                     List<String> disabledPreviewTypes,
                        /**
                         * Some mime types can trigger unwanted download 
behavior, dependent on browser and or OS settings.
-                        * 
+                        *
                         * <p>We have seen CSV files causing issues, so we 
disallow those by default.
-                        * 
+                        *
                         * @see 
https://plugins.krajee.com/file-input/plugin-options#disabledPreviewMimeTypes
                         */
                        @DefaultValue({"text/csv"})
@@ -4331,9 +4357,8 @@ public void initialize(final AssignableFrom 
assignableFrom) {
         public boolean isValid(
             final Class<?> candidateClass,
             final ConstraintValidatorContext constraintContext) {
-            if (superType.get() == null || candidateClass == null) {
+            if (superType.get() == null || candidateClass == null)
                 return true;
-            }
             return superType.get().isAssignableFrom(candidateClass);
         }
     }
diff --git 
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/CausewayModulePersistenceJpaIntegration.java
 
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/CausewayModulePersistenceJpaIntegration.java
index d01144e19e2..34ae16da4db 100644
--- 
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/CausewayModulePersistenceJpaIntegration.java
+++ 
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/CausewayModulePersistenceJpaIntegration.java
@@ -34,6 +34,7 @@
 import 
org.apache.causeway.persistence.commons.CausewayModulePersistenceCommons;
 import 
org.apache.causeway.persistence.jpa.integration.entity.JpaEntityIntegration;
 import 
org.apache.causeway.persistence.jpa.integration.services.JpaSupportServiceUsingSpring;
+import 
org.apache.causeway.persistence.jpa.integration.services.JpaWeavingSafeguardService;
 import 
org.apache.causeway.persistence.jpa.integration.typeconverters.applib.CausewayBookmarkConverter;
 import 
org.apache.causeway.persistence.jpa.integration.typeconverters.applib.CausewayLocalResourcePathConverter;
 import 
org.apache.causeway.persistence.jpa.integration.typeconverters.applib.CausewayMarkupConverter;
@@ -63,6 +64,7 @@
 
         // @Service's
         JpaSupportServiceUsingSpring.class,
+        JpaWeavingSafeguardService.class,
 
 })
 @EntityScan(basePackageClasses = {
@@ -89,7 +91,7 @@ public class CausewayModulePersistenceJpaIntegration {
 
     //TODO close issue https://issues.apache.org/jira/browse/CAUSEWAY-3895 
once this can be removed
     @Bean @Primary
-    public JpaContext defaultJpaContextWorkaround(Set<EntityManager> 
entityManagers) {
+    public JpaContext defaultJpaContextWorkaround(final Set<EntityManager> 
entityManagers) {
         var setOfOne = Set.of(entityManagers.iterator().next());
         return new DefaultJpaContext(setOfOne);
     }
diff --git 
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/services/JpaWeavingSafeguard.java
 
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/services/JpaWeavingSafeguard.java
new file mode 100644
index 00000000000..424d28ec6cb
--- /dev/null
+++ 
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/services/JpaWeavingSafeguard.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.causeway.persistence.jpa.integration.services;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.causeway.commons.internal.reflection._ClassCache;
+import org.apache.causeway.commons.internal.reflection._Reflect;
+import 
org.apache.causeway.commons.internal.reflection._Reflect.InterfacePolicy;
+import org.apache.causeway.core.config.CausewayConfiguration;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public record JpaWeavingSafeguard(
+        CausewayConfiguration.Persistence.Weaving.SafeguardMode mode,
+        _ClassCache classCache) {
+
+    public JpaWeavingSafeguard(final 
CausewayConfiguration.Persistence.Weaving.SafeguardMode mode) {
+        this(mode, _ClassCache.getInstance());
+    }
+
+    public void checkAll(final Iterable<Class<?>> entityTypes) {
+        var seen = new HashSet<Class<?>>();
+        for(final Class<?> entityType : entityTypes) {
+            _Reflect.streamTypeHierarchy(entityType, InterfacePolicy.EXCLUDE)
+                .forEach(type->check(type, seen));
+        }
+    }
+
+    private void check(final Class<?> entityType, final Set<Class<?>> seen) {
+        if(!seen.add(entityType))
+            return; // skip when already checked
+
+        log.info("weaved {}{}",
+                classCache.isByteCodeEnhanced(entityType)
+                    ? "[#]"
+                    : "[-]",
+                entityType.getName());
+    }
+
+}
diff --git 
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/services/JpaWeavingSafeguardService.java
 
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/services/JpaWeavingSafeguardService.java
new file mode 100644
index 00000000000..855ea6bc891
--- /dev/null
+++ 
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/services/JpaWeavingSafeguardService.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.causeway.persistence.jpa.integration.services;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import jakarta.inject.Inject;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.causeway.applib.events.metamodel.MetamodelListener;
+import org.apache.causeway.core.config.CausewayConfiguration;
+import 
org.apache.causeway.core.config.beans.CausewayBeanMetaData.PersistenceStack;
+import org.apache.causeway.core.config.beans.CausewayBeanTypeRegistry;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Service
+@Slf4j
+public class JpaWeavingSafeguardService implements MetamodelListener {
+
+    @Inject CausewayConfiguration config;
+    @Inject CausewayBeanTypeRegistry causewayBeanTypeRegistry;
+
+    @Override public void onMetamodelLoaded() { }
+    @Override public void onMetamodelAboutToBeLoaded() {
+        var safeguardMode = 
Optional.ofNullable(config.persistence().weaving().safeguardMode())
+                
.orElse(CausewayConfiguration.Persistence.Weaving.SafeguardMode.REQUIRE_WEAVED_WHEN_ANY_SUB_IS_WEAVED);
+        var jpaWeavingSafeguard = new JpaWeavingSafeguard(safeguardMode);
+        jpaWeavingSafeguard.checkAll(causewayBeanTypeRegistry
+                .streamEntityTypes(PersistenceStack.JPA)
+                .collect(Collectors.toCollection(HashSet::new)));
+    }
+
+}
+

Reply via email to