I am using the Google Plugin for Eclipse, and I am writing an App Engine app as a Dynamic Web Module in Eclipse WTP.
I have defined the following Java class to serve as a Cloud Endpoint API:
package mypackage;
import static mypackage.OfyService.ofy;
import java.util.List;
import java.util.logging.Logger;
import mypackage.models.ProbeEntry;
import mypackage.models.ProbeSet;
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.config.Named;
import com.googlecode.objectify.ObjectifyService;
#Api(name = "analysisEndpoint",
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "myorg",
ownerName = "myorg",
packagePath = "analysis")
)
public class AnalysisEndpoint {
private static final Logger logger = Logger.getLogger(AnalysisEndpoint.class.getName());
#ApiMethod(name = "getMyProbeEntries", httpMethod = ApiMethod.HttpMethod.GET)
public ProbeSet getMyProbeEntries(#Named("amount") int amount) {
ObjectifyService.begin();
List<ProbeEntry> probeList = ofy().load().type(ProbeEntry.class).limit(amount).list();
return new ProbeSet(probeList);
}
}
I attempt to deploy to the Google App Engine by right-clicking the project -> Google App Engine WTP -> Deploy Project to Remote Server. I see in my console that the project is compiling and uploading, but eventually errors out with:
99% Endpoints configuration not updated. The app returned an error when the Google Cloud Endpoints server attempted to communicate with it.
The error log on the app engine shows the following:
18:31:58.119
javax.servlet.ServletContext log: unavailable
com.google.api.server.spi.config.validation.MissingParameterNameException: analysisEndpoint.myorg.analysis.AnalysisEndpoint.getMyProbeEntries parameter (type int): Missing parameter name. Parameter type (int) is not an entity type and thus should be annotated with #Named.
at
com.google.api.server.spi.config.validation.ApiConfigValidator.validateApiParameter(ApiConfigValidator.java:214)
...
As can be seen in the code, I do have #Named("amount") before the offending parameter. What is going wrong here? Side note: If I simply remove the amount parameter, the project deploys to App Engine without a problem.
Any help would be greatly appreciated.
Related
We deploy our app on Google app engine standard environment. We need to access Memorystore(redis) from our app engine.
Following the document, we create Serverless VPC access connector and configure the app engine:
<vpc-access-connector>
<name>projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME</name>
</vpc-access-connector>
and set the IAM permissions. But we still can not connect to redis instance at private IP like 10.0.0.4 using jedis:
Jedis jedis = new Jedis("10.0.0.4");
It should work if you deploy it with gcloud beta app deploy target/SNAPSHOT.
I prepared and uploaded a sample in Github.
How I did it in a new project:
Enabled App Engine, selected region us-central (corresponds to us-central1)
Created Memorystore instance, in region us-central1
Created VPC Connector, in region us-central1 (At the moment no other region can be selected, so both App Engine and Memorystore have to be in us-central1)
Added the VPC connector in appengine-web.xml:
<vpc-access-connector>
<name>projects/PROJECT_ID/locations/us-central1/connectors/CONNECTOR_NAME</name>
</vpc-access-connector>
Modified pom.xml, adding the following dependency:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
Modified the servlet.java file:
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import redis.clients.jedis.Jedis;
#WebServlet(
name = "Redis",
description = "Redis: Connect to Redis",
urlPatterns = "/redis"
)
public class RedisServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String s;
try{
Jedis jedis = new Jedis("10.0.0.4");
jedis.set("myKey", "It's alive");
s = "Life test: "+ jedis.get("myKey");
}catch(Exception e){s = "Couldn't connect "; e.printStackTrace();}
resp.getWriter().write(s);
}
}
Ran the following to package and deploy:
mvn package (This will create a "target" folder)
gcloud beta app deploy target/ARTIFACT_ID-1.0-SNAPSHOT
Note that it's still in BETA and it might not work very reliably.
I have a Scala web application running in GAE. I need to use a Java library -JWI- which requires me to pass a root folder of Wordnet into edu.mit.jwi.Dictionary's constructor.
I thought about putting all Wordnet stuff into Google Cloud Storage, but it doesn't have a concept of a folder at all. So, my question: is there any way to do what I want with Google Cloud Storage or should I use anything else?
You were right when you stated “there is no API in Google Cloud Java library for folder manipulation”. As of today, there’s no folder manipulation for the java client library. You can check the library here
You can use Google Cloud Storage (GCS), even if gsutil handles subdirectories in a different way, because it behaves as a regular folder and uses the same notation.
I am not sure about how your application works but if I am guessing well:
Load the JWI library to your Cloud Shell.
Import the library in your Scala application in App Engine flexible. Find an example here on how to call a Java class using Scala.
Deploy the application. Following the previous steps, the image deployed will contain the JWI library you need.
Load the Wordnet semantic dictionary in a bucket and pass the root folder of Wordnet, in this case a GCS folder, using the Java client library for the Google Cloud Storage API. The “Dictionary” must be downloaded (using a get function) and locally stored while you are using it.
Find here the Java client library documentation for Cloud Storage. You might need more functions than the ones below which I have written for you, to create a bucket, upload a file and download it.
package com.example.storage;
// Imports the Google Cloud client library
import com.google.cloud.storage.Acl;
import com.google.cloud.storage.Acl.Role;
import com.google.cloud.storage.Acl.User;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
public class QuickstartSample {
public static void main(String... args) throws Exception {
// Instantiates a client
Storage storage = StorageOptions.getDefaultInstance().getService();
// The name for the new bucket
String bucketName = args[0]; // "my-new-bucket";
// Creates the new bucket
Bucket bucket = storage.create(BucketInfo.of(bucketName));
System.out.printf("Bucket %s created.%n", bucket.getName());
// [START uploadFile]
// Object name
String fileName="filename.ext";
// Create file inside the bucket
BlobInfo blobInfo =
storage.create(
BlobInfo
.newBuilder(bucketName, fileName)
// Modify access list to allow all users with link to read file
.setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER))))
.build()
// other options required
);
// return the public download link
blobInfo.getMediaLink();
// [END uploadFile]
// Copy file from a bucket
String blobName = "filename.ext";
BlobId blobId = BlobId.of(bucketName, blobName);
Blob blob = storage.get(blobId);
}
Finally, find here how to compile the code and running it:
mvn clean package -DskipTests
mvn exec:java -Dexec.mainClass=com.example.storage.QuickstartSample -Dexec.args="bucketName"
I am very new to google app engine and endpoints and have been writing basic endpoint functions and deploying to the cloud. I succesfully deployed a HelloWorld endpoint and tested it over the API explorer: http://localhost:8080/_ah/api/explorer
But now when I have created a new endpoint API and followed the same steps (i.e deployed using new APP engine application name in the appengine-web.xml, run as appengine:update), the api explorer still shows my HelloWorld endpoint instead of my new API "yourfirstendpoint".
I've searched and tried to find an answer to no avail - and im sorry if this is a very basic and stupid question on my part (im sure it is) but i would realy appreciate if somebody could point me in the right direction on what i should be doing.
My API
package com.example.zinglife;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;
import com.google.api.server.spi.response.NotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
/**
*
* Defines endpoint functions APIs.
*/
#Api(name = "yourfirstapi", version = "v1",
scopes = {Constants.EMAIL_SCOPE },
clientIds = {Constants.API_EXPLORER_CLIENT_ID},
description = "API for hello world endpoints.")
public class YourFirstAPI
{
#ApiMethod(name = "storeUserModel")
private User storeUserModel(User user) throws NotFoundException
{
String email = user.getEmail();
Key key = KeyFactory.createKey("User",email);
User userEntity = null;
try
{
if (userEntity==null)
{
userEntity = new User();
userEntity.setName(user.getName());
userEntity.setEmail(user.getEmail());
userEntity.setCountry(user.getCountry());
//
}
return userEntity;
}//*endtry
finally
{
}
}
}
The App engine Administrator Log after running the code:
Please let me know if any other information is needed :)
Make sure you have added your new service as one of the values for the 'services' parameter of the EndPointsServlet.
<servlet>
<!-- This is version 2.0 of the endpoints framework. -->
<servlet-name>EndpointsServlet</servlet-name>
<servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<!-- Comma separated classes that provide endpoints -->
<param-value>
com.mycompany.myproduct.endpoint.SomeServiceV1,
com.mycompany.myproduct.endpoint.SomeServiceV2,
com.mycompany.myproduct.endpoint.SomeOtherServiceV1,
com.mycompany.myproduct.endpoint.SomeOtherServiceV2,
com.mycompany.myproduct.endpoint.SomeOtherServiceV3
</param-value>
</init-param>
</servlet>
I need to debug my Google Cloud Endpoint after it is deployed to AppEngine. I am trying to write entries to the log but they never show up in the log viewer on the Google Developers Console. Here is the logging code I created in Android Studio:
import java.util.logging.Logger;
...
public class MyEndpoint {
private static final Logger log = Logger.getLogger(MyEndpoint.class.getName());
...
log.info("message to log");
This code executes in the cloud without error but nothing shows up in the log. What am I doing wrong?
The entry is not showing in the log because the default logging level in app engine is: WARNING. Setting the logging level prior to calling log.info() caused the log entry to show in the console as expected. Here is the revised code with setLevel in context:
import java.util.logging.Logger;
...
public class MyEndpoint {
private static final Logger log =Logger.getLogger(MyEndpoint.class.getName());
...
log.setLevel(Level.INFO);
log.info("message to log");
I want to separate packages for UI and backend development of my GWTP app.
Currently my UI access the backend using Rest dispatch configured like this:
bindConstant().annotatedWith(RestApplicationPath.class).to("/MyProject/api");
I want to access remote service using localhost UI (running GWT app using eclipse plugin). I changed the above line to:
bindConstant().annotatedWith(RestApplicationPath.class).to("http://my-app.appspot.com/MyProject/api");
Using this, call successfully reaches server ( I can see this in appengine logs) but UI always gets back status code 0.
What is wrong with above setup? Do I have to do something else to access remote service using GWT ui ?
If you want to have a solution that works both on localhost/App Engine, you'd want to use something like this:
import com.google.gwt.core.client.GWT;
import com.google.gwt.inject.client.AbstractGinModule;
import com.google.inject.Provides;
import com.gwtplatform.dispatch.rest.client.RestApplicationPath;
import com.gwtplatform.dispatch.rest.client.gin.RestDispatchAsyncModule;
public class ServiceModule extends AbstractGinModule {
#Override
protected void configure() {
install(new RestDispatchAsyncModule.Builder().build());
}
#Provides
#RestApplicationPath
String getApplicationPath() {
String baseUrl = GWT.getHostPageBaseURL();
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
}
return baseUrl + "/MyProject/api";
}
}
The string returned by getApplicationPath will be bound to #RestApplicationPath and used seamlessly by GWTP's RestDispatch.
In your case, the string will resolve to http://localhost:8080/MyProject/api or "http://my-app.appspot.com/MyProject/api" depending on the app running locally or on App Engine.