Sunday, March 04, 2018

Integrating Firebase with Jooby

Lately, I've become enamored of RESTful web microframeworks written in Java, such as:

Almost any of these frameworks will take 99% of the pain out of setting up a RESTful web app, and allow you to have working code almost immediately (assuming you know enough Maven and/or Gradle to get by), but I quickly settled on Jooby as the best impedance match for my particular needs (which are modest) and my skillset (likewise modest). Spark came in a close second.

Jooby is an amazingly powerful framework with some really endearing features (which I don't have time to go into here). I can't say enough good things about Jooby. Go to https://jooby.org/quickstart/ right now and check it out, if you're a Java developer.

Once I realized I could get Java compilation to work in Microsoft's fantastic Visual Studio Code (using the nifty Java extensions from RedHat), and once I realized I could launch my stuff straight from the Terminal in VS Code, I was off and running. When I learned that running a project with "mvn jooby:run" would not only start up the app and server (I'm using Undertow as the embedded server, incidentally, with great results...) but also let me automagically hot-deploy my Java app from VS Code just by doing a Save (with automatic recompilation triggered by Save), I was sold.

Jooby integrates with a lot of great stuff out of the box, but one thing I wanted was JSON-based NoSQL database persistence (without having to resort to something as elaborate as MongoDB). I spent an hour or so researching existing object databases to see which ones might be Java-friendly, easy to learn, embeddable, and lightweight (BWAH HA HA) -- to little avail. Ultimately, I decided it might be fun to go in a slightly different direction and give Google's Firebase Cloud a try.

That's when the misery started.

I tried putting the necessary dependencies in my pom.xml and setting up my imports in my Jooby app, but 9 lines of code immediately turned red and I had strange syntax errors on perfectly fine constructions, with obscure warnings about Maven m2e, such as maven-enforcer-plugin (goal "enforce") is ignored by m2e.

Then it turned out my Firebase import statements weren't working, but sometimes (if I moved code around) they did resolve. And then sometimes, a Maven compile would complete flawlessly; but then a mvn package or jooby:run would immediately fail with unresolved "compilation issues."

When things go this sour this fast, you have to take a deep breath and start looking at how to get something to work again (maybe by removing code).

Fast-forward a couple hours. I decided to go back to the Firebase web site and make sure I was following their Getting Started instructions exactly. I was. Then I Googled around until I came upon https://github.com/GoogleCloudPlatform/java-docs-samples, where there was a firestore (not Firebase) project with a Quickstart.java file that held all the answers.

Long story short, many of the imports were new, the techniques for setting the connection options and obtaining the database handle didn't end up matching anything on Google's Firebase Quickstart page, and I had to disable getUpdateTime() on results.get(), but ultimately, I succeeded in posting a new document record to the demo database from my Jooby test app.

The code that finally worked, for me, is shown below with no cleanups whatsoever; I decided to leave all the uglinesses in place so you can get a feel for how much trial and error was involved in finally getting this shit stuff to work.

You'll have to ignore some of my test routes. The main thing I wanted to get working was the /firebaseTest route, which triggers the updateFirebase() method.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.kt;

import org.jooby.Jooby;

import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.DocumentReference;
// [START fs_include_dependencies]



import java.util.HashMap;

import java.util.Map;
// import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.firestore.Firestore;
//import com.google.firebase.FirebaseOptions;
import com.google.cloud.firestore.FirestoreOptions;
// import com.google.firebase.FirebaseApp;
//import com.google.firebase.*;

