Skip to content

chore: import vaadin-cdi as a Flow module and run its tests in validation - #25525

Open
totally-not-ai[bot] wants to merge 632 commits into
mainfrom
chore/import-cdi-to-flow
Open

chore: import vaadin-cdi as a Flow module and run its tests in validation#25525
totally-not-ai[bot] wants to merge 632 commits into
mainfrom
chore/import-cdi-to-flow

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Brings the vaadin/cdi repository into Flow as a first-class module, so the CDI integration is built, versioned and tested together with the rest of Flow instead of tracking it from a separate repository.

What changed

Module layout

  • vaadin-cdi is imported with its full git history and added to the root reactor and to flow-bom, so it releases with Flow's version number.
  • Its Arquillian suite is reparented as flow-tests/vaadin-cdi-tests. The flow-tests parent already supplies flow-client, flow-html-components, flow-test-util, JUnit and the prerelease repositories, so those declarations and the module's own flow-maven-plugin version are gone; Arquillian and ShrinkWrap versions stay module-local the way vaadin-spring-tests keeps its Spring ones.
  • Every test in that module deploys to a real application server, so surefire is skipped via cdi.container.tests.skip unless one of the container profiles (tomee, wildfly, payara, liberty, tomcat-weld) is active — otherwise a plain mvn verify over flow-tests would fail.
  • Flow's spotless configuration applied. The Eclipse formatter profile and license header are byte-identical to the ones the CDI repo used, so the Java churn is limited to what the newer spotless version reflows; the Polymer test template is reformatted because Flow also runs prettier over frontend sources.
  • Root POM gains managed weld-se-core / weld-junit5 versions for the module's unit tests.

CI

  • computeMatrix.js excludes flow-tests/vaadin-cdi-tests — the generated it-tests matrix has no way to express "deploy to an application server" — and validation.yml gets a dedicated cdi-tests job with the five container profiles.
  • Running all five on every Flow change would add a ~30 minute tail, so only tomee runs unconditionally as a smoke test; the other four run when the CDI sources themselves change, gated the same way gradle-tests gates its expensive steps (and running everything when there is no base to diff against).
  • test-results and cleanup-artifacts now depend on cdi-tests, so a failing container leg flips the aggregate check and the CDI surefire reports reliably reach the published report instead of racing artifact deletion.
  • The Run CDI ITs step uses the detached stdio the it-tests job uses rather than piping through tee: piping made the step exit with tee's status (so every container failure reported success), and an application server JVM outliving Maven could pin the step open by holding the pipe.

Build problems found while wiring it up

  • Non-parseable effective POM. ShrinkWrap builds each WAR by reading target/effective-pom.xml with a strict POM parser, and what help:effective-pom writes under the Flow parent is not well-formed XML: the plugin's banner contributes one XML declaration and the rendered model a second (rejected as a "PITarget with reserved xml name"), and build-helper:parse-version — which the Flow parent runs and the CDI parent did not — sets a property whose name ends in ?, which Maven serialises as an illegal XML element name. An antrun execution strips both and then runs xmlvalidate, so a future change of shape fails on the spot instead of resurfacing as every test failing at its @Deployment method.
  • Arquillian convergence. The liberty leg failed the DependencyConvergence rule the Flow parent enforces: the Liberty adapter pulls Arquillian core in at 1.7.0.Final, diverging from arquillian-junit-container across nine artifacts. Importing arquillian-bom in place of the single managed entry pins all of them at the version nearest-wins already resolved, for every container profile.
  • Inherited antrun execution. Declaring maven-antrun-plugin also activated the delete-lib-folder execution the flow-tests parent manages, which deletes an exploded WAR's WEB-INF/lib. This module is a jar and ShrinkWrap assembles archives in process, so that directory never exists and the execution printed an Ant BuildException and a stack trace on every run — non-fatal, but indistinguishable from a real failure. Bound to phase none here.
  • Enforcer never ran. The dependencyConvergence rule lives in the enforcer default-cli execution, so no lifecycle phase evaluates it and nothing in CI would have caught the divergence above or a regression of it. The CDI job names enforcer:enforce explicitly, which evaluates it once per container profile — the profiles are what bring the conflicting adapters.
  • The slow test profile is renamed slow-tests to match the id the rest of the repository uses.

Docs

  • vaadin-cdi added to the module maps in CLAUDE.md and guidelines/repository.md.
  • vaadin-cdi/README.md links repointed from vaadin/cdi paths to in-repo paths.
  • flow-tests/vaadin-cdi-tests/README.md records how to select a container profile and how to combine slow-tests with one — the upstream root README carried those instructions and was not imported.

API Changes

Nothing here is new to application developers: these are the same types that ship today in the separately released com.vaadin:vaadin-cdi artifact, and they now appear as additions only because the module moved into this repository. No public or protected declaration changed during the import — the commits after the history import touch only a private logger field and formatting.

32 top-level public types added (plus 5 public nested types), no removals, no signature changes. Package-private types (BeanLookup, DeploymentValidator, VaadinTaskExecutorSelector, AbstractContextualStorageManager) and the test fixtures under flow-tests/vaadin-cdi-tests/src/main/java are not API and are omitted.

com.vaadin.cdi.AbstractCdiInstantiator

// Added
public abstract class AbstractCdiInstantiator implements Instantiator
protected abstract DefaultInstantiator getDelegate()
public abstract BeanManager getBeanManager()
public <T> T getOrCreate(Class<T> type)
public I18NProvider getI18NProvider()
public MenuAccessControl getMenuAccessControl()
public PageTitleGenerator getPageTitleGenerator()
public Stream<VaadinServiceInitListener> getServiceInitListeners()

