Implementing transaction in EJB with javax.transaction.UserTransaction - jboss

I am implementing an EJB application. At this point I need a transaction. I need to execute 3 methods in row and if one fails, everything should be rolled back.
Here: http://www.conceptgo.com/gsejb/eb04.html, I have found a tutorial.
My code:
try {
javax.transaction.UserTransaction ut = (UserTransaction) ctx.lookup("java:comp/UserTransaction");
ut.begin();
Feeds feed = loadFeed(url);
try{
em.persist(feed);
uf.setFeedId(feed.getFeedId());
uf.setUserId(user_id);
em.persist(uf);
}catch (EntityExistsException e){
ut.rollback();
return false;
}catch (IllegalArgumentException ea){
ut.rollback();
return false;
}
if (feed.getFeedId()!= null && feed!=null) {
ut.commit();
}else{
ut.rollback();
return false;
}
When I run my app I get next error message:
Caused by: java.lang.IllegalStateException: BaseTransaction.checkTransactionState
- ARJUNA016051: thread is already associated with a transaction
Anyone knows what's the cause and how to solve it?

The error means that you already have a transaction in progress, more than likely a container managed transaction.
What does your bean/method look like? The default transaction behavior for a stateless session bean in JBoss7 is that it executes with Container managed transaction and with 'Required' transaction attribute.
So assuming that container managed transactions work for you, you don't need the UserTransaction. So you can remove the lookup and the begin/commit/rollback since that will be managed for you.
If you do want to manage transactions yourself, then mark the bean as using Bean managed transactions and then you can begin/commit/rollback the UserTransaction. To mark a bean as using bean managed transactions, you will apply the annotation #TransactionManagement(TransactionManagementType.BEAN) to the class or method.

As error shows you are try to interrupt container managed transaction in JBoss. If you want to deal with transaction with own way then change your class/method bean managed like.
#TransactionManagement(TransactionManagementType.BEAN)
public void getTransaction(){
//....
}
Now you are safe from JBoss end but ensure you have to define each step of transaction in Bean managed Transaction.

It is not advisable to use few steps for CMT and few steps for BMT because in case CMT container will responsible for handling transaction and associated thread. So ejb container will acquire lock on objects participated in transaction and will not be released until transaction completed these objects may cause of problem if you need it in BMT. So you have to either use CMT or BMT .

Related

Quarkus: "no tenant identifier specified" in callback

I try to add multi-tenancy support for my Quarkus app, following Quarkus hibernate-orm doc (see last section).
I have my CustomTenantResolver class and configure in application.properties, with multiple data sources, but no named persistent unit, see below:
# Default data source
quarkus.hibernate-orm.datasource=master
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.multitenant=DATABASE
# ----- Tenant 'master' (default) ---------------
quarkus.datasource."master".db-kind=postgresql
quarkus.datasource."master".username=postgres
quarkus.datasource."master".password=password
quarkus.datasource."master".jdbc.url=jdbc:postgresql://localhost:5432/db_master
# ----- Tenant 'test' ---------------------------
quarkus.datasource.test.db-kind=postgresql
quarkus.datasource.test.username=postgres
quarkus.datasource.test.password=password
quarkus.datasource.test.jdbc.url=jdbc:postgresql://localhost:5432/db_test
Everything works fine for Web Services APIs functions - based on incoming web service calls, I can extract and supply tenant identifier for DB access.
Problem is, my app also needs to use callback method to listen on messages coming from Apache Pulsar queue. When a message comes in and triggers this callback, any DB access in this method will give this exception:
SessionFactory configured for multi-tenancy, but no tenant identifier specified: org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified
at org.hibernate.internal.AbstractSharedSessionContract.<init>(AbstractSharedSessionContract.java:172)
at org.hibernate.internal.AbstractSessionImpl.<init>(AbstractSessionImpl.java:29)
at org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:221)
at org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1282)
at org.hibernate.internal.SessionFactoryImpl.openSession(SessionFactoryImpl.java:472)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:86)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.persist(TransactionScopedSession.java:138)
at io.quarkus.hibernate.orm.runtime.session.ForwardingSession.persist(ForwardingSession.java:53)
... (snipped)
Apparently my CustomTenantResolver class was not called during this listener callback as the callback is another fresh thread, hence no tenant id is supplied.
Do I miss anything? How about the scheduler in Quarkus - how does it support multi-tenancy in scheduled jobs?
Thanks for helps.
I had a similar issue when pulling messages from JMS. The cause of the issue is that io.quarkus.hibernate.orm.runtime.tenant.HibernateCurrentTenantIdentifierResolver ( which implements CurrentTenantIdentifierResolver and as the doc says Maps from the Quarkus {#link TenantResolver} to the Hibernate {#link CurrentTenantIdentifierResolver} model ) expects a request context to be active before calling our implementation of TenantResolver, as shown here:
// Make sure that we're in a request
if (!Arc.container().requestContext().isActive()) {
return null;
}
TenantResolver resolver = tenantResolver(persistenceUnitName);
String tenantId = resolver.resolveTenantId();
I solved it on my app by, first, enabling the request context on the JMS consumer:
Arc.container().requestContext().activate();
and, second, using a ThreadLocal to "pass" the current tenant id to the TenantResolver that will be called later by Hibernate ( through the HibernateCurrentTenantIdentifierResolver instance):
CurrentTenantLocal.setCurrentTenantId("public");
On my TenantResolver ( the class that implements TenantResolver ) I resolve the tenant from either an injected JsonWebToken jwt when it comes from a WebRequest, or using the ThreadLocal when consuming from JMS:
if ( CurrentTenantLocal.getCurrentTenantId() != null ) {
return CurrentTenantLocal.getCurrentTenantId();
}
Caveats:
Note that I haven't done an exhaustive search of the possible side effects of activating the request context... but I have no problems so far.

Error EF Core PostgreSQL during an entity insert

I am getting an error during an entity insert. I am using the Entity Framework Core with the PostgreSQL.
Here is a piece of code which produces an error:
public async Task Add(AddVideoDto dto)
{
var videoModel = mapper.Map<Video>(dto);
await context.Videos.AddAsync(videoModel);
await context.SaveChangesAsync();
}
Here is the error log:
fail: Microsoft.EntityFrameworkCore.Database.Connection[20004] et/oklike/oklikebe (master)
An error occurred using the connection to database 'oklikedb' on server 'tcp://127.0.0.1:5432'.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HLVLRDVR67DK", Request id "0HLVLRDVR67DK:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: Reset() called on connector with state Connecting
at Npgsql.NpgsqlConnector.Reset()
at Npgsql.ConnectorPool.Release(NpgsqlConnector connector)
at Npgsql.NpgsqlConnection.<Close>g__FinishClose|76_1(NpgsqlConnection connection,
NpgsqlConnector connector)
at Npgsql.NpgsqlConnection.Close(Boolean wasBroken, Boolean async)
at Npgsql.NpgsqlConnection.CloseAsync()
at Npgsql.NpgsqlConnection.DisposeAsync()
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.DisposeAsync()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.<>c__DisplayClass15_0.<<DisposeAsync>g__Await|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.<>c__DisplayClass15_0.<<DisposeAsync>g__Await|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Http.Features.RequestServicesFeature.<DisposeAsync>g__Awaited|9_0(RequestServicesFeature servicesFeature, ValueTask vt)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FireOnCompletedAwaited(Task currentTask, Stack`1 onCompleted)
I am sure that I set up a connection to my db correctly. I verified that in the following way: I have another piece of code:
public async Task<List<GetVideoDto>> GetAll()
{
var videoModels = await context
.Videos
.ToListAsync();
return mapper.Map<List<GetVideoDto>>(videoModels);
}
And this piece of code works just fine. I manually inserted a value in my database and checked that it is returned by the await context.Videos.ToListAsync(); by debugging and by Postman. Also I can apply migrations to a database successfully.
So, the error seems to tell me that my piece of code tries to open a connection before closing it. But I can not understand how this could be possible.
I am very well aware of the state machine behind the async/await, so the context.SaveChangesAsync(); in my case will definitely run only after the context.Videos.AddAsync(videoModel); has completed.
UPDATE
I was able to better pin down the issue. The error is thrown due to this line:
await context.SaveChangesAsync();
So, I am not getting an error if I use the SaveChanges instead of the SaveChangesAsync. Does that mean that if I want to preserve the performance benefit of the SaveChangesAsync I should make the context to be not a singleton (as it is by default), but scoped?
Here is how I am adding the context right now:
services.AddDbContext<DataContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
I mean here is my entire Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(opt =>
opt.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddCors();
services.AddControllers();
services.AddAutoMapper(typeof(Startup));
services.AddScoped<IVideoService, VideoService>();
}
And by the performance benefit of the SaveChangesAsync I mean that my thread won`t be idle waiting for the SaveChanges to complete, but will go back to the CLR thread pool.
I am strongly feeling that there should be a DDD principle which targets specifically the case of a correct SaveChangesAsync usage. But I can not find it (probably there is no one).
Add entities using plain old Add() rather than trying to add them async. Its not a long running operation just adding to an in memory collection so there is no benefit to trying to make the Add operation async.
When you are adding to your DbSet you are doing just that, it's the actual saving that benefits from being Async, as that's what you can await.
If you read the docs on AddAsync you will also see you should the following:-
This method is async only to allow special value generators, such as
the one used by
'Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo',
to access the database asynchronously. For all other cases the non
async method should be used
.
I typically do this, and await the call to save in my controllers or whatever.
public void AddThing(Thing thingToAdd)
{
_context.Things.Add(thingToAdd);
}
public async Task<bool> SaveChangesAsync()
{
return (await _context.SaveChangesAsync() > 0);
}

Spring Cloud Netflix: Remote service error handling with Feign

We are trying to put Spring Cloud Netflix into production environment. For now we encounter a problem about business logic error handling.
We're using Feign as HTTP REST client. Microservice A needs to invoke microservice B which is deployed in different JVM(or physical server). Microservice B may return some error message which belongs to business. For instance A needs to query order information from B but the order ID may not exist so B has to return the error message that tells A this order doesn't exist. A has to do if-else judgement from the return message to determine if there are erorrs, then code will be like the following snippet:
//remoteServiceA is an interface annotated with #FeignClient
resultA = remoteServiceA.foo();
if (resultA.hasError) {
} else {
}
resultB = remoteServiceB.foo();
if (resultB.hasError) {
} else {
}
// ... ...
There are so many if-else so that it's not graceful enough. What we want is remoteServieA.foo() can throw a self-defined runtime exception such as OrderNotExistException. Any idea to achieve this goal?
I've solved this problem.
I customized the ErrorDecoder component of Feign, in which I can throw my own exception according to the HTTP original response.
If you have Hystrix enabled, you should be able to wrap you serviceA.foo() in a try block and throw an exception in your remote service.
try {
serviceA.foo();
} catch(HystrixRuntimeException ex) {
throw new OrderNotExistException("Error message");
}
You still have to take in account that you can catch that kind of exception if your remote service doesn't answer, or if an other error happens. Maybe you can find information the exception about what happened and decide if you should throw your exception.
First thing that comes to my mind but work in one of my project.

Grails: Event listener never stop (with websocket plugin)

I have a problem with my listener (platform core plugin). It never stop after event (afterInsert) was triggered (Mongo standalone)
My service catch the event and use the SimpMessagingTemplate (websocket plugin) to send on topic the data (A news user registration). The data is saved, but the listener is triggered again and again.
p.s. The data not really saved, the consequent exception, rollback the transaction. But in debug mode I can see the assigned id.
#Transactional
class MessageEventService {
SimpMessagingTemplate brokerMessagingTemplate
#Listener(topic = 'afterInsert',namespace = 'gorm')
void afterInsert(User user) {
log.info("DIOBO")
brokerMessagingTemplate.convertAndSend("/topic/myEventTopic", "myEvent: User Add ${user.name}")
}
}
And at the end...
.[grails] Servlet.service() for servlet grails threw exception
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2367)
Why happens?!?!?
Thanks!

transaction problem with entity framework 4

I'm trying to implement a transaction with entity framework 4. From what I've read, the code below is correct. The SaveChanges works fine but as soon as I hit the first ExecuteFunction call I get the following exception:
The underlying provider failed on
Open. --->
System.Transactions.TransactionManagerCommunicationException:
Network access for Distributed
Transaction Manager (MSDTC) has been
disabled. Please enable DTC for
network access in the security
configuration for MSDTC using the
Component Services Administrative
tool.
I've logged on to the database server and I don't see a service called Distributed Transaction Manager but I do see Distributed Transaction Coordinator and it is started. I'm not sure what I need to change to allow this to work. Anyone know? Thanks.
Here's the code.
using (var h = new WhaleEntities(ConnectionHelper.DBConnectString))
{
using (TransactionScope ts = new TransactionScope())
{
h.Sites.AddObject(s);
h.SaveChanges(SaveOptions.DetectChangesBeforeSave);
retval = s.SiteID;
h.ExecuteFunction("UpdateSiteInterfaceList", new ObjectParameter("pSiteID", retval), new ObjectParameter("pList", "10"));
h.ExecuteFunction("UpdateSiteInterfaceRequiredList", new ObjectParameter("pSiteID", retval),new ObjectParameter("pList", "Email"));
h.ExecuteFunction("UpdateSiteInterfaceAlwaysShownList", new ObjectParameter("pSiteID", retval),new ObjectParameter("pList", "10"));
h.ExecuteFunction("UpdateSiteInterfaceAlwaysRequiredList",new ObjectParameter("pSiteID", retval),new ObjectParameter("pList", "Email"));
ts.Complete();
//changes must be accepted manually once transaction succeeds.
h.AcceptAllChanges();
}
}
See here: How do I enable MSDTC on SQL Server?