public class App extends Jooby {
  Firestore db = null;
  {
    assets("/**"); // need this! else /css & /js not found (404)
    // put an index page up:
    assets("/", "index.html");

    get("/test", () -> "Wow. It's alive!").produces("text/html");

    ws("/ws", ws -> {
      ws.onMessage(message -> System.out.println(" message: " + message.value()));
      System.out.println("Websockets available on /ws...");
    });

    get("/firebaseTest", () -> {
      updateFirebase(); 
      return "called updateFirebase()";
    });

    
 // Use the application default credentials
    
    String projectId = "the-hello-world-project-98dea";
    //GoogleCredentials credentials=GoogleCredentials.getApplicationDefault();

    //FirebaseOptions options =  new FirebaseOptions.Builder().setCredentials(credentials).setProjectId(projectId).build();

    //FirebaseApp.initializeApp(options);

    FirestoreOptions firestoreOptions =
        FirestoreOptions.getDefaultInstance().toBuilder()
            .setProjectId(projectId)
            .build();
     db = firestoreOptions.getService();
   // Firestore db = FirestoreOptions.getDefaultInstance().getService();
    //Firestore db = FirestoreClient.getFirestore();

  }

  
  public void updateFirebase() {
    DocumentReference docRef = db.collection("users").document("kthomas");
    // Add document data 
    Map data = new HashMap<>();
    data.put("first", "Kas");
    data.put("last", "Thomas");
    data.put("born", 1792);
    //asynchronously write data
    ApiFuture result = docRef.set(data);
    // ...
    // result.get() blocks on response
    // System.out.println("Update time : " + result.get().getUpdateTime());
  }
  

  public static void main(final String[] args) {
    run(App::new, args);
  }

}

And so, before you say "but your code uses Firestore, not Firebase," first of all, yes, technically you are right, I am using the (newer, document-oriented) Firestore Cloud database rather than the legacy JSON-oriented Firebase Realtime Database. (Read about the differences here.) The nomenclature is extraordinarily confusing. And it's not made any easier by the Java package names. Who would guess, for example, that FirestoreClient is in the com.google.firebase.cloud  package?

Be that as it may: Google says my Firestore database instance did indeed update, as desired, in the Firebase console at https://console.firebase.google.com -- which was the whole point of the exercise. I wanted the cloud database (the one you can inspect in the Firebase console) to update. And it did.

But it's no thanks to Google, whose online tutorial failed miserably to do the job and led to hours wasted troubleshooting was what turned out to be a pom.xml dependency issue, the magic missing piece being:

<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
<version>0.37.0-beta</version>
</dependency>