com.vaadin.cdi.CdiInstantiator

// Added
public class CdiInstantiator extends AbstractCdiInstantiator
public CdiInstantiator(BeanManager beanManager, VaadinService service)
protected DefaultInstantiator getDelegate()
public BeanManager getBeanManager()
public <T extends Component> T createComponent(Class<T> componentClass)

com.vaadin.cdi.CdiInstantiatorFactory

// Added
public class CdiInstantiatorFactory implements InstantiatorFactory
public Instantiator createInstantitor(VaadinService service)
public Class<? extends VaadinService> getServiceClass()

com.vaadin.cdi.CdiServletDeployer

// Added
public class CdiServletDeployer implements ServletContainerInitializer
public void onStartup(Set<Class<?>> classSet, ServletContext ctx)

com.vaadin.cdi.CdiVaadinServlet

// Added
public class CdiVaadinServlet extends VaadinServlet
public void init(ServletConfig servletConfig) throws ServletException
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
public static String getCurrentServletName()
protected VaadinServletService createServletService(DeploymentConfiguration configuration) throws ServiceException

com.vaadin.cdi.CdiVaadinServletService

// Added
public class CdiVaadinServletService extends VaadinServletService
public CdiVaadinServletService(CdiVaadinServlet servlet, DeploymentConfiguration configuration, BeanManager beanManager)
public void init() throws ServiceException
protected VaadinSession loadSession(WrappedSession wrappedSession)
protected void storeSession(VaadinSession session, WrappedSession wrappedSession)
protected Executor createDefaultExecutor()
public Optional<Instantiator> loadInstantiators() throws ServiceException
public CdiVaadinServlet getServlet()

com.vaadin.cdi.CdiVaadinServletService.CdiVaadinServiceDelegate

// Added
public static class CdiVaadinServiceDelegate implements Serializable
public CdiVaadinServiceDelegate(BeanManager beanManager)
public void init(VaadinService vaadinService) throws ServiceException
public void addUIListeners(UI ui)
public <T> Optional<T> lookup(Class<T> type) throws ServiceException
public BeanManager getBeanManager()
public void uiInit(UIInitEvent event)
public void afterNavigation(AfterNavigationEvent event)
public void beforeEnter(BeforeEnterEvent event)
public void beforeLeave(BeforeLeaveEvent event)
public void onComponentEvent(PollEvent event)

com.vaadin.cdi.UIDetachEvent

// Added
public class UIDetachEvent extends DetachEvent
public UIDetachEvent(DetachEvent event)

com.vaadin.cdi.VaadinExtension

// Added
public class VaadinExtension implements Extension // CDI portable extension, registered via META-INF/services; no public members

com.vaadin.cdi.annotation.CdiComponent

// Added
public @interface CdiComponent // @Stereotype, no members

com.vaadin.cdi.annotation.NormalRouteScoped

// Added
public @interface NormalRouteScoped // @NormalScope, no members

com.vaadin.cdi.annotation.NormalUIScoped

// Added
public @interface NormalUIScoped // @NormalScope, no members

com.vaadin.cdi.annotation.RouteScoped

// Added
public @interface RouteScoped // @Scope (pseudo scope), no members

com.vaadin.cdi.annotation.RouteScopeOwner

// Added
public @interface RouteScopeOwner // @Qualifier
Class<? extends HasElement> value()

com.vaadin.cdi.annotation.UIScoped

// Added
public @interface UIScoped // @Scope (pseudo scope), no members

com.vaadin.cdi.annotation.VaadinServiceEnabled

// Added
public @interface VaadinServiceEnabled // @Qualifier, no members

com.vaadin.cdi.annotation.VaadinServiceScoped

// Added
public @interface VaadinServiceScoped // @NormalScope, no members

com.vaadin.cdi.annotation.VaadinSessionScoped

// Added
public @interface VaadinSessionScoped // @NormalScope, no members

com.vaadin.cdi.context.ContextWrapper

// Added
public class ContextWrapper implements AlterableContext
public ContextWrapper(AbstractContext context, Class<? extends Annotation> scope)
public Class<? extends Annotation> getScope()
public <T> T get(Contextual<T> component, CreationalContext<T> creationalContext)
public <T> T get(Contextual<T> component)
public boolean isActive()
public void destroy(Contextual<?> contextual)

com.vaadin.cdi.context.RouteScopedContext

// Added
public class RouteScopedContext extends AbstractContext
public RouteScopedContext(BeanManager beanManager)
public void init(BeanManager beanManager, Supplier<Boolean> isUIContextActive)
public Class<? extends Annotation> getScope()
public boolean isActive()
protected List<ContextualStorage> getActiveContextualStorages()
protected ContextualStorage getContextualStorage(Contextual<?> contextual, boolean createIfNotExist)

com.vaadin.cdi.context.RouteScopedContext.ContextualStorageManager

// Added
public static class ContextualStorageManager extends AbstractContextualStorageManager<RouteStorageKey>
public ContextualStorageManager()
protected ContextualStorage newContextualStorage(RouteStorageKey key) // RouteStorageKey is a private nested type, so this override is not usable outside the class

com.vaadin.cdi.context.UIScopedContext

// Added
public class UIScopedContext extends AbstractContext
public UIScopedContext(final BeanManager beanManager)
public void init(BeanManager beanManager)
public Class<? extends Annotation> getScope()
public boolean isActive()
protected ContextualStorage getContextualStorage(Contextual<?> contextual, boolean createIfNotExist)

