What is the proper use of EntityManager from a RESTEasy service? (SEAM, JBoss) - jboss

My project is a WAR project generated with seam-gen. It contains a RESTEasy web service class like this (simplified, only the relevant parts):
#Scope(ScopeType.APPLICATION)
public abstract class RestService {
#In
protected EntityManager entityManager;
#GET
#POST
#Produces("application/json")
public Object proxy() {
// invokes various subclass methods
// based on request parameters
// and returns the result
}
// further method and logic
}
And:
#Path("/my")
#Name("myRestService")
public class MyRestService extends RestService {
public Object login(/*...*/) {
User user = getUser(email);
// ...
Token token = user.getToken();
if (token != null) {
entityManager.remove(token);
}
token = new Token();
entityManager.persist(token);
user.setToken(token);
user.setLastlogin(new Date());
entityManager.persist(user);
// ...
}
private User getUser(String email) {
try {
return (User) entityManager
.createQuery("FROM User WHERE UPPER(email) = UPPER(:email)")
.setParameter("email", email)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
}
If i invoke the login method through a web browser, it finds the correct user (based on the get params), instantiates a Token for it (i can see at the STDOUT of Hibernate asking the databse for the next sequence), but the persist() method does not save the Token to the database, neither the modifications of the User object (token id, last login date).
I have googled this for two days now, here's what i could figure out:
my project uses SEAM managed transactions (components.xml):
<persistence:managed-persistence-context name="entityManager" auto-create="true"
persistence-unit-jndi-name="java:/MyEntityManagerFactory"/>
my project uses JTA for transaction handling (persistence.xml):
<persistence-unit name="MyProject" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider> ...
the EntityManager.persist() does NOT commit changes to the database, just queues changes to the current transaction (?)
the SEAM managed Transactions are tied to conversations by default
I tried to use .flush(), an exception was thrown saying that there is no transaction in progress.
I tried to use .joinTransaction() and .getTransaction().begin(), another exception was thrown saying that a JTA EntityManager can not access transactions.
Also tried to use different scope types on the class, or use the #Transactional annotation on my login() method, no luck.
Also tried to inject the EntityManager with the #PersistenceContext annotation, this resulted in an exception saying #PersistenceContext can only be used with session beans.
Also tried to mark my class as #Stateless, this resulted that i could not reach my service (404).
How should i persist my Entities within a RESTEasy service with EntityManager?
System specs:
JBoss 5.1.0 GA
SEAM 2.2.1 Final
Postgres 8.3
Please note that i'm totally new and inexperienced with JavaEE/JBoss/SEAM.
Any comment would be useful! Thanks.

You are detaching a managed field:
entityManager.remove(token);
Creating a new one:
token = new Token();
But here:
entityManager.persist(token);
Token has a relationship with User (I don't what that relationship is - oneToMany, oneToOne, are you cascading from User?, fetching, etc), but just calling persist on token does not re-establish that relationship.
Take a look here
http://www.objectdb.com/java/jpa/persistence/crud

The transactional annotation is important on the login method. This will ensure that the transaction interceptor is creating a transaction if neccessary. (if there is no transaction yet). The easiest way to find out if the interceptor is applied would be to debug into the login method and check the stack. I'm not sure how the class is called but i will update this post as soon as i'm at work to check it out.
If this interceptor is not there it means that you are not using seam transactions. The extract of your components.xml is not showing that you do so.
Martin
Updated:
So here's the stacktrace. Take a look at the TransactionInterceptor if you do not have this in your stack you have no transaction management.
Daemon Thread [http-18081-1] (Suspended (breakpoint at line 170 in QueryHome))
QueryHome.getCandidatesCount() line: 170
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
Reflections.invoke(Method, Object, Object...) line: 22
RootInvocationContext.proceed() line: 32
SeamInvocationContext.proceed() line: 56
RollbackInterceptor.aroundInvoke(InvocationContext) line: 28
SeamInvocationContext.proceed() line: 68
BijectionInterceptor.aroundInvoke(InvocationContext) line: 77
SeamInvocationContext.proceed() line: 68
ConversationInterceptor.aroundInvoke(InvocationContext) line: 65
SeamInvocationContext.proceed() line: 68
TransactionInterceptor$1.work() line: 97
TransactionInterceptor$1(Work<T>).workInTransaction() line: 61
TransactionInterceptor.aroundInvoke(InvocationContext) line: 91
SeamInvocationContext.proceed() line: 68
MethodContextInterceptor.aroundInvoke(InvocationContext) line: 44
SeamInvocationContext.proceed() line: 68
JavaBeanInterceptor(RootInterceptor).invoke(InvocationContext, EventType) line: 107
JavaBeanInterceptor.interceptInvocation(Method, Object[]) line: 185
JavaBeanInterceptor.invoke(Object, Method, Method, Object[]) line: 103

Related

Write/Run Junit Test class (To test actuators) without Datasource bean creation in springboot container

What we are trying to do?
Writing a Junit for Springboot actuator/Admin as below
Code snippet:
ActuatorTests.java
#SpringBootTest(properties = {
"management.endpoints.web.exposure.include=" })
#ActiveProfiles(profiles = "local")
#AutoConfigureMockMvc
public class ActuatorTests {
#Autowired
private MockMvc mockMvc;
#MockBean
JwtDecoder jwtDecoder;
#Test
public void testActuatorEndpointSuccess() throws Exception {
MockHttpServletResponse resp = mockMvc
.perform(MockMvcRequestBuilders.get("/actuator/").accept(MediaType.APPLICATION_JSON)).andReturn()
.getResponse();
assertEquals(resp.getStatus(), 200);
}
application-local.yml
This property contains Datasource, username, password and others properties
What is the issue?
During spring boot container start, it is creating Data Source by using data source properties of application-local.yml
Problem here is I can't rely on application-local.yml becoz properties changes environment to environment may not work all the time with same property values and also which is unnecessary for my Junit as the testcase is about testing the management actuator endpoint only.
What we have tried?
Ran by excluding some the JPA classes using below.
#SpringBootTest(properties = {
"management.endpoints.web.exposure.include=" })
#ActiveProfiles(profiles = "local")
#AutoConfigureMockMvc
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class ActuatorTests { .....}
But found the below error in the console.
Note: the error log also having chain of bean creation errors from DAO,Service, to Controller layer classes,
I have given only the tail of the log due to restrictions.
**Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available**
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:805)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1278)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:276)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
... 118 common frames omitted
Any help on this?
We can see similar question has been asked but no answer found in it.
Run junit without data source persistence context in spring boot
Any other solution to above actuator Test Junit is also welcome..

