Calling CMT bean from BMT bean results in "Local transaction already has 1 non-XA Resource" - jpa-2.0

I have one EJB with Bean-Managed transaction:
#Singleton
#TransactionManagement(TransactionManagementType.BEAN)
public class BmtBean {
#Resource
private DataSource ds1;
#Resource
private SessionContext sessionCtx;
#EJB
private CmtBean cmtBean;
public void callCmtBean() {
Connection conn1 = null;
try {
conn1 = ds1.getConnection();
// create a PreparedStatement and execute a query
// process result set
while(resultSet.next()) {
// map resultSet to an entity
Entity entity = mapResultSetToEntity(resultSet);
sessionCtx.getUserTransaction().begin();
// pass an entity to another EJB,
// that operates on a different JTA data source
cmtBean.call(entity);
sessionCtx.getUserTransaction().commit();
}
} finally {
// release connection
}
}
}
And another bean with container-managed transaction:
#Singleton
#TransactionManagement(TransactionManagementType.CONTAINER)
public class CmtBean {
#PersistenceContext
private EntityManager em;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public void call(Entity entities) {
//persist passed entities
//em.flush()
//em.clear();
}
}
Calling cmtBean#call doesn't cause TransactionRequiredException, because prior to that I start a UserTransaction. But when em#flush is called, this exception is thrown:
Caused by: javax.resource.spi.ResourceAllocationException: Error in
allocating a connection. Cause: java.lang.IllegalStateException: Local
transaction already has 1 non-XA Resource: cannot add more resources.
After digging some EclipseLink code, I see that when calling em#flush() it attempts to obtain a new connection from dataSource and fails to do so.
Is this a bug or expected behaviour? How can I fix this?
UPDATE:
See the updated code example.
Also, I have to stress, that I do use 2 non-XA JTA data sources. But since the connection in BmtBean is by default set to autocommit, by the time CmtBean is called, the transaction has to be already commited.

Related

Unknown thread spawns which ignores the filter chain and fails on async decorator

I am currently facing a strange issue I am not able to reproduce locally, but happens in AWS ECS regularly, letting the application crash or run slow.
We have a spring boot application which extracts the tenant from the incoming GraphQL request and sets the tenant to a ThreadLocal instance.
To support DataLoader from GraphQL Java kickstart we populate the tenant to each child thread which will be used by the graphql dataloader. The tenant is mandatory to specify the database schema.
The executor
#Bean
#Override
public Executor getAsyncExecutor() {
log.info("Configuring async executor for multi tenancy...");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setThreadNamePrefix("tenant-child-executor-");
// Important part: Set the MultiTenancyTaskDecorator to populate current tenant to child thread
executor.setTaskDecorator(new MultiTenancyAsyncTaskDecorator());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
log.info("Executor configured successfully!");
executor.initialize();
return executor;
}
Task Decorator
#NonNull
#Override
public Runnable decorate(#NonNull Runnable runnable) {
if (Objects.isNull(CurrentTenantContext.getTenant())) {
log.warn("Current tenant is null while decorating a new thread!");
}
final TenantIdentifier parentThreadTenantIdentifier = Objects.isNull(CurrentTenantContext.getTenant()) ? TenantIdentifier.asSystem() : CurrentTenantContext.getTenant();
// Also need to get the MDC context map as it is bound to the current local thread
final Map<String, String> parentContextMap = MDC.getCopyOfContextMap();
final var requestAttributes = RequestContextHolder.getRequestAttributes();
return () -> {
try {
CurrentTenantContext.setTenant(TenantIdentifier.of(parentThreadTenantIdentifier.getTenantName()));
if (Objects.isNull(requestAttributes)) {
log.warn("RequestAttributes are not available!");
log.warn("Running on tenant: {}", parentThreadTenantIdentifier.getTenantName());
} else {
RequestContextHolder.setRequestAttributes(requestAttributes, true);
}
if (Objects.isNull(parentContextMap)) {
log.warn("Parent context map not available!");
log.warn("Running on tenant: {}", parentThreadTenantIdentifier.getTenantName());
} else {
MDC.setContextMap(parentContextMap);
}
runnable.run();
} finally {
// Will be executed after thread finished or on exception
RequestContextHolder.resetRequestAttributes();
CurrentTenantContext.clear();
MDC.clear();
}
};
}
Tenant Context
public class CurrentTenantContext {
private static final ThreadLocal<TenantIdentifier> currentTenant = new ThreadLocal<>();
private CurrentTenantContext() {
// Hide constructor to only provide static functionality
}
public static TenantIdentifier getTenant() {
return currentTenant.get();
}
public static String getTenantName() {
return getTenant().getTenantName();
}
public static void setTenant(TenantIdentifier tenant) {
currentTenant.set(tenant);
}
public static void clear() {
currentTenant.remove();
}
public static boolean isTenantSet() {
return Objects.nonNull(currentTenant.get());
}
}
Locally, this works like a charm. Even in a docker compose environment with limited resources (CPU and Mem) like in AWS. Even 100.000 requests (JMETER) everything works like expected.
On AWS we can easily let the application crash.
After one or two requests, containing some child objects to resolve by GraphQL, we see a thread spawning which seems to ignore or not go through the chain
Thread-110 | [sys ] | WARN | MultiTenancyAsyncTaskDecorator | Current tenant is null while decorating a new thread!
An interesting thing in this line is the name of the thread.
Each incoming request has the pattern http-nio-9100-exec-[N] and each child thread the pattern tenant-child-executor-[I] but this one has the pattern Thread-[Y].
Now I am wondering where this thread is coming from and why is it not reproducible locally.
I was able to find the solution to the problem.
I needed to change
private static final ThreadLocal<TenantIdentifier> currentTenant = new ThreadLocal<>();
to
private static final InheritableThreadLocal<TenantIdentifier> currentTenant = new InheritableThreadLocal<>();
But I don't know why it works with InheritableThreadLocal but not with ThreadLocal within the AWS environment.
Further, I wonder why this change was not necessary for local testing which works with both ways.
Maybe somebody can provide some ideas.