com.vaadin.cdi.context.UIScopedContext.ContextualStorageManager

// Added
public static class ContextualStorageManager extends AbstractContextualStorageManager<Integer>
public ContextualStorageManager()
public ContextualStorage getContextualStorage(boolean createIfNotExist)
protected ContextualStorage newContextualStorage(Integer uiId)

com.vaadin.cdi.context.VaadinServiceScopedContext

// Added
public class VaadinServiceScopedContext extends AbstractContext
public VaadinServiceScopedContext(BeanManager beanManager)
public void init(BeanManager beanManager)
public Class<? extends Annotation> getScope()
public boolean isActive()
protected ContextualStorage getContextualStorage(Contextual<?> contextual, boolean createIfNotExist)

com.vaadin.cdi.context.VaadinServiceScopedContext.ContextualStorageManager

// Added
public static class ContextualStorageManager extends AbstractContextualStorageManager<String>
public ContextualStorageManager()

com.vaadin.cdi.context.VaadinSessionScopedContext

// Added
public class VaadinSessionScopedContext extends AbstractContext
public VaadinSessionScopedContext(BeanManager beanManager)
public void init(BeanManager beanManager)
public Class<? extends Annotation> getScope()
public boolean isActive()
public static void destroy(VaadinSession session)
public static boolean guessContextIsUndeployed()
protected ContextualStorage getContextualStorage(Contextual<?> contextual, boolean createIfNotExist)

com.vaadin.cdi.context.VaadinSessionScopedContext.ContextualStorageManager

// Added
public static class ContextualStorageManager
protected static final String ATTRIBUTE_NAME
protected boolean isActive()
protected ContextualStorage getContextualStorage(Contextual<?> contextual, boolean createIfNotExist)

com.vaadin.cdi.util.AbstractContext

// Added
public abstract class AbstractContext implements Context
protected AbstractContext(BeanManager beanManager)
protected abstract ContextualStorage getContextualStorage(Contextual<?> contextual, boolean createIfNotExist)
protected List<ContextualStorage> getActiveContextualStorages()
protected void checkActive()
public boolean isPassivatingScope()
public <T> T get(Contextual<T> bean)
public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
public boolean destroy(Contextual bean)
public void destroyAllActive()
public static Map<Object, ContextualInstanceInfo<?>> destroyAllActive(ContextualStorage storage)
public static void destroyBean(Contextual bean, ContextualInstanceInfo<?> contextualInstanceInfo)

com.vaadin.cdi.util.AnyLiteral

// Added
public class AnyLiteral extends AnnotationLiteral<Any> implements Any

com.vaadin.cdi.util.BeanManagerProvider

// Added
public class BeanManagerProvider implements Extension
public static boolean isActive()
public static BeanManagerProvider getInstance()
public BeanManager getBeanManager()
public void setBeanManager(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager)
public void cleanupFinalBeanManagers(@Observes AfterDeploymentValidation adv)
public void cleanupStoredBeanManagerOnShutdown(@Observes BeforeShutdown beforeShutdown)

com.vaadin.cdi.util.BeanProvider

// Added
public final class BeanProvider
public static <T> T getContextualReference(Class<T> type, Annotation... qualifiers)
public static <T> T getContextualReference(Class<T> type, boolean optional, Annotation... qualifiers)
public static <T> T getContextualReference(BeanManager beanManager, Class<T> type, boolean optional, Annotation... qualifiers)
public static Object getContextualReference(String name)
public static Object getContextualReference(String name, boolean optional)
public static <T> T getContextualReference(String name, boolean optional, Class<T> type)
public static <T> T getContextualReference(BeanManager beanManager, String name, boolean optional, Class<T> type)
public static <T> T getContextualReference(Class<T> type, Bean<T> bean)
public static <T> List<T> getContextualReferences(Class<T> type, boolean optional)
public static <T> List<T> getContextualReferences(Class<T> type, boolean optional, boolean includeDefaultScopedBeans)
public static <T> DependentProvider<T> getDependent(Class<T> type, Annotation... qualifiers)
public static <T> DependentProvider<T> getDependent(BeanManager beanManager, Class<T> type, Annotation... qualifiers)
public static <T> DependentProvider<T> getDependent(String name)
public static <T> DependentProvider<T> getDependent(BeanManager beanManager, String name)
public static <T> Set<Bean<T>> getBeanDefinitions(Class<T> type, boolean optional, boolean includeDefaultScopedBeans)
public static <T> Set<Bean<T>> getBeanDefinitions(Class<T> type, boolean optional, boolean includeDefaultScopedBeans, BeanManager beanManager)
public static <T> T injectFields(T instance)

com.vaadin.cdi.util.ClassUtils

// Added
public abstract class ClassUtils
public static ClassLoader getClassLoader(Object o)
public static boolean isProxyableClass(Type type)
public static Class tryToLoadClassForName(String name)
public static Class tryToLoadClassForName(String name, ClassLoader classLoader)
public static <T> Class<T> tryToLoadClassForName(String name, Class<T> targetType)
public static <T> Class<T> tryToLoadClassForName(String name, Class<T> targetType, ClassLoader classLoader)
public static Class loadClassForName(String name) throws ClassNotFoundException
public static <T> T tryToInstantiateClass(Class<T> targetClass)
public static Object tryToInstantiateClassForName(String className)
public static <T> T tryToInstantiateClassForName(String className, Class<T> targetType)
public static Object instantiateClassForName(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException
public static String getJarVersion(Class targetClass)
public static String getRevision(Class targetClass)
public static boolean containsMethod(Class<?> targetClass, Method method)
public static Method extractMethod(Class<?> clazz, Method sourceMethod)
public static Method extractMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes)
public static boolean containsPossiblyGenericMethod(Class<?> targetClass, Method method)
public static Method extractPossiblyGenericMethod(Class<?> clazz, Method sourceMethod)
public static boolean returns(Method method, Class<?> clazz)