Spring annotation AOP called twice

I annotate my spring boot controller some functions with a custom annotation for logging purpose. However, I find the before advice is executed twice for nested methods. Looking for some idea here. Please refer to the code snippets below.
Controller
#RequestMapping(value = "apply")
#OperationMILog
public ApplyHttpResponse apply(#RequestHeader final String custId, #RequestAttribute final String cardNo,
#RequestBody final InstallmentApplyHttpRequest installApplyReq, #PathVariable final String source) {
//test
this.test(); //**line 387**
...
}
....
#OperationMILog
private String test() {
return this.test1(); //**line 593**
}
#OperationMILog
private String test1() {
return "test1";
}
The annotation
#Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface OperationMILog {
}
The Aspect
#Aspect
public class GenericLoggingAspect {
public static GenericLoggingAspect genericLoggingAspect;
#PostConstruct
public void init() {
genericLoggingAspect = this;
}
#Before("#annotation(com.mycomp.log.OperationMILog)")
public void doBefore(JoinPoint joinPoint) {
System.out.println("Before .........." + joinPoint.getSignature().getName());
}
}
When triggering the apply function in controller, the following logs are printed.
Before ..........apply
Before ..........test
Before ..........test
Before ..........test1
Before ..........test1
Settin break point at doBefore function and Looking into the stack trace in debug mode .
When the first "Before ............ test" is printed,the stack trace looks fine.
GenericLoggingAspect.doBefore(JoinPoint) line: 87
InstallmentController.apply(String, String, InstallmentApplyHttpRequest, String) line: 387
InstallmentController$$FastClassBySpringCGLIB$$55eeb128.invoke(int, Object, Object[]) line: not available
When the second "Before .......... test" is going to show, the stack trace is quite wired as below
GenericLoggingAspect.doBefore(JoinPoint) line: 87
InstallmentController.test() line: 593
InstallmentController.apply(String, String, InstallmentApplyHttpRequest, String) line: 387
InstallmentController$$FastClassBySpringCGLIB$$55eeb128.invoke(int, Object, Object[]) line: not available
I am running out of idea why line 593 triggers the doBefore. The same case applies to the printing of test1.
My project doesn't have any XML configuration, all configurations are done in annotations.
Thanks for showing the log output I asked for in my comment. Now I can tell you what the problem is:
Before ..........call(String com.mycomp.controller.InstallmentController.test())
Before ..........execution(String com.mycomp.controller.InstallmentController.test())
It is obvious that you use AspectJ (probably with LTW), not Spring AOP. Why can I say that? Because Spring AOP only knows execution() joinpoints, not call() ones.
For the reason given above, your pointcut matches twice for each method call: once for the joinpoint where the call is made (caller) and once for the joinpoint where the called method is actually executed (callee). This is why you get both output lines in your log.
So what you actually want to do is to specify in your pointcut what exactly you want to intercept, call or execution. I suggest you add && execution(* *(..)) to the pointcut. Then you get the expected result, similar to what Spring AOP would do even without that addition.
Lesson learned: AspectJ is much more powerful than Spring AOP. You need to learn how to wield such a powerful tool and sometimes limit its power on purpose. :-)