How to get implicitly created scope from resolved instance in DryIoc?

We've got Unit of Work as an external dependency of ViewModel. Both ViewModel and UnitOfWork implement IDisposable interface for cleanup. ViewModel uses UnitOfWork, but does not dispose it:
public class UnitOfWork: IDisposable
{
public void Dispose()
{
// some clean-up code here.
}
}
public class ViewModel: IDisposable
{
private readonly UnitOfWork _unitOfWork;
public ViewModel(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public void Dispose()
{
// some clean-up code here, but NO _unitOfWork.Dispose() because ViewModel does not create UnitOfWork.
}
}
ViewModel is transient: new instance should be created every time DI container's Resolve() method is called. ViewModels are disposed by an external code that has no accces to DI container. For now, UnitOfWork is scoped: only one instance should be created for one ViewModel and Dispose() should be called on ViewModel's Dispose().
We use DryIoc 4.0.7. The documentation says that current resolution scope can be injected to constructor as IDisposable object. So we change our ViewModel:
public class ViewModel: IDisposable
{
private readonly UnitOfWork _unitOfWork;
private readonly IDisposable _scope;
public ViewModel(UnitOfWork unitOfWork, IDisposable scope)
{
_unitOfWork = unitOfWork;
_scope = scope;
}
public void Dispose()
{
// some clean-up code here.
_scope.Dispose(); // we also dispose current scope.
}
}
Our composition root now looks like this:
var container = new Container();
container.Register<UnitOfWork>(reuse: Reuse.Scoped);
container.Register<ViewModel>(
setup: Setup.With(openResolutionScope: true, allowDisposableTransient: true));
I can't see how this code is different from the provided one in the documentation, but it throws an exception on Resolve() method:
DryIoc.ContainerException: 'Unable to resolve IDisposable as parameter "scope"
in ViewModel FactoryId=53 IsResolutionCall
from Container with Scope {Name={ServiceType=ViewModel}}
with Rules with {AutoConcreteTypeResolution}
with Made={FactoryMethod=ConstructorWithResolvableArguments}
Where no service registrations found
and no dynamic registrations found in 0 of Rules.DynamicServiceProviders
and nothing found in 1 of Rules.UnknownServiceResolvers'
What are we missing? Is the documentation outdated? Is there another way to get implicitly created scopes to dispose them?
This documentation is outdated - I have opened the respective issue.
The current scope can be injected as IResolverContext:
public ViewModel(UnitOfWork unitOfWork, IResolverContext scopedContext) {...}

Spring Boot #Async not working

I expect that uploadImage method finishes once the file is uploaded to AWS, while scanFile method is still running asynchronously in the background;
#RestController
public class EmailController {
#PostMapping("/upload")
#ResponseStatus(HttpStatus.OK)
public void uploadImage(#RequestParam MultipartFile photos) {
awsAPIService.uploadImage(photos);
}
}
...
#Service
public class AwsAPIService {
public void uploadImage(MultipartFile file) {
try {
File fileToUpload = this.convertMultiPartToFile(file);
String fileName = this.generateFileName(file);
s3client.putObject(new PutObjectRequest(AWS_S3_QUARANTINE_BUCKET_NAME,fileName, fileToUpload));
fileToUpload.delete();
// start scan file
scanFile();
} ...
}
#Async
public void scanFile() {
log.info("Start scanning");
String queueUrl = sqs.getQueueUrl("bucket-antivirus").getQueueUrl();
List<Message> messages = sqs.receiveMessage(new ReceiveMessageRequest().withQueueUrl(queueUrl)
.withWaitTimeSeconds(20)).getMessages();
for (Message message : messages) {
// delete message
...
}
}
}
...
#EnableAsync
public class AppConfig {
#Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(2);
taskExecutor.setQueueCapacity(200);
taskExecutor.afterPropertiesSet();
return taskExecutor;
}
}
But this seems still running synchronously. What is the problem here?
By default #Async and other Spring method-level annotations like #Transactional work only on the external, bean-to-bean method call. An internal method call from uploadImage() to scanFile() in the same bean won't trigger the proxy implementing the Spring behaviour. As per Spring docs:
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
You could configure AspectJ to enable annotations on internal method calls, but it's usually easier to refactor the code.