com.vaadin.cdi.util.ContextUtils

// Added
public abstract class ContextUtils
public static boolean isContextActive(Class<? extends Annotation> scopeAnnotationClass)
public static boolean isContextActive(Class<? extends Annotation> scopeAnnotationClass, BeanManager beanManager)

com.vaadin.cdi.util.ContextualInstanceInfo

// Added
public class ContextualInstanceInfo<T> implements Serializable
public CreationalContext<T> getCreationalContext()
public void setCreationalContext(CreationalContext<T> creationalContext)
public T getContextualInstance()
public void setContextualInstance(T contextualInstance)

com.vaadin.cdi.util.ContextualStorage

// Added
public class ContextualStorage implements Serializable
public ContextualStorage(BeanManager beanManager, boolean concurrent, boolean passivationCapable)
public Map<Object, ContextualInstanceInfo<?>> getStorage()
public boolean isConcurrent()
public <T> T createContextualInstance(Contextual<T> bean, CreationalContext<T> creationalContext)
public <T> Object getBeanKey(Contextual<T> bean)
public Contextual<?> getBean(Object beanKey)

com.vaadin.cdi.util.DependentProvider

// Added
public class DependentProvider<T> implements Provider<T>, Serializable
public T get()
public void destroy()

com.vaadin.cdi.util.ProxyUtils

// Added
public abstract class ProxyUtils
public static Class getUnproxiedClass(Class currentClass)
public static boolean isProxiedClass(Class currentClass)
public static List<Class<?>> getProxyAndBaseTypes(Class<?> proxyClass)
public static boolean isInterfaceProxy(Class<?> proxyClass)

Test summary

No test methods were authored on this branch: the two suites arrive wholesale with the import (67 Weld unit tests in vaadin-cdi, 58 Arquillian ITs in flow-tests/vaadin-cdi-tests), and the only post-import edits to test sources are two formatting reflows. What this change has to prove is therefore that those suites still run, and that the build/CI plumbing added around them behaves — so the rows below are the behaviours of the wiring, and the mapping names the suites and build assertions that pin them at class granularity rather than listing 125 imported method names.

# Status What the test verifies Why it matters
1 The CDI runtime (instantiator lookup and fallback, service delegate, the four contexts, deployment validation) still passes under Weld now that the module builds inside the Flow reactor against 999.99-SNAPSHOT A reparent that silently dropped a dependency or changed a bean-discovery resource would break every CDI app; this is the cheapest signal and runs in unit-tests
2 The full stack deploys to a real container (tomee) and scopes, push, i18n, templates and service customisation work end to end — on every Flow change This is the only Flow-wide guard that a core change hasn't broken the CDI integration; it is why tomee is ungated
3 The same 58 ITs pass on wildfly, payara, liberty and tomcat-weld — but only when vaadin-cdi/, flow-tests/vaadin-cdi-tests/ or validation.yml changed Container-specific breakage (CDI impl differences, executor lookup, websocket transport) shows up nowhere else; gating trades ~30 min of tail for coverage where regressions actually originate
4 Building the module with no container profile skips its tests rather than running or failing them cdi.container.tests.skip is reached by no other job (mvn install -DskipTests leaves the module out, every matrix leg passes -P<container>), so without this a regression would only surface in a contributor's local mvn verify
5 The generated target/effective-pom.xml is well-formed XML after sanitisation ShrinkWrap parses it with a strict reader; a malformed file fails every @Deployment method with an opaque "Non-parseable POM", which is exactly how this was found
6 Arquillian's nine core artifacts converge under each container profile Each profile brings its own adapter; the rule lives in default-cli, so without naming the goal nothing evaluates it and the liberty divergence would silently return
7 gap The Run CDI ITs step exits non-zero when Maven fails, and a failing leg flips the test-results aggregate check This is the bug that hid every container failure behind tee's exit status; the fix is verified only by inspection and by the first genuinely red run
8 gap computeMatrix.js excludes flow-tests/vaadin-cdi-tests from the generated it-tests matrix If the exclusion were dropped, the module would be handed to a job with no application server and fail confusingly; scripts/ has no test harness to assert this

