Google App Engine: Object with id “” is managed by a different Object Manager - Revisited - spring

I'm getting the following error using GAE, JPA, and Spring
Object with id “” is managed by a different Object Manager
When I first create an account, I put the User object in the session. Then when I update the user profile during that initial session, I merge the detached User. All works great.
I then logout and later create a new session. This time, I load the User object and place into the session. Still OK, but problem is when I update the user profile, the merge fails with the above error.
public boolean loadProfile(String openId, String email) {
User user = null;
try {
user = userDao.findByOpenId(openId);
} catch (NoResultException e) {
}
if (user != null) {
logger.error(JDOHelper.getPersistenceManager(user));
getSessionBean().setUser(user);
return true;
} else {
user = createNewAccount(openId, email);
getSessionBean().setUser(user);
return false;
}
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
private User createNewAccount(String openId, String email) {
User user = new User();
user.setDisplayName(Long.toString(System.currentTimeMillis()));
OpenIdentifier oid = new OpenIdentifier();
oid.setOpenId(openId);
oid.setEmail(email);
oid.setUser(user);
Set<OpenIdentifier> openIds = new HashSet<OpenIdentifier>();
openIds.add(oid);
user.setOpenIds(openIds);
user = userDao.merge(user);
return user;
}
#Transactional(propagation=Propagation.REQUIRED)
public void createOrUpdate(ActionEvent e) {
logger.error(JDOHelper.getPersistenceManager(userFacade.getDelegate()));
User user = userDao.merge(userFacade.getDelegate());
sessionBean.setUser(user);
}
I found these related questions, but I'm still not able to fix.
AppEngine datastore: "Object with id ... is managed by a different Object Manager"
Google App Engine - Object with id "" is managed by a different - JPA
Datanucleus: moving from #Transactional to non-transactional
http://www.atentia.net/2010/03/object-with-id-is-managed-by-a-different-object-manager/
WRT closing the PM (as per 1 & 2), I'm not able to explicitly close the PM since I'm using Spring
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter. From logs, it appears to be opening and closing on each page request.
WRT making the entity detachable (as per 3 & 4), first of all, I'm using JPA and it seems wrong to use a JDO-related annotation. Secondly, it didn't work when I tried.
For extra credit, how do you debug with JDOHelper.getPersistenceManager(obj)? I am getting null in this case, as the User was detached between page requests. That seems normal to me so I'm not clear how to debug with it.

You don't have a PM, you have an EM. No idea what you're referring to there.
Detachable : with JPA all classes are (enhanced as) detachable
You're using some ancient GAE JPA plugin there (v1.x?), and that uses old versions of DataNucleus that are not supported. Use GAE JPA v2.x. "ObjectManager" hasn't existed in DataNucleus for years.
You (or the software you're using) have to close the EM or you get resources leaked all over the place.
NucleusJPAHelper.getEntityManager(obj); is how you get the EntityManager that manages an object (in DataNucleus v3.x, used by GAE JPA v2.x)

Related

Design issue in Spring app: in which layer (web, service or repository) should I retrieve the currently logged in user?

I work on a CRUD Spring app. Let me explain a basic use case:
A user can save an Advertisement. As of now I retrieve the currently logged in member in the web/controller layer and then pass it on to the service layer so that it can be set on the advertisement (the currently logged in user is the owner of the Advertisement; it is retrieve using Spring Security and my custom annotation: #CurrentMember).
In controller layer:
#RequestMapping(value = "/family/new", method = RequestMethod.POST, produces = "text/html")
public String newFamilyAdvertisement(
#ModelAttribute("advertisementInfo") #Validated(value = ValidationGroups.AdvertisementCreation.class) FamilyAdvertisementInfo familyAdvertisementInfo,
BindingResult bindingResult, Model model, #CurrentMember Member member) {
if (bindingResult.hasErrors()) {
populateFamilyAdvertisementModel(model, familyAdvertisementInfo, member);
return "advertisement/family/new";
}
advertisementService.createAdvertisement(member, familyAdvertisementInfo.getAdvertisement(), familyAdvertisementInfo.getAddressReference());
return "redirect:/advertisement/family/new";
}
In service layer:
#Override
public void createAdvertisement(Member member, Advertisement advertisement, String addressReference) {
if (member == null || advertisement == null || addressReference == null || addressReference.isEmpty()) {
throw new IllegalArgumentException("One argument is null or empty");
}
Address address = geolocationService.retrieveAddressFromReference(addressReference);
advertisement.setAddress(address);
advertisement.setMember(member);//SET CURRENTLY LOGGED IN USER
advertisement.setValidated(Boolean.FALSE);
advertisement.setActive(Boolean.TRUE);
advertisement.setCreationDate(utils.now());
saveAdvertisement(advertisement);
}
Still in service layer(Roo ITD):
public void AdvertisementServiceImpl.saveAdvertisement(Advertisement advertisement) {
advertisementRepository.save(advertisement);
}
Now the interrogation I have is:
Should I retrieve the current user/member as early as possible (here in the web layer) and then pass it on until it is needed (here in the service layer)? OR
Should I retrieve the current user/member only when I need it (here in the service layer)?
Thats a matter of design and choices you need to made, usually you dont need to bother controller to pass member to service. It doesnt need any knowledge about user. You can easily load it in service so you api is shorter/cleaner.
But, in a case your api is used from some external project - then api should show what objects are needed to make it work.
To sum up, in your case I would load it in service.

Spring ACL adding ACE when the current user has no permission on the ACL

Short Question: When a new user signs up on my website I need to add a read permission to a domain object. Spring ACLs does a check using the current user's permissions to see if a permission can be added. This will always fail because the user has just signed up and has no permissions on the object that they need a read permission on. Is there a way to skip the security check in certain situations?
Long question: The website I'm working on has an invite system that is done using tokens and emails. User A creates an organization and becomes the owner of that organization. User A can then invite User B to the organization by email, an email will be sent telling them to signup, etc. When User B gets the invite they sign up and the token is looked up. This token has a relation to an organization and at this point I try to give the user a ReadPermission but I get the error:
"org.springframework.security.acls.model.NotFoundException: Unable to locate a matching ACE for passed permissions and SIDs"
Is there a way around this security check?
Or
How far into Spring Security do I need to go to change this setup?
Found the answer to this when reading spring docs. You can use a Runnable and DelegatingSecurityContextExecutor to run the required call as a different user.
User newUser = getCurrentUser();
User notOriginalUser = accountsService.findUserById(someId);
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new UsernamePasswordAuthenticationToken(notOriginalUser, "doesnotmatter", authoritiesList);
context.setAuthentication(authentication);
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor = new DelegatingSecurityContextExecutor(delegateExecutor, context);
class PermissionRunnable implements Runnable {
String u;
Organization o;
PermissionRunnable(Organization org, String username) {
o = org;
u = username;
}
public void run() {
permissionsService.setReadableByPrincipal(o, new PrincipalSid(u));
}
}
Runnable originalRunnable = new PermissionRunnable(org, newUser.getUsername());
executor.execute(originalRunnable);

Hibernate's session.currentSession() with #Transactional errors while session.openSession() doesn't

I have a web app where a RESTful front end produces entries in the database and a job wired with cron expressions fire off logic to process the records. The relationship I have is
User 1 -> * Notification
When notifications are sent they are removed. On rare occasion, if a User receives a notification (to be persisted) as the job is deleting records out of their list of notifications (after being sent) I get a constraint violation on the join table where the join column must reference a notification in the notification table.
public void deleteNotifsInUser(List<Notification> notifsToDelete, User user) {
if(user != null && notifsToDelete != null && notifsToDelete.size() > 0) {
if(log.isDebugEnabled()) {
log.debug(notifsToDelete);
}
Session session = sessionFactory.openSession();
User userInDb = (User) session.merge(user);
for (Notification n : notifsToDelete) {
Object notifInDb = session.merge(n);
userInDb.getNotifications().remove(notifInDb);
session.delete(notifInDb);
}
session.close();
}
}
Can anyone tell me why using a #Transaction on this method would cause a different behavior than using the API to open a session?
By having a quick look at your code you open the session inside an if statement and base on some conditions. When you use the annotation it opens the session before the if statements and regardless of the conditions. That could be the reason behind the different behaviours.

Session management for a RESTful Web Service using Jersey

I am developing a Restful Web Service using Jersey between my Android, iPhone apps and MySQL. I also use Hibernate to map the data to the database.
I have a sessionId (key). it is generated when user Login to the system.
In User class:
public Session daoCreateSession() {
if (session == null) {
session = new Session(this);
} else {
session.daoUpdate();
}
return session;
}
In Session Class:
Session(User user) {
this.key = UUID.randomUUID().toString();
this.user = user;
this.date = new Date();
}
void daoUpdate() {
this.key = UUID.randomUUID().toString();
this.date = new Date();
}
When user Sign in to the system successfully, I send this sessionId to the Mobile app client. Then when I want to get some information from database based on the logged in user, I check this Session key as authentication in the REST Services for every request.
For example for the list of project that user is involved in, I use client.GET(SERVER_ADDRESS/project/get/{SessionID})
insetead of client.GET(SERVER_ADDRESS/project/get/{username}).
And if it is not a valid session key, I'll send back to the client a 403 forbidden code.
You can also take a look here
The thing is I am not sure about my approach. what do you think about cons in this approach considering for Jersey and a mobile app?
I still don't know if the Session key approach is a good idea in my case.
If you want to use SessionId then it should have a validation time, like this:
private static final int MINUTES = 90;
public boolean isValid() {
return System.currentTimeMillis() - date.getTime() < 1000 * 60 * MINUTES;
}
This is a solved problem - servlet containers like Tomcat already do session management, and can distribute session state to other containers in the cluster either by broadcasting over TCP, or by using a shared data source like memcache.
I'd suggest reading up on what's already available, rather than inadvertently reinventing the wheel. Additionally, this is going to become an incredibly hot table table if your application proves popular. How will you clear out old session IDs?

Can I switch use of 'entities.SingleOrDefault' ==> 'entities.Find' without hazards?

In my WCF service's business logic, most of the places when I need to locate an entity, I use this syntax:
public void UpdateUser(Guid userId, String notes)
{
using (ProjEntities entities = new ProjEntities())
{
User currUser = entities.SingleOrDefault(us => us.Id == userId);
if (currUser == null)
throw new Exception("User with ID " + userId + " was not found");
}
}
I have recentely discovered that the DbContext has the Find method, and I understand I can now do this:
public void UpdateUser(Guid userId, String notes)
{
using (ProjEntities entities = new ProjEntities())
{
User currUser = entities.Find(userId);
if (currUser == null)
throw new Exception("User with ID " + userId + " was not found");
}
}
Note : the 'userId' property is the primary key for the table.
I read that when using Find method entity framework checks first to see if the entity is already in the local memory, and if so - brings it from there. Otherwise - a trip is made to the database (vs. SingleOrDefault which always makes a trip to the database).
I was wondering if I now will convert all my uses of SingleOrDefault to Find is there any potential of danger?
Is there a chance I could get some old data that has not been updated if I use Find and it fetches the data from memory instead of the database?
What happens if I have the user in memory, and someone changed the user in the database - won't it be a problem if I always use now this 'memory' replica instead of always fetching the latest updated one from the database?
Is there a chance I could get some old data that has not been updated
if I use Find and it fetches the data from memory instead of the
database?
I think you have sort of answered your own question here. Yes, there is a chance that using Find you could end up having an entity returned that is out of sync with your database because your context has a local copy.
There isn't much more anyone can tell you without knowing more about your specific application; do you keep a context alive for a long time or do you open it, do your updates and close it? obviously, the longer you keep your context around the more susceptible you are to retrieving an up to date entity.
I can think of two strategies for dealing with this. The first is outlined above; open your context, do what you need and then dispose of it:
using (var ctx = new MyContext())
{
var entity = ctx.EntitySet.Find(123);
// Do something with your entity here...
ctx.SaveChanges();
}
Secondly, you could retrieve the DbEntityEntry for your entity and use the GetDatabaseValues method to update it with the values from the database. Something like this:
var entity = ctx.EntitySet.Find(123);
// This could be a cached version so ensure it is up to date.
var entry = ctx.Entry(entity);
entry.OriginalValues.SetValues(entry.GetDatabaseValues());

Resources