My advice to Google would be:
  • Tear down the online tutorials and start over.
  • Make it simpler.
  • Make it foolproof. 
  • Stop with the confusing names: Fire-this, Fire-that. Something Something Cloud. Fuck that crap. Get it together. Simplify the branding.
  • Eliminate boilerplate code (not so much a problem in the above code, but a problem in the non-working example code shown on Google's site). But please don't put me in @AnnotationHell. Please don't be Spring.
  • Start leveraging Java 8 lambda syntax.
  • Provide easy out-of-the-box integration with microframeworks like Jooby, Play, etc. 
Let me know when this FirefoobarCloud stuff is out of beta. IMHO, it's not fully baked.


Sunday, December 18, 2016

A large palindrome in Mycobacterium leprae

I have found a number of sizable palindromes in Mycobacterium leprae Br4923. I report two here.

The larger one is 74 bases long:

GGTGCTTGTTTTGCAATCTCGACCATTACCTGGCCTTAAGGCCAGGTAATGGTCGAGATTGCAAAACAAGCACC

This is a true palindrome (in that the reverse complement of the sequence exactly equals the sequence). It occurs precisely once (hence is not a CRISPR) in pseudogene MLBr01586, which appears to be an analog of M. tuberculosis soluble secreted antigen MPT53. The pseudogene in question, of length 489 bases, is flanked on one side by a pseudogene that appears to be an analog of a 23S rRNA methyltransferase, and on the other side by a pseudogene that is an analog of a putative "integral membrane protein."

MLBr01586 has a G+C content of 56.4%. The pseudogenes on either side of it have G+C contents of 59% and 60%.

The palindrome is long enough to fold back on itself to produce a hairpin-like secondary structure of substantial size. Its function, if any, is of course unknown.

A palindrome of length 60 can be found in pseudogene MLBr00038:

TACCTTGGTTAGGGCATAGCCGCTGTGCAGCTGCACAGCGGCTATGCCCTAACCAAGGTA

MLBr00038 has a G+C content of only 47% and appears to be an analog of Type VII secretion protein EccE from Mycobacterium malmoense (and others), which has a G+C content of 72%.

The rather advanced AT shift of the pseudogenes is consistent with runaway accumulation of random mutations. The existence of long, intact palindromes in the midst of such mutational mayhem is surprising, leading one to wonder whether secondary structure is preferentially conserved in pseudogenes, even as codons degrade. Indeed, maybe that's one reason why the pseudogenes continue to exist after an estimated 9 to 20 million years. Their secondary structures are needed.

Friday, June 17, 2016

Friday Water Cooler

The Media Is an Arm of the Ruling Class of This Country (huffingtonpost.com).You're being fed information from just a handful of companies. See illustration below.

Click to enrage.
Modern mussell shells are much thinner than 50 years ago (phys.org). Because of acidification.

Should We Sequence the DNA of Every Cancer Patient? (technologyreview.com). Boiling the ocean is seldom a good approach, and probably won't be good here either.

New Device Sold on the Dark Web Can Clone Up to 15 Contactless Cards per Second (news.softpedia.com/news). Imagine a crook with one of these just strolling through a crowded subway station, sniffing out card details...

Why Philosophers Should Care About Computational Complexity (scottaaronson.com).

Home Depot Files Antitrust Lawsuit Against Visa, MasterCard (ajc.com).

US Recession Odds Hit 55% According to Deutsche Bank Model (mishtalk.com).

That Time I Went To A Trump Rally (storify.com). Berntards, listen up!

44 Percent of Democrats Want Sanders to Make an Independent Run for the White House (defenddemocracy.press).

Chin Up (carlbeijer.com).

Buy my books (or at least click for more info):



Friday, June 10, 2016

Friday Water Cooler

The False Promise of DNA Testing (theatlantic.com). "One recent study asked participants to shake hands with a partner for two minutes and then hold a knife; when the DNA on the knives was analyzed, the partner was identified as a contributor in 85 percent of cases, and in 20 percent as the main or sole contributor."

Mystery girl in Calif. gets second funeral, 145 years after she was first buried (WaPo).

Scotland Bans Fracking, Forever (oilprice.com).

India Records Its Highest Temperature Ever (CNN).

Internet Boom Times Are Over (bloomberg.com). Internet growth is flat worldwide.

"Noncoding" RNA molecules create micropeptides (sott.net). Someone now needs to continue this work with pseudogenes (many of which, we know, are transcribed to RNA) and see how many are associated with biologically active oligopeptides. Many bacterial pathogens have abnormally large numbers of pseudogenes, which tend (paradoxically) to be highly conserved, evolutionarily. Most pseudogenes are millions of years old. Why? Maybe because they are making useful products.

I Had to Leave the U.S. to Stop Pretending to Be an Extrovert (alternet.org). An ad copywriter tells how his introverted personality, a liability in the U.S., freed him in Switzerland.

L.A. County Is Proposing To Tax Millionaires In Order To End Homelessness (laist.com).

California newspaper endorses Reagan for president (cnn.com).

Twenty Thousand Commit To Anti-Hillary ‘Occupy DNC’ Protest (dailycaller.com).

Trump Leads Clinton on Top-Ranking Economic Issues (gallup.com). Gallup poll finds people prefer Trump over Clinton 53% to 43% on economic matters.

8 Quick Thoughts on the Emmett Rensin Suspension (coreyrobin.com). Newsflash: You can get fired for writing things that piss off your boss! Funny how political writers (specialists in writing about power!) don't seem to understand this. Look: If you write about raw power (politics) for a living, how can you claim to be confused or outraged when a boss fires someone (an exercise of raw power) for rocking the boat? Do you not understand how power works? Yes. it's ugly, yes, it's wrong; we get that. But this is your domain of expertise, if you're a political writer. Act like you know what you're writing about.

Art world is 'hotbed' of corruption, collector claims (telegraph.co.uk). Delete the word "art." There, fixed it.

"Welfare to work" didn't work for these women (slate.com). Welcome to Clintonomics 101. Take a Dixie cup and sit yo broke ass down.

Meaningful work not created, only destroyed, by bosses, study finds (sciencedaily.com).

Worldwide Alcohol Consumption Went Down Last Year—Except in the United States (alternet.org). Cheers!

Thanks for visiting. Be sure to check out prior weeks' Water Coolers (see links at right).



Many, many thanks to the fine folks (below) who retweeted me recently on "the Twitter." Follow these tweeps. They retweet!



Buy my books (or at least click for more info):