Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.lang.IllegalArgumentException: Unable to locate persister: org.springframework.modulith.events.jpa.JpaEventPublication #345

Open
kriehzoo opened this issue Oct 25, 2023 · 14 comments
Assignees
Labels
meta: waiting for feedback Waiting for feedback of the original reporter

Comments

@kriehzoo
Copy link

Hi! First of all thank you for the great work you are doing :-)

I have a Maven multi-module project with one Maven module for each application module plus a solution module that packages all the modules as a Spring fat jar.

I have a simple test case in my solution module to see if the application context still loads:

package com.company.product;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

/**
 * Tests for the main {@link Application}.
 */
@SpringBootTest
class ApplicationTests {

	private final ApplicationContext applicationContext;

	public ApplicationTests(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	/**
	 * Verifies that the application context can be loaded.
	 */
	@Test
	void contextLoads() {
		assertThat(applicationContext).isNotNull();
	}

}

I have 2 modules, company and connector. When a company is created, I create an event.

@Override
  @Transactional
  public Company createCompany(Company company) {
    // do some stuff like persisting with JPA ...
    applicationEventPublisher.publishEvent(new CompanyCreatedEvent(company));
    // ... then return the new company;
  }

Then in the connector module, I handle the event:

@ApplicationModuleListener
  public void onCompanyCreatedEvent(CompanyCreatedEvent event) {
    // do some stuff
  }

Like stated in the Spring Modulith docs, I also added the dependency to the starter package for JPA:

<dependency>
			<groupId>org.springframework.modulith</groupId>
			<artifactId>spring-modulith-starter-jpa</artifactId>
		</dependency>

Now, when running my ApplicationTests, I get java.lang.IllegalArgumentException: Unable to locate persister: org.springframework.modulith.events.jpa.JpaEventPublication

I seems that JpaEventPublication is not picked up by the entity scan.
Already tried to add it to @EntityScan as another base package, but that led to an immutable collection exception because I already have a base package configured:

/**
 * Main application class.
 */
@EnableAsync
@EnableAutoConfiguration
@EntityScan(basePackages = "com.company.**.internal")
@SpringBootApplication
public class Application {