Can't update users email address or password using Thinktecture IdMgr + MembershipReboot.

When I edit a user in Thinktecture IdManager I can change their name, and age and other "custom" properties but when I try to change their email address or password my custom Membership Reboot repository update method is called once to save the change and then again to revert the change.
The controller is called twice by AutoFac container middleware for some reason.
There is only 1 HTTP post coming from the IdMgr application so it's not a client side issue.
The first stack trace is...
Gaist.Authentication.dll!Gaist.Authentication.MR.MembershipRebootRepository.Update(Gaist.Authentication.Mongo.Models.CustomUser item) Line 71 C#
[External Code]
IdentityManager.MembershipReboot.dll!IdentityManager.MembershipReboot.MembershipRebootIdentityManagerService<Gaist.Authentication.Mongo.Models.CustomUser,Gaist.Authentication.Mongo.Models.CustomGroup>.SetPassword(Gaist.Authentication.Mongo.Models.CustomUser account, string password) Line 235 C#
IdentityManager.dll!IdentityManager.ExpressionPropertyMetadata<Gaist.Authentication.Mongo.Models.CustomUser,string>.Set(object instance, string value) Line 87 C#
IdentityManager.dll!IdentityManager.PropertyMetadataExtensions.TrySet(IdentityManager.PropertyMetadata property, object instance, string value, out IdentityManager.IdentityManagerResult result) Line 137 C#
IdentityManager.dll!IdentityManager.PropertyMetadataExtensions.TrySet(System.Collections.Generic.IEnumerable<IdentityManager.PropertyMetadata> properties, object instance, string type, string value, out IdentityManager.IdentityManagerResult result) Line 123 C#
IdentityManager.MembershipReboot.dll!IdentityManager.MembershipReboot.MembershipRebootIdentityManagerService<Gaist.Authentication.Mongo.Models.CustomUser,Gaist.Authentication.Mongo.Models.CustomGroup>.SetUserProperty(System.Collections.Generic.IEnumerable<IdentityManager.PropertyMetadata> propsMeta, Gaist.Authentication.Mongo.Models.CustomUser user, string type, string value) Line 590 C#
IdentityManager.MembershipReboot.dll!IdentityManager.MembershipReboot.MembershipRebootIdentityManagerService<Gaist.Authentication.Mongo.Models.CustomUser,Gaist.Authentication.Mongo.Models.CustomGroup>.SetUserPropertyAsync(string subject, string type, string value) Line 503 C#
IdentityManager.dll!IdentityManager.Api.Models.Controllers.UserController.SetPropertyAsync(string subject, string type) Line 205 C#
[External Code]
IdentityManager.dll!IdentityManager.Configuration.Hosting.KatanaDependencyResolver.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Line 35 C#
[External Code]
IdentityManager.dll!IdentityManager.Configuration.Hosting.AutofacContainerMiddleware.Invoke(System.Collections.Generic.IDictionary<string,object> env) Line 51 C#
[External Code]
IdentityManager.dll!Owin.IdentityManagerAppBuilderExtensions.UseIdentityManager(Microsoft.Owin.IOwinContext ctx, System.Func<System.Threading.Tasks.Task> next) Line 55 C#
[Resuming Async Method]
[External Code]
The second stack trace is ...
Gaist.Authentication.dll!Gaist.Authentication.MR.MembershipRebootRepository.Update(Gaist.Authentication.Mongo.Models.CustomUser item) Line 71 C#
[External Code]
IdentityManager.MembershipReboot.dll!IdentityManager.MembershipReboot.MembershipRebootIdentityManagerService<Gaist.Authentication.Mongo.Models.CustomUser,Gaist.Authentication.Mongo.Models.CustomGroup>.SetUserPropertyAsync(string subject, string type, string value) Line 511 C#
IdentityManager.dll!IdentityManager.Api.Models.Controllers.UserController.SetPropertyAsync(string subject, string type) Line 205 C#
[External Code]
IdentityManager.dll!IdentityManager.Configuration.Hosting.KatanaDependencyResolver.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Line 35 C#
[External Code]
IdentityManager.dll!IdentityManager.Configuration.Hosting.AutofacContainerMiddleware.Invoke(System.Collections.Generic.IDictionary<string,object> env) Line 51 C#
[External Code]
IdentityManager.dll!Owin.IdentityManagerAppBuilderExtensions.UseIdentityManager(Microsoft.Owin.IOwinContext ctx, System.Func<System.Threading.Tasks.Task> next) Line 55 C#
[Resuming Async Method]
[External Code]
This was happening because the MembershipRebootIdentityManagerService implementation that comes out of the box was assuming that the repository would be based on Entity Framework and that calling save twice would be working on the same instance of an EF context and therfore the same entity.
My repo implementation didn't work that way so it was loading an unsaved entity and then overwriting the settings.
I had to frig my repository to hold onto the entity that's being edited for the lifetime of the repo.

