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():
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass obj = new YourModelClass();
When a call is made to the setValue()
method it returns a Task
object, keep a reference to it.
Task<Void> setValueTask = ref.setValue(obj);
Create a TaskCompletionSource
object. This object should be parameterized with the result type of your choice. I’m going to use Boolean
as the result type for this example.
final TaskCompletionSource<Boolean> tcs = new TaskCompletionSource<>();
Generate a Task
from the TaskCompletionSource
that was created in step 2. Again, the generated Task
should use the same parameter type as the TaskCompletionSource
object.
Task<Boolean> tcsTask = tcs.getTask();
Add a completion listener to the Task
that was generated by the call to setValue()
. In the completion listener set the appropriate result on the Task
created in step 3. Calling setResult()
on your TaskCompletionSouce
object will mark the Task
created from it as complete. This is important for step 5.
setValueTask.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if(task.isSuccessful()){
tcs.setResult(true);
}else{
tcs.setResult(false);
}
}
});
Call Task.await()
to block the current thread until the Task
you are interested in has completed. We are waiting for the Task
generated by the TaskCompletionSource
object to be marked complete. This Task
will be considered complete when we call setResult()
on the TaskCompletionSource
used to generate the Task
as we did in step 4. Once complete it will return the result.
try {
Boolean result = Tasks.await(tcsTask);
}catch(ExecutionException e){
//handle exception
}catch (InterruptedException e){
//handle exception
}
That’s it, the current thread will block until Tasks.await()
returns a value. You can also (and should) set a timeout value on the Tasks.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 by setValue()
completed and didn’t care if it was successful or not then you can skip the creation of the TaskCompletionSource
and just use Tasks.await()
directly on that Task
. The same works for updateChildren()
. And if you like you can just use the method calls for updateChilden()
or setValue()
which use a DatabaseReference.CompletionListener
along with a TaskCompletionListener.
Waiting for a read operation to complete is similar.
Sample read operation using addListenerForSingleValueEvent()
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass mModelClassObject;
Create a TaskCompletionSource
object parameterized with the result you expect from the Task
that will be generated from it.
final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>();
Generate a Task
from the TaskCompletionSource
object
Task<YourModelClass> tcsTask = tcs.getTask();
Call addListenerForSingleValueEvent()
on our DatabaseReference
and call setResult()
on the Task
generated in step 2.
ref.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
YourModelClass result = dataSnapshot.getValue(YourModelClass.class);
if(result != null){
tcs.setResult(result);
}
}
@Override
public void onCancelled(DatabaseError databaseError){
//handle error
}
});
Call Tasks.await()
to block the current thread until the Task
you are interested in has completed. The Task
will be considered complete when we call setResult()
as we did in step 3 and will return the result.
try {
mModelClassObject = Tasks.await(tcsTask);
}catch(ExecutionException e){
//handle exception
}catch (InterruptedException e){
//handle exception
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With