  /**
   * Main method to start the application.
   *
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

Any idea what I am doing wrong here?

@odrotbohm
Copy link
Member

You're running into a limitation of the way that boot handles @EntityScan annotations. Unless such annotation is present, Boot uses the autoconfiguration packages it knows about for entity scanning. The Spring Modulith JPA registry support registers such an autoconfigration package in JpaEventPublicationAutoConfiguration. We cannot use @EntityScan ourselves as this would disable the default scanning of user classes in the autoconfiguration packages.

So the only option for now is that you explicitly declare org.springframework.modulith.events.jpa as an additional entity scan package yourself, which is suboptimal I guess, but at least should get your arrangement working.

@odrotbohm odrotbohm self-assigned this Oct 27, 2023
@odrotbohm odrotbohm added the meta: waiting for feedback Waiting for feedback of the original reporter label Oct 27, 2023
@kriehzoo
Copy link
Author

kriehzoo commented Nov 2, 2023

Yes, that is what I did in the end, adding @EntityScan(basePackages = "org.springframework.modulith.events.jpa") on both ApplicationTests and IntegrationTests.

@kriehzoo
Copy link
Author

kriehzoo commented Nov 2, 2023

As stated initially, when I add the @EntityScan to my Application.java to make my app start, I get java.lang.UnsupportedOperationException from

Caused by: java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.setBasePackagesOn(ModuleTestAutoConfiguration.java:98)

So I cannot find a configuration where both the tests and my app are working.

@odrotbohm could you pls help and point me to a workaround, am I still missing something?

@kriehzoo
Copy link
Author

kriehzoo commented Nov 2, 2023

Just to be clear: now I have the @EntityScan on my Application.java and my app starts.
The ApplicationTests are working w/o any further annotations except the @SpringBootTest
Same for my ModularityTests.

The problem lies with my @ApplicationModuleTest which now fails with

13:32:10.821 [main] ERROR o.s.boot.SpringApplication - Application run failed
java.lang.UnsupportedOperationException: null
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.setBasePackagesOn(ModuleTestAutoConfiguration.java:98)

@odrotbohm
Copy link
Member

Hm, I'm sorry about that. I don't quite get how your code could enter that particular path, as it's basically a leftover from early Boot 2.x days. On Boot 3, the fields looked up reflectively there shouldn't even be found. 🤔

@kriehzoo
Copy link
Author

kriehzoo commented Nov 2, 2023

Do you need more information like a full stacktrace?

@odrotbohm
Copy link
Member

Hm, not really. I wonder if you're able to test the 1.1 snapshots as it has this code path removed.

@kriehzoo
Copy link
Author

kriehzoo commented Nov 3, 2023

I tried again with spring-modulith:1.1.0-RC2 and now the test case fails with Violations like this one:

16:22:47.355 [main] ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.modulith.core.Violations: - Cycle detected: Slice businesscase -> 
                Slice company -> 
                Slice root:com.medina -> 
                Slice businesscase

I have to look into those violations but for now is there a way to disable those checks just to see if the original problem goes away?

@kriehzoo
Copy link
Author

kriehzoo commented Nov 3, 2023

Moved classes in my solution package to a package solution to get rid of the Slice root:com.medina, except my Application.java which I cannot move because then my test will not pick up the configuration from there.
With these changes I am back to the original error:

16:40:10.342 [main] ERROR o.s.boot.SpringApplication - Application run failed
java.lang.UnsupportedOperationException: null
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.setBasePackagesOn(ModuleTestAutoConfiguration.java:80)
	at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.registerBeanDefinitions(ModuleTestAutoConfiguration.java:66)
	at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86)
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:373)
	at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:372)
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148)
	at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:427)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:771)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:589)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:436)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
	at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
	at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1406)
	at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:545)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:187)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:119)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127)
	at org.springframework.test.context.junit.jupiter.SpringExtension.getApplicationContext(SpringExtension.java:283)
	at org.springframework.test.context.junit.jupiter.SpringExtension.resolveParameter(SpringExtension.java:269)
	at org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameter(ParameterResolutionUtils.java:136)
	at org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameters(ParameterResolutionUtils.java:103)
	at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:59)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestClassConstructor(ClassBasedTestDescriptor.java:363)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateTestClass(ClassBasedTestDescriptor.java:310)
	at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:79)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:286)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:278)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:277)
	at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$before$2(ClassBasedTestDescriptor.java:203)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:202)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:84)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)

I verified that I have spring-modulith-test-1.1.0-RC2.jar on the test classpath.
Should I try with a newer SNAPSHOT version instead, or when was the code removed that I am somehow running?

@kriehzoo
Copy link
Author

kriehzoo commented Nov 3, 2023

Seems to be the same with 1.1.0-SNAPSHOT:

 16:51:56.004 [main] INFO  o.s.m.t.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer - Re-configuring auto-configuration and entity scan packages to: com.medina.connector, com.medina.company.
16:51:56.013 [main] ERROR o.s.boot.SpringApplication - Application run failed
java.lang.UnsupportedOperationException: null
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.setBasePackagesOn(ModuleTestAutoConfiguration.java:80)
	at org.springframework.modulith.test.ModuleTestAutoConfiguration$AutoConfigurationAndEntityScanPackageCustomizer.registerBeanDefinitions(ModuleTestAutoConfiguration.java:66)
	at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86)

@kriehzoo
Copy link
Author

kriehzoo commented Nov 3, 2023

I do not understand why setBasePackagesOn() tries to add something to packages which is an unmodifiable list.
@odrotbohm any ideas what I could do to solve this issue? Your help is much appreciated :-)

@odrotbohm
Copy link
Member

odrotbohm commented Nov 3, 2023

I think you've revealed a hole in our tests. The problematic code path filters the existing autoconfiguration / entity scan packages (including those manually configured) and only keeps the ones that are related to Spring Modulith. It looks like we never test exactly that scenario (someone explicitly configuring a Spring Modulith package as entity scan package for example).

On the positive side: it is great timing to catch this before the GA release :).

odrotbohm added a commit that referenced this issue Nov 3, 2023
…uration packages.

If Spring Modulith packages were explicitly configured as autoconfiguration or entity scan packages, the test autoconfiguration would fail as it previously attempted to manipulate an immutable list. We now create a copy of that list to fix this.
@odrotbohm
Copy link
Member

This should be fixed and also backported to 1.0.x. If you try these snapshots, you should also not see the tightened cycle detection, yet.

@kriehzoo
Copy link
Author

kriehzoo commented Nov 4, 2023

Looks great, I will test it when I am back to work. Thanks for fixing this so quickly :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
meta: waiting for feedback Waiting for feedback of the original reporter
Projects
None yet
Development

No branches or pull requests

2 participants