Mapping — tests and build-time assertions on this branch to the rows above:

  • CdiInstantiatorTest.getI18NProvider_beanEnabled_instanceReturned, .getMenuAccessControl_beanEnabled_instanceReturned, .getPageTitleGenerator_beanEnabled_instanceReturned, .getServiceInitListeners_javaSPIListenerExists_containsJavaSPIListener (+ rest of class), CdiInstantiatorDefaultsTest.* → 1
  • CdiVaadinServletServiceTest.getInstantiator_serviceInitialized_cdiInstantiatorReturned, .init_instantiatorAmbiguous_ExceptionThrown, .init_instantiatorUnsatisfied_ExceptionThrown, .init_instantiatorInitReturnsFalse_ExceptionThrown (+ rest of class; reflowed on this branch), CdiVaadinServletTest.*, CdiVaadinServletServiceExecutorTest.*, PolymerTest.* → 1
  • DeploymentValidatorTest.validate_normalScopedProblems_collected, .validate_routeScopedProblems_collected → 1
  • SessionContextTest.get_sessionExistsButNotLocked_contextNotActive, .defaultManager_sessionLocked_isActive, .context_nullSession_notActive, .defaultManager_storageAccess_returnsStorage, ServiceContextTest.*, UiContextNormalTest.*, UiContextPseudoTest.*, RouteContextNormalTest.*, RouteContextPseudoTest.* → 1
  • RouteContextualStorageManagerTest.onBeforeEnter_initialNavigationTarget_scopeDoesNotExist_Throws, .afterNavigation_initialNavigationTarget_scopeDoesNotExist_Throws, .onBeforeEnter_group1Navigation_beansAreScoped, .onBeforeEnter_group2NavigationAfterGroup1_beansAreDestroyed (+ rest of class; reflowed on this branch) → 1
  • SmokeTest.injectionHappensWithEnabledCdiServlet, .injectionDoesNotHappenWithDisabledCdiServlet, .navigationCorrectlyHandlesProxiedViews → 2, 3, 5
  • RouteContextTest.navigateFromRootToMasterReleasesRootInjectsEmptyBeans, .navigationFromAssignedToMasterHoldsGroup, .navigationFromApartToMasterReleasesGroup, .rerouteReleasesSource (+ rest), UIContextTest.*, SessionContextTest.*, SessionContextStrictTest.*, SessionContextSpecializesTest.* → 2, 3, 5
  • ServiceTest.bootstrapCustomizedByServiceInitEventObserver, .sessionExpiredMessageCustomized, .errorHandlerCustomized, .sessionInitEventObserved, UIEventsTest.*, TranslationTest.*, TemplateTest.*, InstantiatorFactoryCustomizeTest.*, RemoveOldContentTest.*, BeanDiscoveryModeTest.* → 2, 3, 5
  • PushTest.wsWithXhrForegroundAllContextsActive, .wsWithXhrBackgroundRequestAndSessionDoesNotActive, .wsNoXhrForegroundRequestAndSessionDoesNotActive, .wsNoXhrBackgroundRequestAndSessionDoesNotActive, PushWithoutManagedExecutorTest.* → 2, 3
  • InvalidDeploymentTest.invalidDeploymentShouldBreakDeploy → 2, 3 (also the only IT that asserts DeploymentValidator rejects a bad deployment on a real container)
  • validation.yml step "Verify the no-container build skips its tests" — asserts no Tests run: line and a Tests are skipped line from a profile-less mvn verify → 4
  • flow-tests/vaadin-cdi-tests/pom.xml, antrun execution sanitize-effective-pom with <xmlvalidate> and failOnError=true → 5
  • validation.yml step "Run CDI ITs" — explicit enforcer:enforce goal, evaluated once per container profile (confirmed to fail again if the arquillian-bom import is removed) → 6

Deliberately untested: the Decide whether to run gating script (a wrong answer costs coverage or minutes, never correctness, and it fails open — it runs every container whenever it cannot determine a diff base); the SlowTests-categorised ITs, excluded by default as before the import; and Kill leftover Java processes / artifact packaging, which are cleanup steps guarded by always().

Henri Sara and others added 30 commits September 24, 2014 09:03
This reverts commit 389d7df.

Change-Id: I162012949b0b92e8388d997752436d28329e3b13
Change-Id: I5b7d9907e7f3464f1930611abf0d1a0184fa7d1a
Change-Id: I0f9721163cb105774fabfae5f0af31033e3506da
Change-Id: I6089f881b05b9bb6869898cce12efafc244b02c7
Change-Id: Ic2dd5304bd48a16f545a52ad616d441f769739b5
Change-Id: Ia260dd68b76c90060e2b64a8f3da384022636ef1
Change-Id: I2eec19c14cef6b62b6063bffcc305ea984322330
Change-Id: I2ef8dc42d7238e9784f443f6e1190249f82bcfd1
Change-Id: I127245eaf3ab05877a4b4bbf7e95bbe4bbf349f3
Change-Id: Ibaa778a2665e677427cefca4bdc15c6987724cae
Change-Id: I718308184ec7be64d336ea95c1d2684254649a55
Change-Id: I2be7e557843eaa7c7fc53df15759752b71975a95
Change-Id: If457da33cf5489c9b5cd552827fb60166599cd72
Change-Id: I326af890640a1b3e5fc8d96a2b3407b19785fc95
Using a nested VaadinCDIServlet is allowed.

Change-Id: I17261000feef668c7bd48b521eca3e3cf0a531f7
Change-Id: Ice24d701df7d3e0765c1493e1e75af69e7f54666
Change-Id: I17dfa70e98e64ea99bf897c48bdae7aad97a0f9d
Change-Id: Ic8199751125db735968297ae45b9a181c28d0313
Change-Id: Ic4fea717afa7389a17f82c2adf664f62f8198627
Change-Id: I37da96e890363029eddeaf76575100f237db2e21
Change-Id: Ic316f0fcfbbeebf96f5b33c6f34dc9e6fbe48d73
Change-Id: I04e4171168c021da9e690f553b1ff9c70412fdf9
Change-Id: I48f4623c9379c3a2bef11eda5601ace5458de644
Change-Id: Ibbcd930a52675ea1983ab38c027692a6f6758144
Change-Id: I3044360d37d69916e8a8c861539293681d9a5520
Change-Id: I5831d94061fa9070e5c740d742d2df0e905c3288
Change-Id: Id596cd1f38df95796905b057fd3d2818f84427d0
Change-Id: If5c8b606365247a74637174e1c49d49fc9ed510f
Change-Id: Ib5ca7086840fdd6320550522a6cfee504883a861
Change-Id: If4cac244c171bfb292c29f68dabafa54587edc55
mcollovati and others added 21 commits November 28, 2025 08:38
WildFly 39 ships with Jackson 2.20 which is compatible with Vaadin 25,
so the Jackson module exclusion workaround is no longer needed.
The workaround is preserved but commented, as a reminder for potential
future similar issues.
)

