I followed this tutorial for setting up my Google App Engine instance and I am also using Firebase. My goal is to put all of the "computation" on Google App Engine. I want to call a function like this one below:
MyEndpoint:
package productions.widowmaker110.backend;
/** An endpoint class we are exposing */
@Api(
name = "myApi",
version = "v1",
namespace = @ApiNamespace(
ownerDomain = "backend.widowmaker110.productions",
ownerName = "backend.widowmaker110.productions",
packagePath=""
)
)
public class MyEndpoint {
/** A simple endpoint method that takes a name and says Hi back */
@ApiMethod(name = "sayHi")
public MyBean sayHi(@Named("name") String name) {
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
// Read from the database
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
String value = dataSnapshot.getValue(String.class);
Log.d(TAG, "Value is: " + value);
}
@Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
MainActivity:
package productions.widowmaker110.gpsweather;
// imports...
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new EndpointsAsyncTask().execute(new Pair<Context, String>(this, "Manfred"));
}
class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private MyApi myApiService = null;
private Context context;
@Override
protected String doInBackground(Pair<Context, String>... params) {
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
@Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
@Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
}
}
}
I understand the above code for Firebase is Android specific so running on a Google App Engine instance doesn't work. I was wondering if any one knows how to perform CRUD operations on a firebase database from Google App Engine backend. Any help is appreciated.
You'll need to use the Firebase Server SDK to make server side calls. You can find info on setting that up and using it here:
Add Firebase to your Server
Firebase Server SDK Installation & Setup
When using Firebase with Google Cloud Endpoints be aware that you'll need to use Firebase methods in conjunction with the Tasks API. Since Firebase methods are not blocking, if you don't use Tasks your Endpoint will return before the call you made to Firebase has a chance to return its result. For a brief intro on using tasks check out the link below. Its a talk given at Google I/O 2016. The speaker is talking about Tasks and Firebase on Android but the concepts are the same when using Tasks and Firebase on a server. Note that they have included the Tasks API with the Firebase Server SDK. I've skipped to the portion of the talk which deals directly with tasks.
Firebase SDK for Android: A tech deep dive
The samples below are if you need to process the result from your Firebase read/write operation before your Endpoint returns a value or if other code depends on the result of the Firebase read/write operation. These are server-side examples. I’m going to assume you care whether or not the write operation was successful. There may be better ways to perform these server-side operations but this is what I’ve done so far.
Sample write operation using setValue():
When a call is made to the
setValue()
method it returns aTask
object, keep a reference to it.Create a
TaskCompletionSource
object. This object should be parameterized with the result type of your choice. I’m going to useBoolean
as the result type for this example.Generate a
Task
from theTaskCompletionSource
that was created in step 2. Again, the generatedTask
should use the same parameter type as theTaskCompletionSource
object.Add a completion listener to the
Task
that was generated by the call tosetValue()
. In the completion listener set the appropriate result on theTask
created in step 3. CallingsetResult()
on yourTaskCompletionSouce
object will mark theTask
created from it as complete. This is important for step 5.Call
Task.await()
to block the current thread until theTask
you are interested in has completed. We are waiting for theTask
generated by theTaskCompletionSource
object to be marked complete. ThisTask
will be considered complete when we callsetResult()
on theTaskCompletionSource
used to generate theTask
as we did in step 4. Once complete it will return the result.That’s it, the current thread will block until
Tasks.await()
returns a value. You can also (and should) set a timeout value on theTasks.await()
method if you want to keep the current thread from being blocked indefinitely.If you were only interested in whether or not the
Task
generated bysetValue()
completed and didn’t care if it was successful or not then you can skip the creation of theTaskCompletionSource
and just useTasks.await()
directly on thatTask
. The same works forupdateChildren()
. And if you like you can just use the method calls forupdateChilden()
orsetValue()
which use aDatabaseReference.CompletionListener
along with a TaskCompletionListener.Waiting for a read operation to complete is similar.
Sample read operation using addListenerForSingleValueEvent()
Create a
TaskCompletionSource
object parameterized with the result you expect from theTask
that will be generated from it.Generate a
Task
from theTaskCompletionSource
objectCall
addListenerForSingleValueEvent()
on ourDatabaseReference
and callsetResult()
on theTask
generated in step 2.Call
Tasks.await()
to block the current thread until theTask
you are interested in has completed. TheTask
will be considered complete when we callsetResult()
as we did in step 3 and will return the result.As mentioned above you can use the
Tasks.await()
method along with a timeout value to prevent the current thread from being blocked indefinitely.Just as a heads up I've found that Firebase doesn't kill the background thread that is uses for its operations. This means that the GAE Instance is never idle. Check out this thread for more info:
Firebase, Background Threads, and App Engine