Cannot remove a JPA entity using Spring Integration

When I try to remove an entity using Outbound Channel Adapter I always get removing a detached instance exception.
I know that an entity should be retrieved and deleted in the same transaction to avoid this exception, but how can I achieve it with Spring Integration?
To demonstrate the problem I modified the JPA sample:
PersonService.java
public interface PersonService {
...
void deletePerson(Person person);
}
Main.java
private static void deletePerson(final PersonService service) {
final List<Person> people = service.findPeople();
Person p1 = people.get(0);
service.deletePerson(p1);
}
spring-integration-context.xml
<int:gateway id="personService"
service-interface="org.springframework.integration.samples.jpa.service.PersonService"
default-request-timeout="5000" default-reply-timeout="5000">
<int:method name="createPerson" request-channel="createPersonRequestChannel"/>
<int:method name="findPeople" request-channel="listPeopleRequestChannel"/>
<int:method name="deletePerson" request-channel="deletePersonChannel"/>
</int:gateway>
<int:channel id="deletePersonChannel"/>
<int-jpa:outbound-channel-adapter entity-manager-factory="entityManagerFactory"
channel="deletePersonChannel" persist-mode="DELETE" >
<int-jpa:transactional transaction-manager="transactionManager" />
</int-jpa:outbound-channel-adapter>
When I call deletePerson I get the exception:
Exception in thread "main" java.lang.IllegalArgumentException:
Removing a detached instance
org.springframework.integration.samples.jpa.Person#1001
UPDATE:
Apparently I should've chosen a sample closer to my actual project, because here you can just create a new transaction programmatically and wrap both retrieve and delete function calls in it, as Artem did.
In my project I have a transformer connected to an outbound-channel-adapter. The transformer retrieves an entity and the outbound-channel-adapter removes it. How can I get the transformer and the outbound-channel-adapter to use the same transaction in this case?
To get it worked you should wrap all operations in the deletePerson to transaction, e.g.
private static void deletePerson(final PersonService service) {
new new TransactionTemplate(transactionManager)
.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
final List<Person> people = service.findPeople();
Person p1 = people.get(0);
service.deletePerson(p1);
}
});
}
In this case you should somehow provide to your method transactionManager bean too.
UPDATE:
I shown you a sample for use-case in the original question.
Now re. <transformer> -> <jpa:outbound-channel-adapter>.
In this you should understand where your message flow is started:
If it is <inbound-channel-adapter> with poller, so just make the <poller> <transactional>
If it <gateway>, who call <transformer>, so it's just enough to mark gateway's method with #Transactional
Here is one more transactional advice trick: Keep transaction within Spring Integration flow
In all cases you should get rid of <transactional> from your <jpa:outbound-channel-adapter>

