我正在尝试创建一个Android应用程序,它与Google App Engine进行交互,以加载和保存Google数据存储区中的实体。我想我的配置正确,但我不知道我是否可以上传我的应用程序来测试它,它只是永远挂起然后给我这个错误:
SEVERE: Endpoints configuration not updated. The app returned an error when the Google Cloud Endpoints server attempted to communicate with it.
但是,如果我更改版本号,那么即使在收到此消息后,新版本也会在我的控制台中显示。我已经阅读了有关Datastore的所有Google文档,但无法弄清楚出了什么问题。我已经有一个月的这个问题,并且无法找到解决方案。我在某处读到,如果应用程序包含错误,则无法上传,因此我将包含所有代码。我也不知道自己在做什么,甚至不确定我是否在正确的轨道上为什么这不起作用,所以如果你发现任何愚蠢的事情,你应该告诉我。提前谢谢你,如果你能为我解决这个问题,你将成为我的英雄和我的上帝。
这是我的终点:
package com.polo.backend.endpoints;
/**
* Created by sagesmith on 4/16/16.
*
* Is used to interact with the server
*/
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.ConflictException;
import com.google.api.server.spi.response.NotFoundException;
import com.googlecode.objectify.Ref;
import com.polo.backend.services.ObjcfyService;
import com.polo.backend.entities.Event;
import com.polo.backend.entities.User;
import java.util.List;
import javax.inject.Named;
/**
*
*/
@Api(
name = "userEndpoint",
version = "v1",
namespace = @ApiNamespace(
ownerDomain = "backend.polo.com",
ownerName = "backend.polo.com",
packagePath = ""
)
)
public class UserEndpoint {
/**
* Registers the two entities used in this class
*/
public UserEndpoint(){
ObjcfyService.register(User.class);
ObjcfyService.register(Event.class);
}
@ApiMethod(name = "insertUser")
/**
* Inserts a new {@code User} into the data store
*/
public void insertUser(User user) throws ConflictException, NotFoundException{
if(getUser(user.getUsername()) != null){
throw new ConflictException("Object already exists");
}
}
/**
*
* @param user
* @throws NotFoundException
*/
@ApiMethod(name = "updateUser")
public void updateUser(User user) throws NotFoundException{
if(getUser(user.getUsername()) != null) {
ObjcfyService.objectify().save().entity(user).now();
}
}
/**
*
* @param username
* @throws NotFoundException
*/
@ApiMethod(name = "removeUser")
public void removeUser(@Named("username") String username) throws NotFoundException{
User record = getUser(username);
if(record != null){
ObjcfyService.objectify().delete().entity(record).now();
}
}
/**
*
* @param username
* @return
* @throws NotFoundException
*/
@ApiMethod(name = "getUser")
public User getUser(@Named("username") String username) throws NotFoundException{
User user = ObjcfyService.objectify().load().type(User.class).id(username).now();
if(user != null) {
List<Ref<User>> friendRefs = user.getFriendRefs();
List<Ref<Event>> repeatingEventRefs = user.getRepeatingEventRefs();
List<Ref<Event>> nonRepeatingEventRefs = user.getNonRepeatingEventRefs();
for (Ref<User> friendRef : friendRefs) {
user.addFriend(friendRef.get());
}
for (Ref<Event> repeatingEventRef : repeatingEventRefs) {
user.addRepeatingEvent(repeatingEventRef.get());
}
for (Ref<Event> nonRepeatingEventRef : nonRepeatingEventRefs) {
user.addNonRepeatingEvent(nonRepeatingEventRef.get());
}
} else{
throw new NotFoundException("User not found");
}
return user;
}
}
这是我的用户实体:
package com.polo.backend.entities;
/**
* Created by sagesmith on 4/16/16.
*/
import com.google.api.server.spi.config.AnnotationBoolean;
import com.google.api.server.spi.config.ApiResourceProperty;
import com.googlecode.objectify.Ref;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Load;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
@Entity
public class User {
@Id private String username = null;
@Index private String firstName = null;
@Index private String lastName = null;
@Nullable private String status = null;
@Nullable private String location = null;
@Load @Nullable private List<Ref<User>> friendRefs = new ArrayList<>();
@Load @Nullable private List<Ref<Event>> nonRepeatingEventRefs = new ArrayList<>();
@Load @Nullable private List<Ref<Event>> repeatingEventRefs = new ArrayList<>();
@Nullable public List<User> friends = new ArrayList<>();
@Nullable public List<Event> nonRepeatingEvents = new ArrayList<>();
@Nullable public List<Event> repeatingEvents = new ArrayList<>();
/**
*Returns the first name from the {@code User}
*
* @return the first name of the {@code User} as a {@code String}
*/
public String getFirstName(){
return firstName;
}
/**
* Returns the last name from the {@code User}
*
* @return the last name of the {@code User} as a {@code String}
*/
public String getLastName(){
return lastName;
}
/**
* Return the username from the user
*
* @return the username of the user as a String
*/
public String getUsername(){
return username;
}
/**
* Sets the username of the {@code User}
*
* @param username Username to set the {@code User}'s username to
*/
public void setUsername(String username){
this.username = username;
}
/**
* Sets the firstName of the {@code User}
*
* @param firstName First name to set the {@code User}'s first name to
*/
public void setFirstName(String firstName){
this.firstName = firstName;
}
/**
* Sets the lastName of the {@code User}
*
* @param lastName Last name to set the {@code User}'s last name to
*/
public void setLastName(String lastName){
this.lastName = lastName;
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Adds a friend to the {@code User}'s list of friends
*
* @param user User to add
*/
public void addFriendRef(User user){
friendRefs.add(Ref.create(user));
}
/**
* Returns the {@code User}'s friends
*
* @return {@code List<Users>} the friends of the {@code User}
*/
public List<User> getFriends(){
return friends;
}
/**
* Adds a friend to the users {@code List} of friends
*
* @param user {@code User} to add as a friend
*/
public void addFriend(User user){
friends.add(user);
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Returns a {@code List<Ref<User>>} of references to the {@code User}'s friends
*
* @return {@code List<Ref<User>>}
*/
public List<Ref<User>> getFriendRefs(){
return friendRefs;
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Returns a {@code List<Ref<Event>>} of references to the {@code User}'s repeating
* {@code Event}s
*
* @return {@code List<Ref<Event>>}
*/
public List<Ref<Event>> getRepeatingEventRefs(){
return repeatingEventRefs;
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Returns a {@code List<Ref<Event>>} of references to the {@code User}'s non repeating
* {@code Event}s
*
* @return {@code List<Ref<Event>>}
*/
public List<Ref<Event>> getNonRepeatingEventRefs(){
return nonRepeatingEventRefs;
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Sets the {@code User}'s status to a given {@code String}
*
* @param status {@code String} to set an {@code Users}'s status to
*/
public void setStatus(String status){
this.status = status;
}
/**
* Returns the {@code User}'s status as a {@code String}
*
* @return {@code String} status
*/
public String getStatus(){
return status;
}
/**
* Returns the {@code User}'s location as a {@code String}
*
* @return {@code String} location
*/
public String getLocation(){
return location;
}
/**
* Sets the {@code User}'s location to a given {@code String}
*
* @param location {@code String} to set an {@code Users}'s location to
*/
public void setLocation(String location){
this.location = location;
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Adds a repeating {@code Event} to the {@code User}'s schedule
*
* @param event {@code Event} to add to the {@code User}'s schedule
*/
public void addRepeatingEvent(Event event){
repeatingEventRefs.add(Ref.create(event));
}
@ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
/**
* Adds a non repeating {@code Event} to the user's schedule
*
* @param event Event to add to the the user's schedule
*/
public void addNonRepeatingEvent(Event event){
nonRepeatingEventRefs.add(Ref.create(event));
}
/**
* Returns a {@code List<Event>} of repeating {@code Event}s from the user's schedule
*
* @return List<Event> repeatingEvents from user's schedule
*/
public List<Event> getRepeatingEvents(){
return repeatingEvents;
}
/**
* Returns a list of nonRepeatingEvents from the user's schedule
*
* @return List<Event> repeatingEvents from user's schedule
*/
public List<Event> getNonRepeatingEvents(){
return nonRepeatingEvents;
}
}
和我的活动实体:
package com.polo.backend.entities;
import com.googlecode.objectify.annotation.Entity;
/**
* Created by sagesmith on 5/3/16.
*/
@Entity
public class Event {
private Integer startIndex;
private Integer endIndex;
private Byte[] color;
public Event(Integer startIndex, Integer endIndex, Byte red, Byte green, Byte blue){
this.startIndex = startIndex;
this.endIndex = endIndex;
color = new Byte[]{red, green, blue};
}
}
和我的ObjectifyService:
package com.polo.backend.services;
/**
* Created by sagesmith on 4/16/16.
*/
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;
public class ObjcfyService {
public static void register(Class<?> eClass){
ObjectifyService.register(eClass);
}
public static Objectify objectify(){
return ObjectifyService.ofy();
}
public static ObjectifyFactory objectifyFactory(){
return ObjectifyService.factory();
}
}
这是我的appengine-web.xml,我已经用my_id替换了我的appid,原因很明显:
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>my_id</application>
<version>1</version>
<threadsafe>true</threadsafe>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
这是我的web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>SystemServiceServlet</servlet-name>
<servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>com.polo.backend.endpoints.UserEndpoint</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SystemServiceServlet</servlet-name>
<url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
最后,这是我用来与Android应用程序中的数据存储区通信的AsyncTask,我已经用my_id替换了我的应用程序ID:
package com.polo.client;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.polo.StaticAssets;
import com.polo.activity.Home;
import com.polo.backend.userEndpoint.UserEndpoint;
import com.polo.backend.userEndpoint.model.User;
import java.io.IOException;
/**
* Created by sagesmith on 4/17/16.
*/
public class UserAsyncTask extends AsyncTask<Void, Void, User>{
private static UserEndpoint userApiService = null;
private Home context;
private String username;
/**
*
* @param context
*/
public UserAsyncTask(Home context){
this.context = context;
username = StaticAssets.getUserName(context);
}
/**
*
* @param params
* @return
*/
@Override
protected User doInBackground(Void... params){
if(userApiService == null){
UserEndpoint.Builder builder = new UserEndpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(),
null
)
.setRootUrl("https://my_id.appspot.com/_ah/api/");
userApiService = builder.build();
}
User user = null;
try{
user = userApiService.getUser(username).execute();
} catch (IOException e){
Log.e("User not found", e.getMessage());
}
return user;
}
/**
*
* @param result
*/
@Override
protected void onPostExecute(User result) {
if(result!=null)
Toast.makeText(context, result.getFirstName(), Toast.LENGTH_LONG).show();
}
}
答案 0 :(得分:2)
有助于查看建议的完整错误消息:
有关详细信息,请参阅部署疑难解答文档: https://developers.google.com/appengine/docs/java/endpoints/test_deploy#troubleshooting_a_deployment_failure
作为第一步,文档建议检查日志中的“ / _ ah / spi / BackendService.getApiConfigs ”。如果我尝试根据示例部署端点后端,我会在日志中看到以下内容:
javax.servlet.ServletContext log: unavailable
java.lang.reflect.InvocationTargetException
at com.google.appengine.runtime.Request.process-b693af604777a85a(Request.java)
...
Caused by: java.lang.StackOverflowError
...
com.googlecode.objectify.impl.translate.ClassTranslatorFactory.create(ClassTranslatorFactory.java:49)
...
此错误仅在调用ObjectifyService.register(class)
时发生,嗯......
当Objectify尝试注册类并导致StackOverflowError时,User
类中会出现无限递归:
@Nullable public List<User> friends = new ArrayList<>();
不是定义自己的列表,而是通过定义Ref<User>
的列表来解决这个问题,就像在类中的其他地方一样(可能上面是拼写错误):
@Nullable public List<Ref<User>> friends = new ArrayList<>();
另外,另一个问题是Event
类缺少@Id引起的问题
因此固定:
@Entity
public class Event {
@Id private Long id;
...
进行这些更改后,我可以毫无错误地部署Endpoints应用程序。