I am struggling while handling sessions in GAE. I am trying to store a two classes and a string in session. Although on DEV environment it runs fine, on production a class and a string are not being persisted in session. The class that is not getting saved as a session attribute is as follows:
#PersistenceCapable(detachable="true")
public class Agent implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent private String name; //Name of the Agency
#Element(dependent = "true")
private List<Contact> contacts = new ArrayList<Contact>();
#Element(dependent = "true")
private List<Agency> agencies = new ArrayList<Agency>();
#Persistent private List<Long> subAgents = new ArrayList<Long>();
#Persistent private Date createdOn = new Date();
}
I would like to mention again that it works fine on DEV Environment but on production I get values as null. As you can see I have made the class implement Serializable. But I think it is not the problem because I am setting one more attribute as a simple string and that also is failing (I get the attribute value as null). Session however is created as I can see it at the backend and also there is one more class which is persisted in session.
Anybody have suggestions? Thanks in advance.
Your problem is probably related to either:
GAE often serializes sessions almost immediately, dev environment doesn't. So all objects in your graph must implement Serializable.
BUT EVEN MORE LIKELY is that after you modify a session variable, you must do something like req.getSession().setAttribute(myKey,myObj) - it WILL NOT see changes in your object and automatically write them back to the session... so the session attributes will have the value of whatever they had when they were last set.
Problem #2 above cost me countless time and pain until I tripped over (via a lengthy process of elimination).
Have you enabled sessions in your configuration file?
http://code.google.com/intl/en/appengine/docs/java/config/appconfig.html#Enabling_Sessions
Making classes Agency and Contact Serializable solves the problem. That mean each and every object (be it nested or otherwise) which is present inside a session attribute should be serializable.
Related
Say this is my classes
#Entity
public class Library{
...
}
#Entity
public class Book{
#Load
#Parent
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
private Ref<Library> libraryRef;
#Ignore
private Library library;
}
I want to send List<Book> to the "android" client: I don't want the android client to get libraryRef but I want the client to get library
Here is the data access method I have now
public static List< Book > getAllBooks(){
return OfyService.ofy().load().type(Book.class).list();
}
My endpoint will just return List<Book> to android. I believe I have accomplished the first part: make sure datastore does not store library but libraryRef. But how do I accomplish the second part: make sure the client gets library?
I am sure it is not yet loaded. How do I make sure it is loaded? Do I have to use my own for-loop for iteration?
My advice for anyone working with code shared between client and server is to make a clean separation between your API objects and your domain objects. It's a little more work up front to make DTOs but it makes your whole system more flexible - if you want to change your domain objects, you don't risk breaking a zillion mobile phone apps that are on a slow (or nonexistant) upgrade cycle.
I try to make a chat solution on App Engine for my android app.
A decided that instead of save all messages send to a topic in a separated entity like ChatMessage or something like this, I can save them in a List of Strings inside the Topic entity, like this:
#Entity
public class Topic {
#Id
public String id;
public List<Long> users = new ArrayList<Long>(2);
public Long lastChangeTime;
public LinkedList<String> messages = new LinkedList<String>();
}
I came up with this because usually storing the topic id for every message is more data than the message string itself. :S
What I don't know is, can this list strong consistent?
This is how I add a new message to a Topic:
// 2. get topic if it exists or create a new if not
Topic topic = ofy().load().key(Key.create(Topic.class, topicId)).now();
if (topic == null) {
topic = new Topic(senderId, recipientId);
}
// 3. add message
// this method adds the new string into the topic and update the
// last change time
topic.addMessage(senderId, recipientId, send.message);
// 4. save topic & the new message
ofy().save().entity(topic).now();
So if two users send a message at the same time, can it happens that the first user load the Topic, add his message, but in the same time the second user already loaded the topic (without the first user's message) and add his own new message. The first save the topic first. But can the second override the previous save of first user? Or what happens?
If it can happen, how can i avoid this, bearing in mind that it's a high write rate entity so I need more write than 1/sec!
Thanks, and best regards.
What I don't know is, can this list strong consistent?
Consistency is determined by entity groups and queries, not properties.
So if two users send a message at the same time, can it happens that the first user load the Topic, add his message, but in the same time the second user already loaded the topic (without the first user's message) and add his own new message. The first save the topic first. But can the second override the previous save of first user? Or what happens?
You would need to do this inside a transaction. If a ConcurrentModificationException is thrown inside the transaction (your example scenario) then Objectify will retry for you.
But, to avoid the contention, you will need to change your data model. You could have a Message class and a Topic, like this:
#Entity
public class Topic {
#Id
String id;
List<Long> users = new ArrayList<Long>(2);
Long lastChangeTime;
}
And a Message referencing one or more topics (I'm making assumptions here):
#Entity
public class Message {
#Id
Long id;
Long lastChangeTime;
#Index
Ref<Topic> topic;
}
The #Index annotation on the topic will allow you to query for Messages by topic. You could change the Ref<Topic> to a List of same if you messages can be in multiple topics.
Hi I´m new using GAE and JPA, and I´m having some problems trying to update an entity. I copy next a code example:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String userName;
#ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
private Address address;
}
When I save on datastore a User instance it's store without problems.
After this, I retrive this instance from the datastore, then I set a new userName and try to update it.
public User updateUser(User user) {
EntityManager mgr = getEntityManager();
if (!containsUser(user)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(user);
}
The update is performed, the new userName is stored in the dataStore, but as the Address field has FetchType.LAZY I'm losing the persisted value on the update.
How can I make an update of some fields without losing other values?
Try getting and saving the entity in the same method, otherwise you will lost the session and the manager will recognize the lazy attributes as null ones.
Hope it helps!
My web app suddenly won't allow people to buy our products. I don't know what to do.
Last night one of our engineers accidentally removed the #Persistent tag from an owned one-to-many field (in the parent class). He uploaded this without testing it to the App Engine server. We got reports that people could not buy things, so we looked at the changes, went in and restored the #Persistent tag.
But now we get an exception every time the code tries to modify that field.
Please help - I don't know what to do - we've added the #Persistent annotation back - why would this exception be happening??
#PersistenceCapable
public class Published {
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#PrimaryKey
private Long id;
...
// see note about this field above
#Persistent(defaultFetchGroup = "true", mappedBy = "pub")
private List<License> licenses;
...
}
#PersistenceCapable
public class License {
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#PrimaryKey
private Key id;
#Persistent
private Published pub;
...
}
The following exception happens when I create a new Published and call pub.getLicensed().add(new Licensed(...))
Uncaught exception from servlet
java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
at org.datanucleus.store.appengine.DatastoreRelationFieldManager.checkForParentSwitch(DatastoreRelationFieldManager.java:203)
at org.datanucleus.store.appengine.DatastoreRelationFieldManager$1.setObjectViaMapping(DatastoreRelationFieldManager.java:134)
at org.datanucleus.store.appengine.DatastoreRelationFieldManager$1.apply(DatastoreRelationFieldManager.java:113)
at org.datanucleus.store.appengine.DatastoreRelationFieldManager.storeRelations(DatastoreRelationFieldManager.java:82)
at org.datanucleus.store.appengine.DatastoreFieldManager.storeRelations(DatastoreFieldManager.java:959)
at org.datanucleus.store.appengine.DatastorePersistenceHandler.storeRelations(DatastorePersistenceHandler.java:585)
at org.datanucleus.store.appengine.DatastorePersistenceHandler.insertPostProcess(DatastorePersistenceHandler.java:320)
at org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObjects(DatastorePersistenceHandler.java:272)
at org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:256)
at org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent(JDOStateManagerImpl.java:3185)
at org.datanucleus.state.JDOStateManagerImpl.makePersistent(JDOStateManagerImpl.java:3161)
at org.datanucleus.ObjectManagerImpl.persistObjectInternal(ObjectManagerImpl.java:1298)
at org.datanucleus.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:1476)
at org.datanucleus.store.mapped.scostore.ElementContainerStore.validateElementForWriting(ElementContainerStore.java:380)
at org.datanucleus.store.mapped.scostore.FKListStore.validateElementForWriting(FKListStore.java:609)
at org.datanucleus.store.mapped.scostore.FKListStore.internalAdd(FKListStore.java:344)
at org.datanucleus.store.appengine.DatastoreFKListStore.internalAdd(DatastoreFKListStore.java:146)
at org.datanucleus.store.mapped.scostore.AbstractListStore.add(AbstractListStore.java:105)
at org.datanucleus.sco.backed.List.add(List.java:649)
at (my servlet)
Looks like an App Engine error was preventing me from uploading the code. Once GAE caught up and switched over to the new code, it all worked fine.
For the record, I'm not sure what fixed it - it may have been just adding the #Persistent annotation, or it may have been changing Published.id to be a Key instead of a Long.
I have an Employee class
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String firstName;
#Persistent
private String lastName;
#Persistent
private Date hireDate;
public Employee(String firstName, String lastName, Date hireDate) {
this.firstName = firstName;
this.lastName = lastName;
this.hireDate = hireDate;
}
// Accessors for the fields. JDO doesn't use these, but your application does.
public Key getKey() {
return key;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getHireDate() {
return hireDate;
}
public void setHireDate(Date hireDate) {
this.hireDate = hireDate;
}
}
I have used the JDO for the app engine. Now I want to share this code between server and client. In which package should I keep this. In fact I have tried both way. Neither worked out. Please share if you have already done this type of codes.
If what you are looking for is instantiating of your entities in both client and server, putting the classes under the "client" package will do the trick.
But if you are trying to pass your persistent entities through RPC, that probably wont work out of the box. DataNucleus "enhaces" the bytecode, and RPC can't serialize then. Hibernate has a similar problem, please take a look at this article, it explains the problem very well and presents alternatives.
I am creating DTOs to workaround this problem. It is a little more work, but it really depends on how many Entities you have.
I've done this before, but just in a small test app. Assuming you're using GWT-RPC, it should work pretty smoothly. You'll have to do two things:
Put the code in a 'client' namespace, i.e. in a directory that's getting compiled by GWT. You can still use this code on the server.
Hit compile and start fixing errors. The main one you'll find is that the 'Key' type isn't available in GWT land. You can use a string-encoded key instead. See the "key as encoded string" section in the relevant documentation.
If you're not using GWT-RPC, you're on your own. JSON is attractive for this purpose but requires significant legwork. This should be better in GWT 2.0 but won't entirely go away.
We probably need more detail, as you could be hitting a number of problems, but here's some tips:
The package doesn't matter so much as long as both the GWT compiler and javac can see it. I keep shared code in a package appropriately named... "shared". :)
Key is not available in GWT, so use an encoded string Key.
JDO is tricky, but workable. Newer versions of GWT (after Java AppEngine was released) have been able to handle DataNucleus' JDO enhancement. I'd make sure you are working off of trunk or a recent snapshot, in case DataNucleus is your problem.
Make sure you detach your objects before sending them to the client.
That's why I am using the low-level api. I wrote a helper class that converts an entity to a pojo and back. This way, I get the Entity which gets converted into my desired POJO that then goes to the client. From the client, the same POJO goes back to the server gets converted into an entity by my helper class and then a simple "put" call does the trick. You don't need to dettach/attach anything... I can share some code if you want.