【问题标题】:Google Cloud Endpoints client libraries generation duplicates entitiesGoogle Cloud Endpoints 客户端库生成重复实体
【发布时间】:2014-04-14 14:15:49
【问题描述】:

当我为我的端点生成客户端库时,我发现了一个奇怪的行为。

在我的 appengine 项目中,我有两个端点类来处理两个实体的操作:

实体组的GroupEndpoint

实体联系人的ContactEndpoint

组实体有一个联系人列表,因为有时当调用 GroupEndpoint 的 API 方法时,我必须更新它的联系人。

问题是当我生成客户端库时,Contact 实体是在两个不同的命名空间(每个端点一个)中生成的,这很令人困惑,因为我最终得到了同一个类(完全相同)两次。

这是一个例子:

Group.java

package backend;

import java.util.List;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable
public class Group {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private List<Contact> contactList;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public List<Contact> getContactList() {
        return contactList;
    }

    public void setContactList(List<Contact> contactList) {
        this.contactList = contactList;
    }
}

GroupEndpoint.java(示例的虚拟代码)

package backend;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.response.CollectionResponse;

import javax.annotation.Nullable;
import javax.inject.Named;

@Api(name = "groupendpoint")
public class GroupEndpoint {

    @ApiMethod(name = "listContact")
    public CollectionResponse<Group> listGroup(
            @Nullable @Named("cursor") String cursorString,
            @Nullable @Named("limit") Integer limit) {
        return null;
    }
}

Contact.java

package backend;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable
public class Contact {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
}

ContactEndpoint.java(示例的虚拟代码)

package backend;

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 javax.annotation.Nullable;
import javax.inject.Named;

@Api(name = "contactendpoint")
public class ContactEndpoint {

    @ApiMethod(name = "listContact")
    public CollectionResponse<Contact> listContact(
            @Nullable @Named("cursor") String cursorString,
            @Nullable @Named("limit") Integer limit) {
        return null;
    }
}

build.gradle

// Currently, the appengine gradle plugin's appengine devappserver launch doesn't interact well with Intellij/AndroidStudio's
// Gradle integration.  As a temporary solution, please launch from the command line.
// ./gradlew modulename:appengineRun
// If you would like more information on the gradle-appengine-plugin please refer to the github page
// https://github.com/GoogleCloudPlatform/gradle-appengine-plugin

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.google.appengine:gradle-appengine-plugin:1.9.1'
    }
}

repositories {
    mavenCentral();
}

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'

sourceCompatibility = 1.7
targetCompatibility = 1.7

dependencies {
  appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.1'
  compile 'javax.servlet:servlet-api:2.5'
    compile 'com.google.appengine:appengine-endpoints:1.9.1'
    compile 'com.google.appengine:appengine-endpoints-deps:1.9.1'
    compile 'javax.servlet:servlet-api:2.5'
    compile 'org.datanucleus:datanucleus-core:3.2.13'
    compile 'org.datanucleus:datanucleus-api-jpa:3.2.3'
    compile 'javax.jdo:jdo-api:3.0.1'
    compile 'org.datanucleus:datanucleus-api-jdo:3.2.8'
    compile 'org.datanucleus:datanucleus-jdo-query:3.0.2'
    compile 'com.google.appengine.orm:datanucleus-appengine:2.1.2'
    compile 'org.apache.geronimo.specs:geronimo-jpa_2.0_spec:1.1'
    compile 'com.ganyo:gcm-server:1.0.2'
    compile 'net.sf.javaprinciples.persistence:persistence-api:4.0.0'
}

appengine {
  downloadSdk = true
  appcfg {
    oauth2 = true
  }
}

当我生成客户端库时,它会在我的构建目录中创建 groupendpoint-v1-java.zip 和 contactendpoint-v1-java.zip。如果我提取这些文件,我会看到每个 zip 文件都有一个 Contact 类。

对于 groupendpoint-v1-java.zip:

/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://code.google.com/p/google-apis-client-generator/
 * (build: 2014-04-15 19:10:39 UTC)
 * on 2014-04-22 at 12:22:19 UTC 
 * Modify at your own risk.
 */

package com.appspot.myapplicationid.groupendpoint.model;

