Couchbase Lite on Java — Validating

    +

    Description — Couchbase Lite on Java — a framework for developing offline-first Java applications for mobile and edge
    Abstract — This content provides sample code and instructions that enable you to test your Couchbase Lite for java installation.
    Related Content — Install | Prerequisites | Test Build |

    Build a Getting Started App

    This section explains how to validate your configured build environment by building a starter app that uses many of Couchbase Lite on Java’s more common features.

    The GettingStarted app demonstrates how to use Couchbase Lite on Java. Console and Web App versions are available.

    • Console App

    • Web App

    • About the Starter App

    Ensure you added the Couchbase Lite dependency to your build.gradle file

    Create, build and run a new project using the following GettingStarted.java code:

    import com.couchbase.lite.*;
    
    import java.io.File;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Random;
    
    public class GettingStarted {
    
    private static final String DB_NAME = "getting-started";
    /*      Credentials declared this way purely for expediency in this demo - use OAUTH in production code */
    private static final String DB_USER = "sync_gateway";
    private static final String DB_PASS = "password"; (3)
    //    private static final String SYNC_GATEWAY_URL = "ws://127.0.0.1:4984/db" + DB_NAME;
    private static final String SYNC_GATEWAY_URL = "ws://127.0.0.1:4984/getting-started"; (1)
    private static final String DB_PATH = new File("").getAbsolutePath()+"/resources";
    
    
    public static void main (String [] args) throws CouchbaseLiteException, InterruptedException, URISyntaxException {
        Random RANDOM = new Random();
        int randPtrLang = RANDOM.nextInt(5) ;
        int randPtrType = RANDOM.nextInt(5) ;
        int numRows = 0;
    
        Double randVn = RANDOM.nextDouble() + 1;
    
        List<String> listLangs = new ArrayList<String> (Arrays.asList("Java","Swift","C#.Net","Objective-C","C++","Cobol"));
    //        List<String> listTypes = new ArrayList<String>();
        List<String> listTypes = new ArrayList<String> (Arrays.asList("SDK","API","Framework","Methodology","Language","IDE"));
    
        String Prop_Id ="id";
        String Prop_Language = "language";
        String Prop_Type = "type";
        String Prop_Version = "version";
        String searchStringType = "SDK";
        String dirPath = new File("").getAbsolutePath()+"/resources";
    
    
        // Initialize Couchbase Lite
        CouchbaseLite.init(); (2)
    
        // Get the database (and create it if it doesn’t exist).
        DatabaseConfiguration config = new DatabaseConfiguration();
        config.setDirectory(DB_PATH); (5)
        config.setEncryptionKey(new EncryptionKey(DB_PASS)); (3)
        Database database = new Database(DB_NAME, config);
    
        // Create a new document (i.e. a record) in the database.
        MutableDocument mutableDoc = new MutableDocument()
                .setDouble(Prop_Version, randVn)
                .setString(Prop_Type,listTypes.get(randPtrType));
    
        // Save it to the database.
        database.save(mutableDoc);
    
        // Update a document.
        mutableDoc = database.getDocument(mutableDoc.getId()).toMutable();
        mutableDoc.setString(Prop_Language, listLangs.get(randPtrLang));
        database.save(mutableDoc);
    
        Document document = database.getDocument(mutableDoc.getId());
        // Log the document ID (generated by the database) and properties
        System.out.println("Document ID is :: " + document.getId());
        System.out.println("Learning " + document.getString(Prop_Language));
    
        // Create a query to fetch documents of type SDK.
        System.out.println("== Executing Query 1");
        Query query = QueryBuilder.select(SelectResult.all())
                .from(DataSource.database(database))
                .where(Expression.property(Prop_Type).equalTo(Expression.string(searchStringType)));
        ResultSet result = query.execute();
        System.out.println(String.format("Query returned %d rows of type %s", result.allResults().size(), searchStringType));
    
        // Create a query to fetch all documents.
        System.out.println("== Executing Query 2");
        Query queryAll = QueryBuilder.select(SelectResult.expression(Meta.id),
                SelectResult.property(Prop_Language),
                SelectResult.property(Prop_Version),
                SelectResult.property(Prop_Type))
            .from(DataSource.database(database));
            try {
                for (Result thisDoc : queryAll.execute()) {
                  numRows++;
                  System.out.println(String.format("%d ... Id: %s is learning: %s version: %.2f type is %s",
                      numRows,
                      thisDoc.getString(Prop_Id),
                      thisDoc.getString(Prop_Language),
                      thisDoc.getDouble(Prop_Version),
                      thisDoc.getString(Prop_Type)));
                  }
            } catch (CouchbaseLiteException e) {
                e.printStackTrace();
            }
        System.out.println(String.format("Total rows returned by query = %d", numRows));
    
        Endpoint targetEndpoint = new URLEndpoint(new URI(SYNC_GATEWAY_URL));
        ReplicatorConfiguration replConfig = new ReplicatorConfiguration(database, targetEndpoint);
        replConfig.setReplicatorType(ReplicatorConfiguration.ReplicatorType.PUSH_AND_PULL);
    
        // Add authentication.
        replConfig.setAuthenticator(new BasicAuthenticator(DB_USER, DB_PASS));
    
        // Create replicator (be sure to hold a reference somewhere that will prevent the Replicator from being GCed)
        Replicator replicator = new Replicator(replConfig);
    
        // Listen to replicator change events.
        replicator.addChangeListener(change -> {
            if (change.getStatus().getError() != null) {
                System.err.println("Error code ::  " + change.getStatus().getError().getCode());
            }
        });
    
        // Start replication.
        replicator.start();
    
        // Check status of replication and wait till it is completed
        while (replicator.getStatus().getActivityLevel() != Replicator.ActivityLevel.STOPPED) {
            Thread.sleep(1000);
        }
    
        System.out.println("Finish!");
    
        System.exit(0); (4)
    }
    1 The app will start a replicator pointing to ws://localhost:4984/db, where db is the name of your Sync Gateway database
    2 This is the only API that differs from the Java (android) version. It accepts no parameters and should be called once only
    3 You can optionally AES-256 encrypt the database by providing a key
    4 This is needed for a tidy closedown of work executors
    5 It is advisable to set a specific directory path for the database.

    On running the app, you should see the document ID and property printed to the console together with a query result showing the number of rows in the database.

    This shows the document was successfully persisted to the database.

    See [About the Getting Started App] for more on the app itself

    This section explains how to set-up a build project to create Couchbase Lite on Java web apps using gradle and Intellij IDEA.

    Steps

    1. Create a new project folder and add a build.gradle file containing the following:

      
      ext{
        TOMCAT_VERSION="9.0.24"
      }
      
      apply plugin: 'java'
      apply plugin: 'war'
      apply plugin: 'com.bmuschko.tomcat'
      
      sourceCompatibility = 1.8
      
      buildscript {
          repositories {
              jcenter()
          }
          dependencies {
              classpath 'com.bmuschko:gradle-tomcat-plugin:2.5'
          }
      }
      
      repositories {
          mavenCentral()
      }
      
      dependencies {
          //  Use for Enterprise version
          compileOnly "com.couchbase.lite:couchbase-lite-java-ee:2.7.0"
          //  Use for community versions
          //    compileOnly "com.couchbase.lite:couchbase-lite-java:2.7.0"
          compileOnly "javax.servlet:javax.servlet-api:4.0.1"
          tomcat "org.apache.tomcat.embed:tomcat-embed-core:${TOMCAT_VERSION}",
              "org.apache.tomcat.embed:tomcat-embed-logging-juli:9.0.0.M6",
              "org.apache.tomcat.embed:tomcat-embed-jasper:${TOMCAT_VERSION}"
      }
      
      tomcat {
          httpPort = 8080
          httpProtocol = 'org.apache.coyote.http11.Http11Nio2Protocol'
          ajpProtocol  = 'org.apache.coyote.ajp.AjpNio2Protocol'
          contextPath = '/'
      }
      
      [tomcatRun, tomcatRunWar].each { task ->
          fileTree("libs").visit { FileVisitDetails details ->
              task.additionalRuntimeResources << file(details.file.path)
          }
      }
    2. Within Intellij IDEA, open the new project folder

      If you don’t have auto-import enabled, then accept the Import from Gradle prompt that appears at the bottom right of the screen.

    3. Create a Java class GettingStarted using this code:

      import com.couchbase.lite.*;
      
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      import java.util.Random;
      
      @WebServlet( value = "/GettingStarted")
      public class GettingStarted extends javax.servlet.http.HttpServlet {
          private static final String DB_DIR = "/usr/local/var/tomcat/data"; (1)
          private static final String DB_NAME = "getting-started";
          /*      Credentials declared this way purely for expediency in this demo - use OAUTH in production code */
          private static final String DB_USER = "sync_gateway";
          private static final String DB_PASS = "password";
          //    private static final String SYNC_GATEWAY_URL = "ws://127.0.0.1:4984/db" + DB_NAME;
          private static final String SYNC_GATEWAY_URL = "ws://127.0.0.1:4984/getting-started"; (2)
          private static final String NEWLINETAG = "<br />";
          private String MYRESULTS;
          private int NUMROWS;
          private Random RANDOM = new Random();
      
          protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse
                  response) throws javax.servlet.ServletException, IOException {
              outputMessage("Servlet started :: doGet Invoked");
              doPost(request, response);
          }
      
          protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse
                  response) throws javax.servlet.ServletException, IOException {
              NUMROWS = 0;
              MYRESULTS = "";
              outputMessage("Servlet started :: doPost Invoked");
              String url = "/showDbItems.jsp";
              try {
                  MYRESULTS = testCouchbaseLite();
              } catch (CouchbaseLiteException | URISyntaxException | InterruptedException e) {
                  e.printStackTrace();
              } finally {
                  outputMessage(String.format("CouchbaseLite Test Ended :: There are %d rows in DB", NUMROWS));
              }
              request.setAttribute("myRowCount", NUMROWS);
              request.setAttribute("myResults", MYRESULTS);
              getServletContext()
                      .getRequestDispatcher(url)
                      .forward(request, response);
              outputMessage("Servlet Ended :: doPost Exits");
          }
      
          public String testCouchbaseLite() throws CouchbaseLiteException, URISyntaxException, InterruptedException, ServletException {
      
              int randPtr = RANDOM.nextInt(5) + 1;
              long syncTotal = 0;
              Double randVn = RANDOM.nextDouble() + 1;
              List<String> listLangs = new ArrayList<String>(Arrays.asList("Java", "Swift", "C#.Net", "Objective-C", "C++", "Cobol"));
              List<String> listTypes = new ArrayList<String>(Arrays.asList("SDK", "API", "Framework", "Methodology", "Language", "IDE"));
      
              String Prop_Id = "id";
              String Prop_Language = "language";
              String Prop_Type = "type";
              String Prop_Version = "version";
              String searchStringType = "SDK";
      
              // Get and configure database
              // Note initialisation of CouchbaseLite is done in ServletContextListener
              outputMessage("== Opening DB and doing initial sync");
              Database database = DatabaseManager.manager().getDatabase(DB_NAME,DB_DIR,DB_PASS);
      
              // Initial DB sync prior to local updates
              syncTotal = DatabaseManager.manager().runOneShotReplication(database, SYNC_GATEWAY_URL, DB_USER, DB_PASS);
              outputMessage(String.format("Inital number of rows synchronised = %d", syncTotal));
      
              // Create a new document (i.e. a record) in the database.
              outputMessage("== Adding a record");
              MutableDocument mutableDoc = new MutableDocument()
                      .setDouble(Prop_Version, randVn)
                      .setString(Prop_Type, listTypes.get(RANDOM.nextInt(listTypes.size() - 1)));
      
              // Save it to the database.
              try {
                  database.save(mutableDoc);
              } catch (CouchbaseLiteException e) {
                  throw new ServletException("Error saving a document", e);
              }
      
              // Update a document.
              outputMessage("== Updating added record");
              mutableDoc = database.getDocument(mutableDoc.getId()).toMutable();
              mutableDoc.setString(Prop_Language, listLangs.get(RANDOM.nextInt(listLangs.size() - 1)));
              // Save it to the database.
              try {
                  database.save(mutableDoc);
              } catch (CouchbaseLiteException e) {
                  throw new ServletException("Error saving a document", e);
              }
      
              outputMessage("== Retrieving record by id");
              Document newDoc = database.getDocument(mutableDoc.getId());
              // Show the document ID (generated by the database) and properties
              outputMessage("Document ID :: " + newDoc.getId());
              outputMessage("Learning " + newDoc.getString(Prop_Language));
      
              // Create a query to fetch documents of type SDK.
              outputMessage("== Executing Query 1");
              Query query = QueryBuilder.select(SelectResult.all())
                      .from(DataSource.database(database))
                      .where(Expression.property(Prop_Type).equalTo(Expression.string(searchStringType)));
              try{
                  ResultSet result = query.execute();
                  outputMessage(String.format("Query returned %d rows of type %s", result.allResults().size(), searchStringType));
              } catch (CouchbaseLiteException e) {
                  e.printStackTrace();
              }
      
              // Create a query to fetch all documents.
              outputMessage("== Executing Query 2");
              Query queryAll = QueryBuilder.select(SelectResult.expression(Meta.id),
                      SelectResult.property(Prop_Language),
                      SelectResult.property(Prop_Version),
                      SelectResult.property(Prop_Type))
                      .from(DataSource.database(database));
              try {
                  for (Result thisDoc : queryAll.execute()) {
                    NUMROWS++;
                      outputMessage(String.format("%d ... Id: %s is learning: %s version: %.2f type is %s",
                        NUMROWS,
                        thisDoc.getString(Prop_Id),
                        thisDoc.getString(Prop_Language),
                        thisDoc.getDouble(Prop_Version),
                        thisDoc.getString(Prop_Type)));
                    }
                  } catch (CouchbaseLiteException e) {
                  e.printStackTrace();
              }
              outputMessage(String.format("Total rows returned by query = %d", NUMROWS));
      
      //      Do final single-shot replication to incorporate changed NumRows
              outputMessage("== Doing final single-shot sync");
              syncTotal = DatabaseManager.manager().runOneShotReplication(database, SYNC_GATEWAY_URL, DB_USER, DB_PASS);
              outputMessage(String.format("Total rows synchronised = %d", syncTotal));
              database.close();
              return MYRESULTS;
          }
      
          public void outputMessage(String msg) {
              String thisMsg = "Null message";
              if (msg.length() > 0) {
                  thisMsg = msg;
              }
              System.out.println(msg);
              MYRESULTS = MYRESULTS + msg + NEWLINETAG;
          }
      }
      1 It is advisable to set a specific directory path for the database.
      2 The app will start a replicator pointing to ws://localhost:4984/db, where db is the name of your Sync Gateway database
    4. Create a Java class Listener using this code:

      import javax.servlet.ServletContextEvent;
      import javax.servlet.ServletContextListener;
      import javax.servlet.annotation.WebListener;
      
      @WebListener
      public class Application implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent event) {
            DatabaseManager.manager().init();
        }
      }
    5. Create a Java class Database Manager using this code:

      import com.couchbase.lite.*;
      import java.net.URI;
      import java.net.URISyntaxException;
      
      public class DatabaseManager {
      
          private static DatabaseManager instance;
          private Database database;
          public static synchronized DatabaseManager manager() {
              if (instance == null) {
                  instance = new DatabaseManager();
              }
              return instance;
          }
          public synchronized void init() {
              CouchbaseLite.init(); (1)
          }
          public synchronized Database getDatabase(String parDbname, String parDbDir,  String parDbPass) {
              if (database == null) {
                  try {
                      DatabaseConfiguration config = new DatabaseConfiguration();
                      config.setDirectory(parDbDir); (2)
                      config.setEncryptionKey(new EncryptionKey(parDb_PASS)); (3)
                      database = new Database(parDbname, config);
                  }
                  catch (CouchbaseLiteException e) {
                      throw new IllegalStateException("Cannot create database", e);
                  }
              }
              return database;
          }
      
          public synchronized long runOneShotReplication( Database parDb, String parURL, String parName, String parPassword) throws InterruptedException {
              long syncTotal = 0;
              // Set replicator endpoint
              URI sgURI = null;
              try {
                  sgURI = new URI(parURL);
              } catch (URISyntaxException e) {
                  e.printStackTrace();
              }
              URLEndpoint targetEndpoint = new URLEndpoint(sgURI);
      
              // Configure replication
              System.out.println("== Synchronising DB :: Configuring replicator");
              ReplicatorConfiguration replConfig = new ReplicatorConfiguration(parDb, targetEndpoint);
      
              replConfig.setReplicatorType(ReplicatorConfiguration.ReplicatorType.PUSH_AND_PULL);
              replConfig.setContinuous(false);    // make this a single-shot replication cf. a continuous replication
      
              // Add authentication.
      //        outputMessage("== Synchronising DB :: Setting authenticator");
              replConfig.setAuthenticator(new BasicAuthenticator(parName, parPassword));
      
              // Create replicator (be sure to hold a reference somewhere that will prevent the Replicator from being GCed)
      //        outputMessage("== Synchronising DB :: Creating replicator");
              Replicator replicator = new Replicator(replConfig);
      
              // Listen to replicator change events.
      //        System.out.println("== Synchronising DB :: Adding listener");
              replicator.addChangeListener(change -> {
                  if (change.getStatus().getError() != null) {
                      System.err.println("Error code ::  " + change.getStatus().getError().getCode());
                  }
              });
      
              // Start replication.
      //        outputMessage("== Synchronising DB :: Starting");
              replicator.start();
              // Check status of replication and wait till it is completed
              while ((replicator.getStatus().getActivityLevel() != Replicator.ActivityLevel.STOPPED) && (replicator.getStatus().getActivityLevel() != Replicator.ActivityLevel.IDLE)) {
                  Thread.sleep(1000);
              }
      
              syncTotal = replicator.getStatus().getProgress().getTotal();
              replicator.stop();
      //        outputMessage("== Synchronising DB :: Completed ");
              return replicator.getStatus().getProgress().getTotal();
      
              }
      }
      1 This is the only API that differs from the Java (android) version. It accepts no parameters and should be called once only
      2 It is advisable to set a specific directory path for the database.
      3 You can optionally AES-256 encrypt the database by providing a key
    6. Create an index.html file in src/main/web app with the following content:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Couchbase Lite (Java Jvm) :: Getting Started App</title>
      </head>
      <body>
      <h1>Couchbase Lite (Java Jvm) :: Getting Started App</h1>
      
      <p>To invoke the GettingStarted servlet click <a href="GettingStarted">here</a></p>
      
      </body>
      </html>
    7. Create a showDbItems.jsp file in src/main/web app with the following content:

      <html>
      <head>
          <title>Couchbase Lite (Java Jvm) :: Getting Started App</title>
      </head>
      <body>
      <h1>Couchbase Lite (Java Jvm) :: Getting Started App</h1>
      <h2>List all current DB rows</h2>
      <hr>
      <p>NumRows = ${myRowCount}</p>
      <hr>
      ${myResults}
      <hr>
      </body>
      </html>
    8. Build, deploy and run the app using tomcatRun

      GradleMenuWebApp
      1. Point your browser to: localhost:8080/gettingstarted

        This opens the browser at your index.html page.

      2. Select the here link

        This launches the servlet and displays the results in showdDbItems.jsp. They are also added to the catalina.out log file.

    The GettingStarted app will:

    • Create a database

      The app creates its database in the /getting-started.cblite2 directory relative to its root location when run (See: Finding a Database File).

    Explicitly state your required database location when creating your database (see: Finding a Database File for how to do this)
    • Add content to the DB

    • Run a simple query counting the DB rows

    • Start a one-shot, bi-directional replication using Sync Gateway and Couchbase Server

    • Produce a simple report on the db Contents

      cblOutput
    Before starting your app ensure you have started both your Couchbase Server and Sync Gateway instances.