Passing more than one object to an EJB remote interface

I am trying to pass more than object to an EJB remote interface deployed no Glassfish 3.1.1. Whatever is the first object it will pass fine the second object is always null. Is it due to the specs of rmi-iiop or is it a property setting?
Here is my property settings:
final Properties props = new Properties();
props.setProperty(InitialContext.STATE_FACTORIES,
"com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs",
"com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state",
"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
props.setProperty("org.omg.CORBA.ORBInitialHost", "192.168.1.192");
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
// props.setProperty("com.sun.appserv.iiop.orbconnections","5");
// Increase ORB Response Timeout to 5 min instead of 30 min:
// props.setProperty("com.sun.corba.ee.transport.ORBTCPTimeouts",
// "500:90000:20");
props.setProperty(
"com.sun.corba.ee.transport.ORBWaitForResponseTimeout",
"300000");
try {
InitialContext ic=new InitialContext(props);
TestRemote remote=(TestRemote) ic.lookup("java:global/com.capmtech_test.ear-ear_ear_1.0-SNAPSHOT/test.ear-ejb-1.0-SNAPSHOT/Test!com.capmtech.TestRemote");
Person p = new Person();
p.setName("Smith");
Phone m = new Phone();
remote.test(p, m);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Here is the stateless EJB:
#Stateless
public class Test implements TestRemote {
#EJB
private PersonFacadeLocal personFacade;
#Override
public void test(Person person, Phone mobile) {
Person p = person;
p.setMobile(mobile);
personFacade.create(p);
}
// Add business logic below. (Right-click in editor and choose
// "Insert Code > Add Business Method")
}
In this case mobile will always be null, if I swap the aruguments person would be null! If the IP was set to localhost everything would work fine.
Please help
This is all assuming you are running your glassfish server and the standalone client on the same machine. I am able to reproduce a problem in this area. While it is not exactly your problem, you may get results with the same changes. I am running GF 3.1 on Windows Vista. My windows\system32\drivers\etc\hosts file has the following lines:
10.99.0.199 pc-2017.pgx.local
127.0.0.1 localhost pc-2017.pgx.local
When my client software connects with:
props.put("org.omg.CORBA.ORBInitialHost", "localhost");
I get the error:
Exception in thread "main" javax.ejb.EJBException: java.rmi.RemoteException: CORBA INTERNAL 1330446421 No; nested exception is:
org.omg.CORBA.INTERNAL: ----------BEGIN server-side stack trace----------
org.omg.CORBA.INTERNAL: WARNING: IOP00710085: A reflective tie got an error while invoking method saveMonkey on class com.pts.monkey._MonkeySessionRemote_Remote
vmcid: OMG minor code: 85 completed: No
but then if I change the client to connect with:
props.put("org.omg.CORBA.ORBInitialHost", "pc-2017.pgx.local");
it works correctly.
replace the following lines:
props.setProperty("org.omg.CORBA.ORBInitialHost", "192.168.1.192");
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
with:
props.setProperty(Context.PROVIDER_URL,"iiop://192.168.1.192:3700");