/**
 * Model definition for Contact.
 *
 * <p> This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the groupendpoint. For a detailed explanation see:
 * <a href="http://code.google.com/p/google-http-java-client/wiki/JSON">http://code.google.com/p/google-http-java-client/wiki/JSON</a>
 * </p>
 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class Contact extends com.google.api.client.json.GenericJson {

  @Override
  public Contact set(String fieldName, Object value) {
    return (Contact) super.set(fieldName, value);
  }

  @Override
  public Contact clone() {
    return (Contact) super.clone();
  }

}

对于contactendpoint-v1-java.zip:

/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://code.google.com/p/google-apis-client-generator/
 * (build: 2014-04-15 19:10:39 UTC)
 * on 2014-04-22 at 12:22:21 UTC 
 * Modify at your own risk.
 */

package com.appspot.myapplicationid.contactendpoint.model;

/**
 * Model definition for Contact.
 *
 * <p> This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the contactendpoint. For a detailed explanation see:
 * <a href="http://code.google.com/p/google-http-java-client/wiki/JSON">http://code.google.com/p/google-http-java-client/wiki/JSON</a>
 * </p>
 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class Contact extends com.google.api.client.json.GenericJson {

  @Override
  public Contact set(String fieldName, Object value) {
    return (Contact) super.set(fieldName, value);
  }

  @Override
  public Contact clone() {
    return (Contact) super.clone();
  }

}

请注意,唯一的区别是它们属于不同的命名空间。当我使用客户端库时,这太令人困惑了。

如何避免这种行为?

谢谢。

【问题讨论】:

  • 您是如何(使用哪些工具)生成客户端库的?
  • 我在 Eclipse 中使用 Google 插件生成了客户端库,然后我迁移到了 Android Studio,使用 Gradle 的 appengine 插件得到了相同的结果。请参阅Google Plugin for Eclipse tutorial的步骤 3@
  • 我现在看到了您的问题。现在我怀疑是否有办法避免这个问题。端点生成器正在创建类并确定命名空间......我在使用 .Net Web 服务时遇到了同样的问题,唯一的方法是在您的客户端代码中处理它。

标签: java google-app-engine google-cloud-endpoints


【解决方案1】:

我遇到了同样的问题,我想出了解决这个问题的方法。它可能有一些优点和限制,但我会在这里介绍它们。

您有不同的模型(在客户端),因为它们位于不同的库中。例如,您的联系人列表将位于一个 libcontactendpoint 和另一个 libgroupendpoint 中,因为您的 Contact 类在两者中都使用。

为了在客户端只有一个类代表您的实体,您只需将其保留在一个端点中(在客户端项目中)。因此,一种方法是使用 @Api 注释。此注释用于您的所有端点类。因此,如果您的类 GroupEndpoint 具有 @Api(name = "groupendpoint"),则将创建一个 libgroupendpoint。如果您希望联系人和组都在同一个端点中,并且没有重复,那么您的端点类必须指向同一个 API。 解决方案:

package backend;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.response.CollectionResponse;

import javax.annotation.Nullable;
import javax.inject.Named;

//Changing api name
@Api(name = "generalendpoint")
public class GroupEndpoint {

    @ApiMethod(name = "listContact")
    public CollectionResponse<Group> listGroup(
            @Nullable @Named("cursor") String cursorString,
            @Nullable @Named("limit") Integer limit) {
        return null;
    }
}

ContactEndpoint 也将具有 generalendpoint(您可以使用任何名称,条件是它们相等)。

package backend;

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 javax.annotation.Nullable;
import javax.inject.Named;

@Api(name = "generalendpoint")
public class ContactEndpoint {

    @ApiMethod(name = "listContact")
    public CollectionResponse<Contact> listContact(
            @Nullable @Named("cursor") String cursorString,
            @Nullable @Named("limit") Integer limit) {
        return null;
    }
}

现在,你不会有这个问题了。但是,请记住,现在在客户端,您必须使用 Generalenpoint,而不是使用 Groupendpoint 或 Contactendpoint 来执行操作并调用这些端点方法。

缺点:

  1. 所有指向同一个@Api的类的方法在云端点库生成后都会在同一个类中
  2. 类中指向同一个@Api(但Java中的不同类)的方法不能具有相同的名称@ApiMethod(name = "nameOfMethod")

1 不是那么重要,因为您不编辑库,只需调用该方法。在您的应用引擎项目中跨类创建方法时,需要注意第二点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 2013-08-11
    相关资源
    最近更新 更多