How to Mock DataSource Dependency Injection Despite Being Accessible via Static Method

I'm using Mockito, DBUnit and HSQLDB to unit test my database code. I'm also writing integration tests of course.
I'm having trouble understanding how to inject a mocked DataSource into the system under test (class I'm testing). The DataSource is used for connection pooling, and therefore other classes can call a static method in the same class in order to retrieve an instance of this DataSource. This means that the DataSource is not injected into any constructors, anywhere, and so my tests don't have any constructors to inject the mocked DataSource into.
I'm getting around this by altering the logic of my real code to check if a private variable is null, and if so then use the injected DataSource (bad design since it's only needed for tests), otherwise it calls the static method to retrieve the connection pool's source (better design).
How do I inject a mocked DataSource into a class that doesn't have a constructor set up to accept it, because it can instead just call the static method to retrieve the dependency?
Class to Test
public DBConnection(DBSource dbSource) { // <--- Constructor only required for test purposes :(
this.dbSource = dbSource;
}
public final void createCompsDB() {
Connection conn = null;
Statement statement = null;
try {
if(dbSource==null){
conn = DBSource.getInstance().getConnection();
}else{
conn = dbSource.getConnection(); /** Likely bad design, since dbSource is only NOT null for tests, so that you can inject the mocked datasource :( */
}
statement = conn.createStatement();
statement.executeUpdate("CREATE DATABASE placesdb");
System.out.println("Database created...");
} catch (SQLException e) {
// ...
}
} finally {
// Close Resources...
}
}
}
Test Class -- Test Passes
public class DBConnectionTest {
final Statement statement = mock(Statement.class);
final Connection connection = mock(Connection.class);
final DBSource dataSource = mock(DBSource.class);
#Before
public void setUp() throws SQLException, IOException, PropertyVetoException {
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
}
#Test
public void testCreateCompDBIfNotAlready() throws Exception {
DBConnection dbConnection = new DBConnection(localDB, dataSource); /** This constructor is only needed for testing :( . How do I avoid it since all the classes I need to test don't require the dependency to be injected? */
dbConnection.createCompsDB();
verify(statement).executeUpdate("CREATE DATABASE PLACES");
}
}
DBSource.java
protected DBSource() throws IOException, SQLException, PropertyVetoException {
ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUsername("user");
ds.setPassword("pass");
ds.setUrl("jdbc:postgresql://localhost:5432/placesdb");
}
public static DBSource getInstance() { // <--- Static method means dependent classes don't need to accept injections
if (datasource == null) {
datasource = new DBSource();
return datasource;
} else {
return datasource;
}
}
public Connection getConnection() throws SQLException {
return this.ds.getConnection();
}
}
Mocking of the static class methods may be done with PowerMockito.
The test class should be something like this:
#RunWith(PowerMockRunner.class)
#PrepareForTest(DBSource.class)
public class DBConnectionTest {
#Mock
final Statement statement;
#Mock
final Connection connection;
#Mock
final DBSource dbsource;
#Before
public void setUp() throws SQLException, IOException, PropertyVetoException {
PowerMockito.mockStatic(DBSource.class);
when(DbSource.getInstance()).thenReturn(dbsource);
when(dbsource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
}
#Test
public void testCreateCompDBIfNotAlready() throws Exception {
DBConnection dbConnection = new DBConnection(localDB); // No test-only constructor anymore
dbConnection.createCompsDB();
verify(statement).executeUpdate("CREATE DATABASE PLACES");
}
}
You can read here more about mocking with PowerMock.

Eclipse Scout client unit tests with ScoutClientTestRunner

I am trying to create unit test with scout context and I can't find proper tutorial or example for it.
When I create test with ScoutClientTestRunner, I get error
java.lang.Exception: Client session class is not set. Either set the default client session using 'ScoutClientTestRunner.setDefaultClientSessionClass' or annotate your test class and/or method with 'ClientTest'
I try to set client session class like this :
#Before
public void setClassSession() throws Exception {
ScoutClientTestRunner.setDefaultClientSessionClass(ClientSession.class)
}
and
#BeforeClass
public void setClassSession() throws Exception {
ScoutClientTestRunner.setDefaultClientSessionClass(ClientSession.class);
}
I try to add #ClientTest to the class and to all methods but I still get same error.
How to set client session in tests if you use ScoutClientTestRunner ?
The ScoutClientTestRunner ensures that the JUnit tests are executed having all the Scout Context (OSGi and so on) available.
Your attempts with #Before or #BeforeClass are too late. You need to provide the Scout Context initialization parameters before that. As the exception message says, you have 2 possibilities:
(1) #ClientTest annotation
You can annotate test classes or methods with #ClientTest using the clientSessionClass parameter:
#RunWith(ScoutClientTestRunner.class)
#ClientTest(clientSessionClass = ClientSession.class)
public class DesktopFormTest {
#Test
public void test1() throws Exception {
//Do something requiring a scout context:
//for example instantiate a DesktopForm.
}
}
If necessary you can also do it at method level:
#RunWith(ScoutClientTestRunner.class)
public class DesktopFormTest {
#Test
#ClientTest(clientSessionClass = Client1Session.class)
public void test1() throws Exception {
//client session is an instance of Client1Session.
}
#Test
#ClientTest(clientSessionClass = Client2Session.class)
public void test2() throws Exception {
//client session is an instance of Client2Session.
}
}
(2) Defining a TestEnvironment
When the test is run (directly or using maven-tycho), a lookup for a fully qualified class org.eclipse.scout.testing.client.runner.CustomClientTestEnvironment is done.
The CustomClientTestEnvironment class should implement org.eclipse.scout.testing.client.runner.IClientTestEnvironment
The method setupGlobalEnvironment() is called once and can be used to define the default client session with ScoutClientTestRunner.setDefaultClientSessionClass(..). This method can also be used to register required services.
Here an example:
package org.eclipse.scout.testing.client.runner; // <= can not be changed.
// add imports
public class CustomClientTestEnvironment implements IClientTestEnvironment {
#Override
public void setupGlobalEnvironment() {
//Set client session:
ScoutClientTestRunner.setDefaultClientSessionClass(ClientSession.class);
}
#Override
public void setupInstanceEnvironment() {
}
}
Of course (1) and (2) are compatible. The second mechanism defines only the default and ClientSession configured with (1) will override the default.