Accessing `@VaadinSessionScoped` beans from background threads that have
a `VaadinSession` set as current but don't hold the session lock causes
an `IllegalStateException` from `VaadinSession.getAttribute()`.

Check `hasLock()` in `isActive()` so the scope correctly reports as
inactive without the lock, producing the expected
`ContextNotActiveException` instead.

Fixes #495
Configure the spotless plugin with settings matching Vaadin Flow:
- Add VaadinJavaConventions.xml for Eclipse formatter settings
- Add cdi.importorder for import ordering
- Add apache2-license-header.txt for license headers
- Configure Java formatting with forbidden wildcard imports
- Configure POM sorting

This resolves #509.

https://claude.ai/code/session_01S7HsGqoR2cKkfdpxzYGqnW

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Marco Collovati <marco@vaadin.com>
Fork PRs will have the `build` job run (compile, matrix generation) but no tests. Reviewers must validate fork PRs manually by checking out the
branch.
…#515)

Move the activation predicate and storage-access logic out of
`VaadinSessionScopedContext` into a nested `ContextualStorageManager`
managed bean. The context becomes a thin delegator. As a side
effect, applications may replace the bean via CDI `@Specializes` to
adjust behavior — though the manager is intentionally undocumented
and overriding it is at the integrator's own risk.

Adds integration tests covering the strict default and the lenient
`@Specializes` scenario from a background thread that does not hold
the session lock.

References #506
)

* fix: bind route scope to window name only for `@PreserveOnRefresh`

Route scope storage was keyed by window name whenever the extended client
details reported one. That check used to be an implicit `@PreserveOnRefresh`
detector: up to Vaadin 24, Flow requested the window name only for preserved
navigation chains.

Since #22719 the client details are collected during UI
initialization and every tab gets a window name, so the check is always true.
All `@RouteScoped` beans became shared between the UIs of the same browser
window. When two such UIs are alive at the same time (duplicated tab,
restored session, or reloads faster than the unload beacons that close the
previous UI), the second UI is given a route target that still belongs to the
first UI's state tree, and navigation fails with "Can't move a node from one
state tree to another".

The storage is now bound to the window only when the navigation target or one
of its layouts is annotated with `@PreserveOnRefresh`, mirroring Flow's
isPreserveOnRefreshTarget rule. Otherwise it is bound to a single UI and
destroyed on its detach.

Fixes #526

* fix: keep route scope storage when its owner changes scope

