Issues with setting up AppEngine Endpoint class from Objectify in Eclipse - google-app-engine

I am very new to Objectify and I am trying to create endpoints for a simple object. The issue that I am running into is that the endpoint class that Eclipse auto creates has references to JPA. Do I have to manually create my own endpoint class instead of using the "Generate Endpoint Class" in Eclipse? Here are the steps I took to set up my project and the source code:
Created a Web Application Project in Eclipse (app engine project)
Added Objectify-5.0.3.jar and guava-17.0.jar files to web-inf/lib and project's class path
Created a simple class Car.java and added annotations
Registered the entity in servlet (ObjectifyService.register(Car.class);
Right clicked on the Car class and selected Google App Engine (WPT) > Generate Cloud End Point Class
Deployed to App Engine and from API Explorer tried to insert a record and I got this error in logs:
https://objectify-example-650.appspot.com/_ah/api/carendpoint/v1/car
Method: carendpoint.insertCar
Error Code: 400
Reason: badRequest
Message: java.lang.IllegalArgumentException: Type ("com.appengine.objectify.Car") is not that of an entity but needs to be for this operation
I also see this error/warning in app engine project log:
<pre>
org.datanucleus.metadata.MetaDataManager loadPersistenceUnit: Class com.appengine.objectify.CarEndpoint was specified in persistence-unit transactions-optional but not annotated, so ignoring
</pre>
Here is my source code:
import com.googlecode.objectify.annotation.Entity;
#Entity
public class Car {
#Id Long id;
String vin;
String color;
private Car() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getVin() {
return vin;
}
public void setVin(String vin) {
this.vin = vin;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
The endpoint class:
#Api(name = "carendpoint", namespace = #ApiNamespace(ownerDomain = "appengine.com", ownerName = "appengine.com", packagePath = "objectify"))
public class CarEndpoint {
#SuppressWarnings({ "unchecked", "unused" })
#ApiMethod(name = "listCar")
public CollectionResponse<Car> listCar(
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit) {
EntityManager mgr = null;
Cursor cursor = null;
List<Car> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery("select from Car as Car");
if (cursorString != null && cursorString != "") {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<Car>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null)
cursorString = cursor.toWebSafeString();
for (Car obj : execute)
;
} finally {
mgr.close();
}
return CollectionResponse.<Car> builder().setItems(execute)
.setNextPageToken(cursorString).build();
}
#ApiMethod(name = "getCar")
public Car getCar(#Named("id") Long id) {
EntityManager mgr = getEntityManager();
Car car = null;
try {
car = mgr.find(Car.class, id);
} finally {
mgr.close();
}
return car;
}
#ApiMethod(name = "insertCar")
public Car insertCar(Car car) {
EntityManager mgr = getEntityManager();
try {
if (containsCar(car)) {
throw new EntityExistsException("Object already exists");
}
mgr.persist(car);
} finally {
mgr.close();
}
return car;
}
#ApiMethod(name = "updateCar")
public Car updateCar(Car car) {
EntityManager mgr = getEntityManager();
try {
if (!containsCar(car)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(car);
} finally {
mgr.close();
}
return car;
}
#ApiMethod(name = "removeCar")
public void removeCar(#Named("id") Long id) {
EntityManager mgr = getEntityManager();
try {
Car car = mgr.find(Car.class, id);
mgr.remove(car);
} finally {
mgr.close();
}
}
private boolean containsCar(Car car) {
EntityManager mgr = getEntityManager();
boolean contains = true;
try {
Car item = mgr.find(Car.class, car.getId());
if (item == null) {
contains = false;
}
} finally {
mgr.close();
}
return contains;
}
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
}
EFM class created by eclipse when autogenerating the endpoint class:
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public final class EMF {
private static final EntityManagerFactory emfInstance = Persistence
.createEntityManagerFactory("transactions-optional");
private EMF() {
}
public static EntityManagerFactory get() {
return emfInstance;
}
}

The auto-gen cloud endpoint classes, are for JPA/ JDO only, its covered here: Objectify with Endpoints for android Hope that helps

Thanks Tim! I ended up writing the endpoint class. As you mentioned eclipse does not create endpoint class for classes annotated for Objectity.
package com.appengine.objectify;
import static com.appengine.objectify.OfyService.ofy;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Named;
#Api(name = "carendpoint", namespace = #ApiNamespace(ownerDomain = "appengine.com", ownerName = "appengine.com", packagePath = "objectify"))
public class CarEndpoint {
#ApiMethod(name = "listCar")
public List<Car> listCar() {
List<Car> result = new ArrayList<Car>();
result = ofy().load().type(Car.class).list();
return result;
}
#ApiMethod(name = "getCar")
public Car getCar(#Named Long id) {
Car Car = ofy().load().type(Car.class).id(id).now();
return Car;
}
#ApiMethod(name = "insertCar")
public Car insertCar(Car Car) {
ofy().save().entity(Car).now();
return Car;
}
#ApiMethod(name = "removeCar")
public void removeCar(#Named Long id) {
ofy().delete().type(Car.class).id(id).now();
}
}

Related

How do I delete multiple records using REST API

Am new to Springboot, I have develop the resource to delete the record by ID, now I like delete selected multiple records.
Example: I like to delete 3 records out of 10 records in single request
Controller class:
#ApiHeader(
apiOperation = "delete a Content Manage by id",
apiOperationNotes = "delete a Content Manage by id"
)
#PostMapping(value = UriConstants.CONTENT_MANAGE_DELETE)
#ResponseStatus(HttpStatus.OK)
public void deleteContentManage(#PathVariable("content_manage_id") int contentmanageId) {
contentManageService.deleteContentManage(contentmanageId);
}
Service Class:
#Transactional(rollbackFor = Exception.class)
public void deleteContentManage(int contentmanageId) {
Optional<UserContentManage> optional = userContentManageRepository.findById(contentmanageId);
if(!optional.isPresent()){
log.error("Exception occurs while not found content manage ({}) in deletion. ", contentmanageId);
throw new GenericBadException(StaffNotificationExceptionEnum.CONTENT_MANAGE_NOT_FOUND_EXCEPTION);
}
userContentManageRepository.deleteById(contentmanageId);
}
JPA Class:
public interface UserContentManageRepository extends JpaRepository<UserContentManage, Integer> {
}
please suggest me how do I delete selected multiple records.
You can add method in Repository like
#Modifying
#Transactional
#Query("delete from UserContentManagep where u.id in(:integers)")
void deleteByIdIn(List<Integer> integers);
If you have implemented soft delete in project you can do soft delete like below:
#Modifying
#Transactional
#Query("update UserContentManagep u set u.active = false where u.id in(:integers)")
void softDeleteAllIds(List<Integer> integers);
And from service class you can try to call as
public void deleteAllBYIds(List<Integer> integers) {
personRepository.deleteByIdIn(integers);
}
Fully working example:
#RestController
#RequestMapping("/person")
public class PersonController {
private final PersonService personService;
#Autowired
public PersonController(PersonService personService) {
this.personService = personService;
}
#GetMapping
public Iterable<Person> list() {
return personService.list();
}
#PostMapping
public Person create(#RequestBody Person car) {
return personService.save(car);
}
#DeleteMapping
public String delete(#RequestParam("ids") List<Integer> ids) {
System.out.println("deleting");
personService.deleteAllBYIds(ids);
return String.join(",", ids.stream().map(value -> Integer.toString(value)).collect(Collectors.toList()));
}
}
#Getter
#Setter
#ToString
#Entity
#Where(clause = "active = true") // selecting only items which are active
class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private boolean active = true;
}
#Service
class PersonService {
private final PersonRepository personRepository;
#Autowired
PersonService(PersonRepository personRepository) {
this.personRepository = personRepository;
}
#Transactional
public Person save(Person person) {
return personRepository.save(person);
}
#Transactional(readOnly = true)
public Iterable<Person> list() {
return personRepository.findAll();
}
#Transactional(readOnly = true)
public PersonDTO findPersonByName(String name) {
return personRepository.findPersonsByName(name);
}
public void deleteAllBYIds(List<Integer> integers) {
// personRepository.deleteByIdIn(new ArrayList<>(integers));
personRepository.softDeleteAllIds(integers);
System.out.println("deleted adnlakdjakldlas");
}
}
interface PersonDTO {
String getName();
Collection<String> getPersonEvents();
}
#Repository
interface PersonRepository extends CrudRepository<Person, Integer> {
PersonDTO findPersonsByName(String name);
#Modifying
#Transactional
#Query("delete from Person p where p.id in(:integers)")
void deleteByIdIn(List<Integer> integers);
#Modifying
#Transactional
#Query("update Person p set p.active = false where p.id in(:integers)")
void softDeleteAllIds(List<Integer> integers);
}
First of all you need to create a jpa query method that brings all records belong to id.
public interface UserContentManageRepository extends JpaRepository<UserContentManage, Integer> {
List<UserContentManage> findAllById(Integer id);
}
After that you can do deleteAll() operation on List.
#Transactional(rollbackFor = Exception.class)
public void deleteContentManage(int contentmanageId) {
List<UserContentManage> userContentManageList = userContentManageRepository.findAllById(contentmanageId);
if(userContentManageList == null){
log.error("Exception occurs while not found content manage ({}) in deletion. ");
throw new GenericBadException(StaffNotificationExceptionEnum.CONTENT_MANAGE_NOT_FOUND_EXCEPTION);
}
userContentManageRepository.deleteAll(userContentManageList );
}

App Engine endpoint to accept POST data in request body

I have created a Google Endpoint in my App Engine Server as follows:
package com.xxxxx.gcmbackend;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.response.CollectionResponse;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Named;
import static com.xxxxxx.gcmbackend.OfyService.ofy;
#Api(
name = "register",
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "gcmbackend.xxxxx.com",
ownerName = "gcmbackend.xxxxx.com",
packagePath=""
)
)
public class UserRegistrationEndpoint {
private static final Logger log = Logger.getLogger(RegistrationEndpoint.class.getName());
#ApiMethod(name = "register")
public void registerDevice(#Named("regId") String regId, #Named("username") String username, #Named("phone") String phone) {
if(findRecord(regId) != null) {
log.info("Device " + regId + " already registered, skipping register");
return;
}
RegistrationRecord record = new RegistrationRecord();
record.setRegId(regId);
record.setUsername(username);
record.setPhone(phone);
ofy().save().entity(record).now();
}
private RegistrationRecord findRecord(String regId) {
return ofy().load().type(RegistrationRecord.class).filter("regId", regId).first().now();
}
}
This works perfectly in creating new User records. The API is of the following format:
http://example.appspot.com/_ah/api/register/v1/registerDevice/<regId>/<username>/<phone>
However, I want the url to look like this:
http://example.appspot.com/_ah/api/register/v1/registerDevice/
and then send POST data as follows:
{
regId: "some_value",
username: "some_value",
phone: "some_value"
}
What do I need to change in my Endpoint in order to achieve this format?
You need to create a java bean with regId, username and phone attributes e.g. RegistrationInput.
public class RegistrationInput {
private String regId;
private String username;
private String phone;
public String getRegId() {
return regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Then add the above java bean RegistrationInput, as a parameter to the ApiMethod
#ApiMethod(name = "register")
public void registerDevice(RegistrationInput input) {
.....
}

JDO + RequestFactory - entity versioning

I was trying to set-up a really trivial RequestFactory example, but I failed. I persist entities in to my datastore just find, however when trying to pull them out again, i get a
com.google.web.bindery.requestfactory.server.UnexpectedException: The persisted entity with id aglub19hcHBfaWRyCgsSBFVzZXIYBAw has a null version
So first of all this is my JPO annotated entity class. At the end you find to static function for RequestFactory to call, and a non-static member function which will become an InstanceRequest.
package com.test.server;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.annotations.Column;
import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.Version;
import javax.jdo.annotations.VersionStrategy;
#PersistenceCapable(identityType = IdentityType.APPLICATION)
#Version(strategy = VersionStrategy.VERSION_NUMBER, column = "VERSION", extensions = { #Extension(vendorName = "datanucleus", key = "field-name", value = "version") })
public class User {
public User() {
}
public User(String name) {
this.name = name;
}
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String id;
#Persistent
#Column(name = "version")
private Integer version;
#Persistent
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static final PersistenceManager persistenceManager() {
return PMF.get().getPersistenceManager();
}
#SuppressWarnings("unchecked")
public static List<User> findAllUsers() {
PersistenceManager pm = persistenceManager();
try {
String query = "SELECT FROM " + User.class.getName();
List<User> objects = (List<User>) pm.newQuery(query).execute();
objects.size(); // This is the workaround to retrieve all objects
return objects;
} finally {
pm.close();
}
}
public static User findUser(String id) {
PersistenceManager pm = persistenceManager();
try {
User u = pm.getObjectById(User.class, id);
return u;
} finally {
pm.close();
}
}
public void persist() {
PersistenceManager pm = persistenceManager();
try {
pm.makePersistent(this);
} finally {
pm.close();
}
}
}
The RequestFactory interface itself is really simple
package com.test.shared;
import com.google.web.bindery.requestfactory.shared.RequestFactory;
public interface UserOrderRequestFactory extends RequestFactory {
UserRequest userRequest();
}
so is the corresponding RequestContext
package com.test.shared;
import java.util.List;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.Service;
import com.google.web.bindery.requestfactory.shared.Request;
import com.test.server.User;
#Service(User.class)
public interface UserRequest extends RequestContext {
Request<List<UserProxy>> findAllUsers();
InstanceRequest<UserProxy, Void> persist();
}
Here is the proxy of user for the client side
package com.test.shared;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.ProxyFor;
#ProxyFor(com.test.server.User.class)
public interface UserProxy extends EntityProxy {
EntityProxyId<UserProxy> stableId();
String getName();
void setName(String name);
}
and finally my onModuleLoad() which first persists a user and then gets a list of all users.
public void onModuleLoad() {
final EventBus eventBus = new SimpleEventBus();
requestFactory = GWT.create(UserOrderRequestFactory.class);
requestFactory.initialize(eventBus);
UserRequest userRequest = requestFactory.userRequest();
UserProxy user = userRequest.create(UserProxy.class);
user.setName("Luigi");
userRequest.persist().using(user).fire( new Receiver<Void>()
{
#Override
public void onSuccess(Void arg0)
{
GWT.log("User persisted.");
}
});
userRequest = requestFactory.userRequest();
Request<List<UserProxy>> findAllUsersRequest = userRequest.findAllUsers();
findAllUsersRequest.fire( new Receiver<List<UserProxy>>() {
#Override
public void onSuccess(List<UserProxy> list) {
for(UserProxy u: list) {
GWT.log(u.getName());
}
}
});
Any input is welcome. I would be happy to receive any advice on this.
Thank you in advance.
While JPA seems to do this automatically it seems to be my job to advance the version counter in JDO. I added the following code to my persist routine in User.java
// JPA #Version does this automatically, but JDO #Version is not working like that. Not sure why.
if (version == null) {
version = 0l;
}
version++;
I am not sure if it matters but the #Version column = "VERSION" does not match the #Column(name = "version").
However GAE does not really have 'columns' as such and you can just ignore them by removing the column = "" and the #Column.
See http://gae-java-persistence.blogspot.com.au/2009/10/optimistic-locking-with-version.html

App Engine JPA Datastore delete entity

im trying to build a google app engine projekt with JPA, JAX-RS and JAX-B. My POST and GET Methods work, but my DELETE method doesn't delete the data.
Resource
#DELETE
#Path("card/{id}")
public void deleteCardById (#PathParam ("id") Long id) {
Service.removeCard(id);
}
Service
public static void removeCard(Long id) {
EntityManager em = EMFService.get().createEntityManager();
Card emp = findCard(id);
if (emp != null) {
em.remove(emp);
}
em.close();
}
public static Card findCard(Long id) {
EntityManager em = EMFService.get().createEntityManager();
Card card = em.find(Card.class, id);
em.close();
return card;
}
Entity
#XmlRootElement
#Entity
public class Card {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String begriff;
String tabu1;
String tabu2;
String tabu3;
public Card(String begriff, String tabu1, String tabu2, String tabu3) {
super();
Begriff = begriff;
Tabu1 = tabu1;
Tabu2 = tabu2;
Tabu3 = tabu3;
}
public Card() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBegriff() {
return Begriff;
}
public void setBegriff(String begriff) {
Begriff = begriff;
}
public String getTabu1() {
return Tabu1;
}
public void setTabu1(String tabu1) {
Tabu1 = tabu1;
}
public String getTabu2() {
return Tabu2;
}
public void setTabu2(String tabu2) {
Tabu2 = tabu2;
}
public String getTabu3() {
return Tabu3;
}
public void setTabu3(String tabu3) {
Tabu3 = tabu3;
}
#Override
public String toString() {
return "Card [Begriff=" + Begriff + ", Tabu1=" + Tabu1 + ", Tabu2="
+ Tabu2 + ", Tabu3=" + Tabu3 + "]";
}
When i Debug the app it gives the correct Object to the remove function. But it just don't remove the data ...
You mean you're using v1 of the GAE JPA plugin, and you don't bother putting a transaction around your remove (so the remove is delayed until the next transaction ... which never happens)?
Obviously you could either put a transaction around the remove, or better still you use v2 of the GAE JPA plugin
I was facing similar issue too. the JPA delete actually deletes the entity in the datastore,but it doesn't delete the entity from the JPA Cache.. You page is actually using the JPA Cached result list to display..
The way I used to resolve the issue is to have the JPA Cache cleared every time after a delete.
Sample Code would be something like this:
EM.getTransaction().begin();
EM.remove(current_record);
EM.getTransaction().commit();
EM.getEntityManagerFactory().getCache().evictAll();
ok i think i should write it like this
*edit the problem was the findCard function, i think because of the secone instance of the EntityManager. I chnaged it without using this method to this and now it works.
public static void removeCard(Long id) {
EntityManager em = EMFService.get().createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
Card card = em.find(Card.class, id);
if (card != null) {
em.remove(card);
}
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
}

Could not initialize class server.PMF while storing data in DataStore

I have been using these two functions for storing data and verifying data. These two functions are GWT-RPC service methods in Eclipse on server side:
public String greetServer(String mail,String pass)
{
User e;
PersistenceManager pm1 = PMF.get().getPersistenceManager();
try
{
e = pm1.getObjectById(User.class, mail);
} finally {
pm1.close();
}
if (e.getPassword()==pass)
{
return "valid";
}
else return "invalid";
}
public String UserRegister(String data[])
{
PersistenceManager pm2 = PMF.get().getPersistenceManager();
Date d1=new Date();
User u1=new User(data[0],data[1],data[2],data[3],d1);
try {
pm2.makePersistent(u1);
} finally {
pm2.close();
}
return "done";
}
And google App Engine gives the following error:
error java.lang.NoClassDefFoundError: Could not initialize class pathname/server.PMF
How can I solve this problem?
And I am using following PMF Class:
package com.google.gwt.sample.login.server;
import java.util.logging.Logger;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public final class PMF {
private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");
public static synchronized PersistenceManagerFactory get() {
return pmfInstance;
}
}

Resources