The storage key of an owner switches between window and UI binding when
navigating between a preserved and a plain child of the same layout. The
storage is now relocated to the new key instead of letting a second one
be created, so the beans of a layout that stays in the navigation chain
are not recreated. Taking over the window storage is skipped while
another UI of the same window is still alive.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Flow 25.3 (#25268) moved the service listeners onto the new
VaadinServiceEventBus. BootstrapHandler now fires a UIInitEvent through
the bus instead of calling VaadinService.fireUIInitListeners, which is
deprecated for removal and no longer invoked by Flow.

The extension attached its CDI event forwarding listeners to the UI from
an override of that method, so they were never registered: no navigation,
poll or UI detach event reached CDI observers, and @RouteScoped beans got
no context because RouteScopedContext observes BeforeEnterEvent and
AfterNavigationEvent.

UIEventListener now implements UIInitListener and attaches itself to the
UI before firing the CDI UIInitEvent, and is registered with
addUIInitListener instead of a lambda. Only APIs available since Flow 1.0
are used, so the fix works on both Flow 25.2 and 25.3.

The unit tests fire the event the way Flow does, through the service
event bus, reached reflectively so that they still compile and exercise
the same code path against Flow 25.2, where the bus does not exist yet.
Brings vaadin/cdi main into this repository the same way vaadin/spring
was imported in #13002: a merge of the upstream history so `git log` and
`git blame` keep working for the CDI sources.

The library lands at /vaadin-cdi and the Arquillian integration tests at
/flow-tests/vaadin-cdi-tests. The dead vaadin-cdi-example module (a
README with no sources) and the IDE descriptors are left behind; the
upstream root pom, CI workflow and formatter configuration are replaced
by Flow's in the following commits.
Reparents vaadin-cdi to flow-project so it inherits the Flow version and
is released with it: the flow-* dependencies now resolve through
${project.version} instead of a separately maintained
vaadin.flow.version, and vaadin-cdi is published through flow-bom.

The Jakarta EE BOM was already imported by the Flow parent at the same
11.0.0 version the CDI parent used, so only the Weld versions had to be
hoisted. The source, surefire and javadoc plugin configuration is
dropped in favour of the Flow parent's.

BeanManagerProvider switches to SLF4J, which the check-logging-imports
enforcer rule requires of every module in this repository.
Reparents the imported Arquillian suite to flow-tests, which already
supplies flow-client, flow-html-components, flow-test-util, junit and the
prerelease repositories, so those declarations and the separate
flow-maven-plugin version can go. The Arquillian and ShrinkWrap versions
stay module-local, the way vaadin-spring-tests keeps its Spring ones.

Every test here deploys to a real application server, so surefire now
skips unless one of the container profiles is active. Without that guard
the module would fail a plain `mvn verify` over flow-tests, since the
tests are surefire-bound `*Test` classes rather than failsafe ITs.
Runs spotless:apply with Flow's configuration. The Eclipse formatter
profile and license header are byte-identical to the ones the CDI repo
used, so the Java churn is limited to what the newer spotless version
reflows; the Polymer test template is reformatted because Flow also runs
prettier over frontend sources.
The suite deploys to a real application server, which the generated
it-tests matrix has no way to express, so computeMatrix.js excludes the
module and validation.yml gets a job of its own with the five container
profiles the CDI repository used.

Running all five on every Flow change would add a ~30 minute tail to
validation, so only tomee runs unconditionally, as a smoke test for
changes anywhere in Flow; wildfly, payara, liberty and tomcat-weld run
when the CDI sources themselves change, gated the same way the
gradle-tests job gates its expensive steps.
Adds the module to the maps in CLAUDE.md and guidelines/repository.md,
repoints the README links that referred to paths in the vaadin/cdi
repository, and records in the test module's README how to select a
container profile — the upstream root README carried those instructions
and it was not imported.
The `Run CDI ITs` step piped Maven through tee, so the step exited with
tee's status and every container failure reported success. It now uses
the detached stdio the it-tests job uses, which also keeps an
application server JVM that outlives Maven from pinning the step open by
holding the pipe.

cdi-tests restores saved-workspace, so cleanup-artifacts has to wait for
it before deleting the artifact, and test-results now waits for it too:
without that a failing container leg did not flip the aggregate check and
whether the CDI surefire reports made it into the published test report
was down to timing.

Adds a step that builds the module with no container profile and asserts
the tests are skipped rather than run or failed. That is the one path
this branch introduced that nothing covered, since `mvn install
-DskipTests` leaves the module out and every leg passes -P<container>.

The slow test profile is renamed to slow-tests to match the id the rest
of the repository uses, and the README now shows it being combined with a
container profile - on its own it skips everything.
@cla-assistant

cla-assistant Bot commented Sep 7, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
6 out of 8 committers have signed the CLA.

✅ mshabarov
✅ mcollovati
✅ TatuLund
✅ ZheSun88
✅ Artur-
✅ tltv
❌ thevaadinman
❌ beth-soptim
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions github-actions Bot added the +0.1.0 label Sep 7, 2026
Every test in the module failed at its @deployment method with
"Non-parseable POM .../effective-pom.xml". ShrinkWrap builds the WAR by
reading that file with a strict POM parser, and what help:effective-pom
writes under the Flow parent is not well-formed XML, for two independent
reasons:

- the plugin's banner contributes an XML declaration and the model it
  renders contributes a second one, which the parser rejects as a
  "PITarget with reserved xml name". Which rendering path the plugin
  takes depends on the model, which is why this appeared only after the
  module was reparented.
- build-helper:parse-version, which the Flow parent runs and the CDI
  parent did not, sets a property whose name ends in a question mark.
  That is a legal Maven property name but not a legal XML element name,
  so Maven serialises a tag no parser accepts.

An antrun execution now strips both and validates what is left, so a
future change of shape fails on the spot instead of resurfacing as every
test failing to build its archive. Neither the declaration nor the
parsedVersion properties are of any use to ShrinkWrap, which reads this
file for the dependency list.

Verified by driving ArchiveProvider directly: the smoke deployment
resolves and packages its 19 libraries again.
The liberty leg failed the DependencyConvergence rule the Flow parent
enforces and the CDI parent did not: the Liberty adapter pulls the
Arquillian core in at 1.7.0.Final, diverging from what
arquillian-junit-container brings, across nine artifacts.

Importing arquillian-bom in place of the single managed entry pins all of
them. Nearest-wins already resolved them to arquillian.version, so this
records the version that was in effect rather than changing it, and it
holds for every container profile instead of only the ones that happen
not to declare an adapter of their own.

Convergence now passes for tomee, wildfly, payara, liberty and
tomcat-weld alike.
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test Results

 1 465 files  + 30   1 549 suites  +30   1h 39m 20s ⏱️ + 3m 59s
12 038 tests +142  11 970 ✅ +142  68 💤 ±0  0 ❌ ±0 
12 356 runs  +142  12 288 ✅ +142  68 💤 ±0  0 ❌ ±0 

Results for commit ec85beb. ± Comparison against base commit 0dae006.

♻️ This comment has been updated with latest results.

Declaring maven-antrun-plugin also activated the execution the flow-tests
parent manages, which deletes an exploded WAR's WEB-INF/lib. This module
is a jar and ShrinkWrap assembles its archives in process, so that
directory never exists and the execution printed an Ant BuildException
and a stack trace on every run - non-fatal, but indistinguishable from a
real failure in the log. Bound to phase none here.

The dependencyConvergence rule lives in the enforcer default-cli
execution, so no lifecycle phase evaluates it and nothing in CI would
have caught the Arquillian divergence the previous commit fixed, or a
regression of it. The CDI job now names the goal explicitly, which
evaluates it once per container profile - the profiles are what bring
the conflicting adapters. Confirmed it fails again if the BOM import is
removed.
@sonarqubecloud

sonarqubecloud Bot commented Sep 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
57.4% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@mshabarov

Copy link
Copy Markdown
Contributor

@claude do you see any blockers in this move - any concerns about CI, release, structure problems, test run and similar?

@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown

Claude finished @mshabarov's task in 2m 19s —— View job


Review: blockers & concerns for the CDI import

I read the CI wiring (validation.yml, computeMatrix.js), the module POMs (flow-tests/vaadin-cdi-tests/pom.xml, vaadin-cdi/pom.xml, flow-bom, root reactor, flow-tests) and the skip/gating logic. Overall this is a careful, well‑reasoned import — the tricky parts (effective‑POM sanitisation, Arquillian convergence, detached stdio, the no‑container skip) are all handled and explained. I don't see a hard structural blocker. The things worth deciding before merge are below, ordered by how likely they are to actually stop the merge.

🔴 Likely merge blockers (checks, not code)

  1. SonarQube quality gate is currently red. It fails on 57.4% coverage on new code (needs ≥80%) and a C reliability rating on new code. The 15k imported lines land without Flow‑measured coverage — the Arquillian ITs aren't wired into Sonar's JaCoCo, and the Weld unit tests only cover part of the runtime. If the Sonar gate is a required check, this blocks auto‑merge regardless of the tests passing. You'll likely need a Sonar new‑code exclusion for vaadin-cdi/** (imported code, analysed as if authored here) and a look at the handful of reliability issues Sonar flags. Worth an explicit decision rather than leaving it red.

  2. CLA not signed by all committers. thevaadinman and beth-soptim haven't signed. Because the branch carries the full upstream git history (632 commits), the CLA bot sees every historical author. This will keep the CLA check red until resolved/overridden.

🟠 Release / versioning — worth confirming before merge

  1. The artifact com.vaadin:vaadin-cdi now has two possible producers. It inherits flow-project 25.3-SNAPSHOT, so the GAV stays com.vaadin:vaadin-cdi but its version jumps to Flow's line and it releases on Flow's cadence. Two things to confirm:
    • The old vaadin/cdi repository must stop publishing that GAV, or two sources publish the same coordinates.
    • The Vaadin Platform BOM / release tooling that currently pins vaadin-cdi to its own version needs to switch to Flow's version (it's now managed in flow-bom). This is a downstream coordination step, not something this PR can fully close on its own.
    • The version scheme is discontinuous for existing consumers who depend on com.vaadin:vaadin-cdi directly — fine, but should be called out in release notes.

🟡 CI cost / robustness — by design, but note the trade‑offs

  1. Every Flow PR now waits on a real TomEE deploy. The tomee leg is ungated, and cdi-tests is a needs: of both test-results and cleanup-artifacts. So an application‑server download + startup is now on the critical path of every PR, and if TomEE is flaky it can flip the aggregate test-results check for changes that have nothing to do with CDI. The timeout-minutes: 45 / step 35 budget is generous, which is good, but this is the main new ongoing‑maintenance surface. Acceptable as the "one smoke container" strategy — just make sure cdi-tests (specifically the tomee leg) is what branch protection treats as required, and watch its flake rate early.

  2. Fork PRs don't exercise any of this. cdi-tests, it-tests, unit-tests and test-results are all gated on !github.event.pull_request.head.repo.fork. That's consistent with the existing jobs (they need the TB licence), so it's not a regression — but it means a fork contributor changing CDI sources gets zero CDI signal, and the gating script's fork branch is untested by construction. Fine to accept; just be aware.

  3. Gating fails open (intended). The non‑tomee legs run only when vaadin-cdi/, flow-tests/vaadin-cdi-tests/ or validation.yml changed, and run everything when no diff base is available. A wrong answer costs coverage/minutes, never correctness — the PR is explicit about this and it matches how gradle-tests gates. No concern.

🟢 Things I checked that are fine

  • Reactor & resolution. vaadin-cdi is a top‑level reactor module (pom.xml:53) and in flow-bom, so it's always built/installed; the cdi-tests job's -pl flow-tests/vaadin-cdi-tests (no -am) resolves because the saved workspace has vaadin-cdi and all parent/utility poms in .m2. ✅
  • Matrix exclusion. flow-tests/vaadin-cdi-tests is in globalExclusions (computeMatrix.js:13), so the generic it-tests matrix won't try to run it without a container. ✅
  • No‑container skip. cdi.container.tests.skip=true by default, flipped false only by the five container profiles; surefire honours it, and there's an explicit CI assertion that a profile‑less verify skips rather than runs/fails. ✅
  • Aggregate check wiring. test-results now includes cdi-tests in needs and its failure condition, and the detached‑stdio Run CDI ITs step exits with Maven's status (the tee bug is fixed), so a red container actually flips the check. ✅
  • Convergence enforcement. arquillian-bom import pins the nine core artifacts; enforcer:enforce is named explicitly in the job (the rule lives in default-cli, so nothing else evaluates it) and runs per container profile. ✅
  • Effective‑POM sanitisation and the phase‑none binding of the inherited delete-lib-folder antrun execution are both correct and well‑commented. ✅

One small nit (non‑blocking)

  • The Arquillian ITs are named *Test and run under surefire with skipTests, not failsafe/*IT. That's inherited from upstream and harmless, but it means a local mvn test -P<container> will spin up a container during the test phase, which can surprise contributors. Not worth changing as part of this import.

Bottom line

No structural blocker to the move itself — the build/reactor/CI plumbing is sound. The two things that will actually gate the merge are external to the code: the Sonar quality gate (coverage + reliability on new code) and the CLA. The one decision I'd want made explicitly is the release coordination (old vaadin/cdi stops publishing; platform BOM repoints to Flow's version), and the one thing to watch operationally is the ungated tomee leg now sitting on every PR's critical path.


Analysis based on the branch as of ec85beb0; I read files but did not run the container test suites.
· branch chore/import-cdi-to-flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.