Android Native – sync MediaPlayer progress to Seekbar

Introduction

MediaPlayer (android.media.MediaPlayer) is a popular way to play media files, and combining it with a SeekBar can greatly improve the user experience. In this tutorial, we will learn how to synchronize a MediaPlayer progress to a SeekBar position.

Goals

At the end of the tutorial, you would have learned:

  1. How to sync an active MediaPlayer to a SeekBar.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Intermediate Android.
  2. ActivityResult APIs.
  3. Storage Access Framework (SAF).
  4. Coroutines.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Give the default Hello World! TextView android:id of textView_time.

  3. Completely remove the android:text attribute from textView_time.

  4. Add the tools:text attribute to textView_time with the value of 0:00.

  5. Add the android:textSize attribute with the value of 32sp.

  6. Constraint textView_time to the top, start, and end of ConstraintLayout, but leave the bottom side unconstrained.

  7. Your textView_time should look like the code below.

     <TextView
        android:id="@+id/textView_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="32sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="0.00" />
  8. Download the .mp3 file called Unexpected Gifts of Spring by Alextree from the FreeMusicArchive. This song is licensed under CC BY 4.0. You are recommended to download the file from the AVDs built-in web browser. If you have downloaded the file to your development machine, you can also drag and drop the file into the AVDs via the Device File Explorer, which will trigger a download action on the AVD (this behavior has only been tested on a Windows machine, I am not sure if this works on a Mac/Linux). Getting the files using these methods will automatically add an entry into the MediaStore.

  9. Add the LifecycleScope KTX extension to your module build.gradle file.

     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
Add the SeekBar

SeekBar is a built-in Android View, which extends ProgressBar. It contains a draggable circle that end users can drag to specific points on a timeline.

progress.jpg

Perform the steps below to add a SeekBar into the project.

  1. Open activity_main.xml in the Code view.

  2. Copy and paste the code below into activity_main.xml.

     <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:layout_marginHorizontal="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textView_time" />
  3. Alternatively, if you prefer to configure your own SeekBar from scratch, it can also be found at Palette > Widgets, in the Design view.

  4. Now, add these attributes below into textView_time to finish creating a vertical chain for the two Views.

     app:layout_constraintBottom_toTopOf="@id/seekBar"
     app:layout_constraintHorizontal_bias="0.5"
  5. We will need to reference these Views later, so append these lines of code to MainActivity#onCreate().

     //Gets the textView_time reference
     val timeView = findViewById<TextView>(R.id.textView_time)
    
     //Gets the seekBar reference
     val seekBar = findViewById<SeekBar>(R.id.seekBar)
Open an mp3 File

The next thing that we need in our project is a way to open the mp3 file that we downloaded earlier. We will use SAF/ActivityResult APIs for this.

  1. Append the code below to MainActivity#onCreate().

     //Launcher to open file with a huge callback. Organize in real code.
     val openMusicLauncher = registerForActivityResult(OpenDocument()){ uri ->
    
     }
    
     val mimeTypes = arrayOf("audio/mpeg")
     openMusicLauncher.launch(mimeTypes)
  2. The code snippet above will start a Content Picker UI for the end user to pick a file matching the specified mime type. The chosen mime type of audio/mpeg will match .mp3 files, according to this Common MIME Types list.

Add the MediaPlayer

Now, we need to add a MediaPlayer object into our App for playing music files. To add MediaPlayer into our code, follow the steps below:

  1. It is recommended to call MediaPlayer#release() to release resources when you are done with it, so we will add a reference to the MediaPlayer as a class property, for easy access in multiple callbacks such as onPause(), onStop(), onDestroy().

     //Keeps a reference here to make it easy to release later
     private var mediaPlayer: MediaPlayer? = null
  2. Override MainActivity#onStop() to release and null out mediaPlayer.

     override fun onStop() {
        super.onStop()
        mediaPlayer?.release()
        mediaPlayer = null
     }
  3. Inside the openMusicLauncher callback, instantiate a MediaPlayer using the factory function MediaPlayer#create() and assign it to mediaPlayer.

     mediaPlayer = MediaPlayer.create(applicationContext, uri)
  4. Since mediaPlayer is nullable, we will chain the newly created MediaPlayer with an also {} scope function block to skip multiple null checks in the next few steps.

     mediaPlayer = MediaPlayer.create(applicationContext, uri)
        .also { //also {} scope function skips multiple null checks
    
        }
Synchronize SeekBar (and TextView) to MediaPlayer progress

The SeekBar that the end user sees on the screen must scale relatively with the duration of the media file.

  1. So we will have to set the SeekBar max value corresponding to the file duration. Inside the also {} block, add the code below.

     seekBar.max = it.duration
  2. Now, start() the MediaPlayer.

     it.start()
  3. Launch a coroutine running the Main thread with the code below.

     //Should be safe to use this coroutine to access MediaPlayer (not thread-safe)
     //because it uses MainCoroutineDispatcher by default
     lifecycleScope.launch {
        }
        //Can also release mediaPlayer here, if not looping.
     }
  4. Inside of launch {}, add a while() loop conditioned to the Boolean MediaPlayer.isPlaying.

     //Should be safe to use this coroutine to access MediaPlayer (not thread-safe)
     //because it uses MainCoroutineDispatcher by default
     lifecycleScope.launch {
        while (it.isPlaying){
        }
        //Can also release mediaPlayer here, if not looping.
     }
  5. Inside of this while() loop, we can synchronize SeekBar#progress with MediaPlayer.currentPosition.

     lifecycleScope.launch {
        while (it.isPlaying){
            seekBar.progress = it.currentPosition
        }
        //Can also release mediaPlayer here, if not looping.
     }
  6. We can also synchronize TextView#text with MediaPlayer.currentPosition. The milliseconds extension property of Int is used here for convenience because its default toString() form is quite readable (you will see later). Time-formatting is not the focus of this tutorial.

     //Should be safe to use this coroutine to access MediaPlayer (not thread-safe)
     //because it uses MainCoroutineDispatcher by default
     lifecycleScope.launch {
        while (it.isPlaying){
            seekBar.progress = it.currentPosition
            timeView.text = "${it.currentPosition.milliseconds}"
        }
        //Can also release mediaPlayer here, if not looping.
     }
  7. Finally, add a delay() to the coroutine. This ensures that the seekBar will only update every one second.

     lifecycleScope.launch {
        while (it.isPlaying){
            seekBar.progress = it.currentPosition
            timeView.text = "${it.currentPosition.milliseconds}"
            delay(1000)
        }
        //Can also release mediaPlayer here, if not looping.
     }
Synchronize MediaPlayer progress (and TextView) to SeekBar position

The code that we have so far will only update the SeekBar position and TextView content to the MediaPlayer progress. We will have to add a SeekBar.OnSeekBarChangeListener object to the SeekBar to monitor for changes. Follow the steps below to complete the tutorial.

  1. Append the object below to the openMusicLauncher callback. We have only implemented onProgressChanged because that is all we need for now. We also used the function MediaPlayer#seekTo() to seek a specified time position.

     //Move this object somewhere else in real code
     val seekBarListener = object : SeekBar.OnSeekBarChangeListener {
        override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
            if (fromUser){
                //sets the playing file progress to the same seekbar progressive, in relative scale
                mediaPlayer?.seekTo(progress)
    
                //Also updates the textView because the coroutine only runs every 1 second
                timeView.text = "${progress.milliseconds}"
            }
        }
        override fun onStartTrackingTouch(seekBar: SeekBar?) {}
        override fun onStopTrackingTouch(seekBar: SeekBar?) {}
     }
  2. Now, assign seekBarLisenter to seekBar.

     seekBar.setOnSeekBarChangeListener(seekBarListener)
Run the App

We are now ready to launch the App. Your App should behave similarly to the Gif below. You can ignore the other files in my AVDs Downloads directory that are not part of the project setup.

AudioLoop.gif

Solution Code

build.gradle

plugins {
   id 'com.android.application'
   id 'org.jetbrains.kotlin.android'
}

android {
   compileSdk 31

   defaultConfig {
       applicationId "com.codelab.daniwebandroidaudioseekbarsync"
       minSdk 21
       targetSdk 31
       versionCode 1
       versionName "1.0"

       testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }

   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
   compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }
   kotlinOptions {
       jvmTarget = '1.8'
   }
}

dependencies {
   implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
   implementation 'androidx.core:core-ktx:1.7.0'
   implementation 'androidx.appcompat:appcompat:1.4.1'
   implementation 'com.google.android.material:material:1.5.0'
   implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
   testImplementation 'junit:junit:4.13.2'
   androidTestImplementation 'androidx.test.ext:junit:1.1.3'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <TextView
       android:id="@+id/textView_time"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textSize="32sp"
       app:layout_constraintBottom_toTopOf="@id/seekBar"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintHorizontal_bias="0.5"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       tools:text="0.00" />

   <SeekBar
       android:id="@+id/seekBar"
       android:layout_width="match_parent"
       android:layout_height="64dp"
       android:layout_marginHorizontal="16dp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintHorizontal_bias="0.5"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/textView_time" />
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.codelab.daniwebandroidaudioseekbarsync

import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.SeekBar
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts.OpenDocument
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.milliseconds

class MainActivity : AppCompatActivity() {

   //Keeps a reference here to make it easy to release later
   private var mediaPlayer: MediaPlayer? = null

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       //Gets the textView_time reference
       val timeView = findViewById<TextView>(R.id.textView_time)

       //Gets the seekBar reference
       val seekBar = findViewById<SeekBar>(R.id.seekBar)

       //Launcher to open file with a huge callback. Organize in real code.
       val openMusicLauncher = registerForActivityResult(OpenDocument()){ uri ->
           //Instantiates a MediaPlayer here now that we have the Uri.
           mediaPlayer = MediaPlayer.create(applicationContext, uri)
               .also { //also {} scope function skips multiple null checks
                   seekBar.max = it.duration
                   it.start()

                   //Should be safe to use this coroutine to access MediaPlayer (not thread-safe)
                   //because it uses MainCoroutineDispatcher by default
                   lifecycleScope.launch {
                       while (it.isPlaying){
                           seekBar.progress = it.currentPosition
                           timeView.text = "${it.currentPosition.milliseconds}"
                           delay(1000)
                       }
                       //Can also release mediaPlayer here, if not looping.
                   }
               }

           //Move this object somewhere else in real code
           val seekBarListener = object : SeekBar.OnSeekBarChangeListener {
               override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                   if (fromUser){
                       //sets the playing file progress to the same seekbar progressive, in relative scale
                       mediaPlayer?.seekTo(progress)

                       //Also updates the textView because the coroutine only runs every 1 second
                       timeView.text = "${progress.milliseconds}"
                   }
               }
               override fun onStartTrackingTouch(seekBar: SeekBar?) {}
               override fun onStopTrackingTouch(seekBar: SeekBar?) {}
           }

           seekBar.setOnSeekBarChangeListener(seekBarListener)
       }

       val mimeTypes = arrayOf("audio/mpeg")
       openMusicLauncher.launch(mimeTypes)
   }

   override fun onStop() {
       super.onStop()
       mediaPlayer?.release()
       mediaPlayer = null
   }
}
Summary

Congrations, you have learned how to sync a MediaPlayer and a SeekBar. The full project code can be found at https://github.com/dmitrilc/DaniwebAndroidAudioSeekbarSync.

CSS help – inline newsletter signup form on squarespace

Screen_Shot_2022-02-18_at_19_31_27.jpg

I'm trying to make a form all in one line instead of over multiple lines/height.

It can be seen on https://grapefruit-crocodile-hdl3.squarespace.com/ (pwd: contagency)

It's supposed to look the attached image, any pointers appreciated.

I've got this far, so far:

form {
  display: flex;
}

.form-item.field.email, .form-item.field.select, .form-button-wrapper.field.submit {
 display: inline-block;
 min-width: 30.8%;
}

.form-wrapper {
opacity: 0.6;
background-color: #000000;
  width: 100%;
}

.form-wrapper .field-list .field  {
  background-color: #ccc;
  border: 1px solid #000000;

}

The Ultimate Guide to Legal and Ethical Web Scraping in 2022

The popularity of web scraping is growing at such an accelerated pace these days. Nowadays, not everyone has technical knowledge of web scraping and they use APIs like news API to fetch news, blog APIs to fetch blog-related data, etc.

As web scraping is growing, it would be almost impossible not to get cross answers when the big question arises: is it legal?

Data Lives Longer Than Any Environment, Data Management Needs to Extend Beyond the Environment

Komprise enables enterprises to analyze, mobilize and monetize file and object data across clouds, data centers, and the edge. The solution constantly monitors key business services, identifies changes in usage patterns, and automatically captures new insights. Komprise also simplifies access to all enterprise data helping companies make better decisions faster while driving increased revenue from existing infrastructure

The 41st IT Press Tour had the opportunity to meet with Kumar Goswami, co-founder and CEO, Darren Cunningham, VP of Marketing, Ben Conneely, VP EMEA Sales, Krishna Subramanian, Co-Founder and COO of Komprise.

The Intelligent Storage Revolution

Intelligent computing and intelligent storage are the future. Intelligent computing is the ability to automatically adjust computing resources based on data and application needs, such as the acceleration of an analytics workload. 

Intelligent storage ensures that applications can always access their data by transparently managing multiple disk tiers. 

How to Use Postman to Build a Mock Server That Mimics the Marqeta Core API?

The Marqeta Core API lets you build web applications for custom payment card programs with the Marqeta platform. While SDKs for the Core API are available for Python and Ruby, you can always use the developer sandbox and the Core API Explorer to work with the API, regardless of the language you use to develop your application.

For faster prototype development, however, you might find it useful to build an API mock server. In this post, we'll work through how to use Postman to build a mock server that mimics the Marqeta Core API. We'll demonstrate how to create a new collection in Postman, save responses from live requests to the sandbox, and then create a mock server and send our test requests.

How to Recover Permanently Deleted Files and Folders in Google Drive

When you delete any file or folder in your Google Drive, it is moved to the trash folder. The deleted file stays in trash for 30 days and then it is permanently deleted. You can right-click the deleted file in the trash bin and select Restore to undelete the file.

Restore Deleted Files in Google Drive

Restore Permanently Deleted Files

If you fail to restore a deleted file within the 30-day window, or if you have emptied the trash manually, the files will be permanently deleted from your Google Drive.

You can however contact Google Support and they may restore the deleted files for you.

  1. Go to support.google.com/drive?p=file_recovery and sign-in with the Google account you used to delete the file.

  2. Provide your first name, last name, and check the consent box to confirm that you are trying to recover files you deleted from Google Drive.

Request Undelete Files

You’ll receive an email from drive-noreply@google.com confirming that your request has been received and that it may take up to 48 hours for the files to be restored. (It usually takes less than 12 hours.)

Google recommends that you avoid emptying your trash while the file recovery process is ongoing. Also, please note that files that you have yourself uploaded to Google Drive may be recovered in this process. If you are not the owner of a file, it cannot be restored through this process.

Once the restoration process is complete, Google Support will send you another email confirming that the files have been restored. You can now open your Google Drive and the permanently deleted files and folders will be visible in their original location.

Files Restored in Google Drive

The above steps are only valid for individual Google accounts. If you have a Google Workspace account, you would need to get in touch with your domain administrator to restore files. Also, the restoration process will restore all deleted files, it is currently possible to restore individual files or folders.

23 Cool Custom Binder Designs

These funky, fresh binder designs will guide you to the middle ground of utility and uniqueness. Binders aren’t just for homework anymore. They are used for a myriad of purposes, which include portfolios, business proposals, and marketing materials. And like those school day binders, they weren’t cool unless the design showed it. It’s now time...

The post 23 Cool Custom Binder Designs appeared first on DesignrFix.

How To Ensure Data Transparency and Why It’s Important

Data transparency is more important than ever before, with more people going online amid surging cyberthreats. Consumers are becoming more aware and critical of how software and websites use their data. More than a few companies have gotten bad publicity due to a lack of transparency about user data. 

How can developers ensure they are providing good data transparency? A few key tactics will help.

Comparing Node JavaScript to JavaScript in the Browser

Being able to understand Node continues to be an important skill if you’re a front-end developer. Deno has arrived as another way to run JavaScript outside the browser, but the huge ecosystem of tools and software built with Node mean it’s not going anywhere anytime soon.

If you’ve mainly written JavaScript that runs in the browser and you’re looking to get more of an understanding of the server side, many articles will tell you that Node JavaScript is a great way to write server-side code and capitalize on your JavaScript experience.

I agree, but there are a lot of challenges jumping into Node.js, even if you’re experienced at authoring client-side JavaScript. This article assumes you’ve got Node installed, and you’ve used it to build front-end apps, but want to write your own APIs and tools using Node.

For a beginners explanation of Node and npm you can check out Jamie Corkhill’s “Getting Started With Node” on Smashing Magazine.

Asynchronous JavaScript

We don’t need to write a whole lot of asynchronous code on the browser. The most common usage of asynchronous code on the browser is fetching data from an API using fetch (or XMLHttpRequest if you’re old-school). Other uses of async code might include using setInterval, setTimeout, or responding to user input events, but we can get pretty far writing JavaScript UI without being asynchronous JavaScript geniuses.

If you’re using Node, you will nearly always be writing asynchronous code. From the beginning, Node has been built to leverage a single-threaded event loop using asynchronous callbacks. The Node team blogged in 2011 about how “Node.js promotes an asynchronous coding style from the ground up.” In Ryan Dahl’s talk announcing Node.js in 2009, he talks about the performance benefits of doubling down on asynchronous JavaScript.

The asynchronous-first style is part of the reason Node gained popularity over other attempts at server-side JavaScript implementations such as Netscape’s application servers or Narwhal. However, being forced to write asynchronous JavaScript might cause friction if you aren’t ready for it.

Setting up an example

Let’s say we’re writing a quiz app. We’re going to allow users to build quizes out of multichoice questions to test their friends’ knowledge. You can find a more complete version of what we’ll build at this GitHub repo. You could also clone the entire front-end and back-end to see how it all fits together, or you can take a look at this CodeSandbox (run npm run start to fire it up) and get an idea of what we’re making from there.

Screenshot of a quiz editor written in Node JavaScript that contains four inputs two checkboxes and four buttons.

The quizzes in our app will consist of a bunch of questions, and each of these questions will have a number of answers to choose from, with only one answer being correct.

We can hold this data in an SQLite database. Our database will contain:

  • A table for quizzes with two columns:
    • an integer ID
    • a text title
  • A table for questions with three columns:
    • an integer ID
    • body text
    • An integer reference matching the ID of the quiz each question belongs to
  • A table for answers with four columns:
    • an integer ID
    • body text
    • whether the answer is correct or not
    • an integer reference matching the ID of the question each answer belongs to

SQLite doesn’t have a boolean data type, so we can hold whether an answer is correct in an integer where 0 is false and 1 is true.

First, we’ll need to initialize npm and install the sqlite3 npm package from the command line:

npm init -y
npm install sqlite3

This will create a package.json file. Let’s edit it and add:

"type":"module"

To the top-level JSON object. This will allow us to use modern ES6 module syntax. Now we can create a node script to set up our tables. Let’s call our script migrate.js.

// migrate.js

import sqlite3 from "sqlite3"; 

let db = new sqlite3.Database("quiz.db");
    db.serialize(function () {
      // Setting up our tables:
      db.run("CREATE TABLE quiz (quizid INTEGER PRIMARY KEY, title TEXT)");
      db.run("CREATE TABLE question (questionid INTEGER PRIMARY KEY, body TEXT, questionquiz INTEGER, FOREIGN KEY(questionquiz) REFERENCES quiz(quizid))");
      db.run("CREATE TABLE answer (answerid INTEGER PRIMARY KEY, body TEXT, iscorrect INTEGER, answerquestion INTEGER, FOREIGN KEY(answerquestion) REFERENCES question(questionid))");
      // Create a quiz with an id of 0 and a title "my quiz" 
      db.run("INSERT INTO quiz VALUES(0,\"my quiz\")");
      // Create a question with an id of 0, a question body
      // and a link to the quiz using the id 0
      db.run("INSERT INTO question VALUES(0,\"What is the capital of France?\", 0)");
      // Create four answers with unique ids, answer bodies, an integer for whether
      // they're correct or not, and a link to the first question using the id 0
      db.run("INSERT INTO answer VALUES(0,\"Madrid\",0, 0)");
      db.run("INSERT INTO answer VALUES(1,\"Paris\",1, 0)");
      db.run("INSERT INTO answer VALUES(2,\"London\",0, 0)");
      db.run("INSERT INTO answer VALUES(3,\"Amsterdam\",0, 0)");
  });
db.close();

I’m not going to explain this code in detail, but it creates the tables we need to hold our data. It will also create a quiz, a question, and four answers, and store all of this in a file called quiz.db. After saving this file, we can run our script from the command line using this command:

node migrate.js

If you like, you can open the database file using a tool like DB Browser for SQLite to double check that the data has been created.

Changing the way you write JavaScript

Let’s write some code to query the data we’ve created.

Create a new file and call it index.js .To access our database, we can import sqlite3, create a new sqlite3.Database, and pass the database file path as an argument. On this database object, we can call the get function, passing in an SQL string to select our quiz and a callback that will log the result:

// index.js
import sqlite3 from "sqlite3";

let db = new sqlite3.Database("quiz.db");

db.get(`SELECT * FROM quiz WHERE quizid  = 0`, (err, row) => {
  if (err) {
    console.error(err.message);
  }
  console.log(row);
  db.close();
});

Running this should print { quizid: 0, title: 'my quiz' } in the console.

How not to use callbacks

Now let’s wrap this code in a function where we can pass the ID in as an argument; we want to access any quiz by its ID. This function will return the database row object we get from db.

Here’s where we start running into trouble. We can’t simply return the object inside of the callback we pass to db and walk away. This won’t change what our outer function returns. Instead, you might think we can create a variable (let’s call it result) in the outer function and reassign this variable in the callback. Here is how we might attempt this:

// index.js
// Be warned! This code contains BUGS
import sqlite3 from "sqlite3";

function getQuiz(id) {
  let db = new sqlite3.Database("quiz.db");
  let result;
  db.get(`SELECT * FROM quiz WHERE quizid  = ?`, [id], (err, row) => {
    if (err) {
      return console.error(err.message);
    }
    db.close();
    result = row;
  });
  return result;
}
console.log(getQuiz(0));

If you run this code, the console log will print out undefined! What happened?

We’ve run into a disconnect between how we expect JavaScript to run (top to bottom), and how asynchronous callbacks run. The getQuiz function in the above example runs like this:

  1. We declare the result variable with let result;. We haven’t assigned anything to this variable so its value is undefined.
  2. We call the db.get() function. We pass it an SQL string, the ID, and a callback. But our callback won’t run yet! Instead, the SQLite package starts a task in the background to read from the quiz.db file. Reading from the file system takes a relatively long time, so this API lets our user code move to the next line while Node.js reads from the disk in the background.
  3. Our function returns result. As our callback hasn’t run yet, result still holds a value of undefined.
  4. SQLite finishes reading from the file system and runs the callback we passed, closing the database and assigning the row to the result variable. Assigning this variable makes no difference as the function has already returned its result.

Passing in callbacks

How do we fix this? Before 2015, the way to fix this would be to use callbacks. Instead of only passing the quiz ID to our function, we pass the quiz ID and a callback which will receive the row object as an argument.

Here’s how this looks:

// index.js
import sqlite3 from "sqlite3";
function getQuiz(id, callback) {
  let db = new sqlite3.Database("quiz.db");
  db.get(`SELECT * FROM quiz WHERE quizid  = ?`, [id], (err, row) => {
    if (err) {
       console.error(err.message);
    }
    else {
       callback(row);
    }
    db.close();
  });
}
getQuiz(0,(quiz)=>{
  console.log(quiz);
});

That does it. It’s a subtle difference, and one that forces you to change the way your user code looks, but it means now our console.log runs after the query is complete.

Callback hell

But what if we need to do multiple consecutive asynchronous calls? For instance, what if we were trying to find out which quiz an answer belonged to, and we only had the ID of the answer.

First, I’m going to refactor getQuiz to a more general get function, so we can pass in the table and column to query, as well as the ID:

Unfortunately, we are unable to use the (more secure) SQL parameters for parameterizing the table name, so we’re going to switch to using a template string instead. In production code you would need to scrub this string to prevent SQL injection.

function get(params, callback) {
  // In production these strings should be scrubbed to prevent SQL injection
  const { table, column, value } = params;
  let db = new sqlite3.Database("quiz.db");
  db.get(`SELECT * FROM ${table} WHERE ${column} = ${value}`, (err, row) => {
    callback(err, row);
    db.close();
  });
}

Another issue is that there might be an error reading from the database. Our user code will need to know whether each database query has had an error; otherwise it shouldn’t continue querying the data. We’ll use the Node.js convention of passing an error object as the first argument of our callback. Then we can check if there’s an error before moving forward.

Let’s take our answer with an id of 2 and check which quiz it belongs to. Here’s how we can do this with callbacks:

// index.js
import sqlite3 from "sqlite3";

function get(params, callback) {
  // In production these strings should be scrubbed to prevent SQL injection
  const { table, column, value } = params;
  let db = new sqlite3.Database("quiz.db");
  db.get(`SELECT * FROM ${table} WHERE ${column} = ${value}`, (err, row) => {
    callback(err, row);
    db.close();
  });
}

get({ table: "answer", column: "answerid", value: 2 }, (err, answer) => {
  if (err) {
    console.log(err);
  } else {
    get(
      { table: "question", column: "questionid", value: answer.answerquestion },
      (err, question) => {
        if (err) {
          console.log(err);
        } else {
          get(
            { table: "quiz", column: "quizid", value: question.questionquiz },
            (err, quiz) => {
              if (err) {
                console.log(err);
              } else {
                // This is the quiz our answer belongs to
                console.log(quiz);
              }
            }
          );
        }
      }
    );
  }
});

Woah, that’s a lot of nesting! Every time we get an answer back from the database, we have to add two layers of nesting — one to check for an error, and one for the next callback. As we chain more and more asynchronous calls our code gets deeper and deeper.

We could partially prevent this by using named functions instead of anonymous functions, which would keep the nesting lower, but make our code our code less concise. We’d also have to think of names for all of these intermediate functions. Thankfully, promises arrived in Node back in 2015 to help with chained asynchronous calls like this.

Promises

Wrapping asynchronous tasks with promises allows you to prevent a lot of the nesting in the previous example. Rather than having deeper and deeper nested callbacks, we can pass a callback to a Promise’s then function.

First, let’s change our get function so it wraps the database query with a Promise:

// index.js
import sqlite3 from "sqlite3";
function get(params) {
  // In production these strings should be scrubbed to prevent SQL injection
  const { table, column, value } = params;
  let db = new sqlite3.Database("quiz.db");

  return new Promise(function (resolve, reject) {
    db.get(`SELECT * FROM ${table} WHERE ${column} = ${value}`, (err, row) => {
      if (err) {
        return reject(err);
      }
      db.close();
      resolve(row);
    });
  });
}

Now our code to search for which quiz an answer is a part of can look like this:

get({ table: "answer", column: "answerid", value: 2 })
  .then((answer) => {
    return get({
      table: "question",
      column: "questionid",
      value: answer.answerquestion,
    });
  })
  .then((question) => {
    return get({
      table: "quiz",
      column: "quizid",
      value: question.questionquiz,
    });
  })
  .then((quiz) => {
    console.log(quiz);
  })
  .catch((error) => {
    console.log(error);
  }
);

That’s a much nicer way to handle our asynchronous code. And we no longer have to individually handle errors for each call, but can use the catch function to handle any errors that happen in our chain of functions.

We still need to write a lot of callbacks to get this working. Thankfully, there’s a newer API to help! When Node 7.6.0 was released, it updated its JavaScript engine to V8 5.5 which includes the ability to write ES2017 async/await functions.

Async/Await

With async/await we can write our asynchronouse code almost the same way we write synchronous code. Sarah Drasner has a great post explaining async/await.

When you have a function that returns a Promise, you can use the await keyword before calling it, and it will prevent your code from moving to the next line until the Promise is resolved. As we’ve already refactored the get() function to return a promise, we only need to change our user-code:

async function printQuizFromAnswer() {
  const answer = await get({ table: "answer", column: "answerid", value: 2 });
  const question = await get({
    table: "question",
    column: "questionid",
    value: answer.answerquestion,
  });
  const quiz = await get({
    table: "quiz",
    column: "quizid",
    value: question.questionquiz,
  });
  console.log(quiz);
}

printQuizFromAnswer();

This looks much more familiar to code that we’re used to reading. Just this year, Node released top-level await. This means we can make this example even more concise by removing the printQuizFromAnswer() function wrapping our get() function calls.

Now we have concise code that will sequentially perform each of these asynchronous tasks. We would also be able to simultaneously fire off other asynchronous functions (like reading from files, or responding to HTTP requests) while we’re waiting for this code to run. This is the benefit of all the asynchronous style.

As there are so many asynchronous tasks in Node, such as reading from the network or accessing a database or filesystem. It’s especially important to understand these concepts. It also has a bit of a learning curve.

Using SQL to its full potential

There’s an even better way! Instead of having to worry about these asynchronous calls to get each piece of data, we could use SQL to grab all the data we need in one big query. We can do this with an SQL JOIN query:

// index.js
import sqlite3 from "sqlite3";

function quizFromAnswer(answerid, callback) {
  let db = new sqlite3.Database("quiz.db");
  db.get(
    `SELECT *,a.body AS answerbody, ques.body AS questionbody FROM answer a 
    INNER JOIN question ques ON a.answerquestion=ques.questionid 
    INNER JOIN quiz quiz ON ques.questionquiz = quiz.quizid 
    WHERE a.answerid = ?;`,
    [answerid],
    (err, row) => {
      if (err) {
        console.log(err);
      }
      callback(err, row);
      db.close();
    }
  );
}
quizFromAnswer(2, (e, r) => {
  console.log(r);
});

This will return us all the data we need about our answer, question, and quiz in one big object. We’ve also renamed each body column for answers and questions to answerbody and questionbody to differentiate them. As you can see, dropping more logic into the database layer can simplify your JavaScript (as well as possibly improve performance).

If you’re using a relational database like SQLite, then you have a whole other language to learn, with a whole lot of different features that could save time and effort and increase performance. This adds more to the pile of things to learn for writing Node.

Node APIs and conventions

There are a lot of new node APIs to learn when switching from browser code to Node.js.

Any database connections and/or reads of the filesystem use APIs that we don’t have in the browser (yet). We also have new APIs to set up HTTP servers. We can make checks on the operating system using the OS module, and we can encrypt data with the Crypto module. Also, to make an HTTP request from node (something we do in the browser all the time), we don’t have a fetch or XMLHttpRequest function. Instead, we need to import the https module. However, a recent pull request in the node.js repository shows that fetch in node appears to be on the way! There are still many mismatches between browser and Node APIs. This is one of the problems that Deno has set out to solve.

We also need to know about Node conventions, including the package.json file. Most front-end developers will be pretty familiar with this if they’ve used build tools. If you’re looking to publish a library, the part you might not be used to is the main property in the package.json file. This property contains a path that will point to the entry-point of the library.

There are also conventions like error-first callbacks: where a Node API will take a callback which takes an error as the first argument and the result as the second argument. You could see this earlier in our database code and below using the readFile function.

import fs from 'fs';

fs.readFile('myfile.txt', 'utf8' , (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data)
})

Different types of modules

Earlier on, I casually instructed you to throw "type":"module" in your package.json file to get the code samples working. When Node was created in 2009, the creators needed a module system, but none existed in the JavaScript specification. They came up with Common.js modules to solve this problem. In 2015, a module spec was introduced to JavaScript, causing Node.js to have a module system that was different from native JavaScript modules. After a herculean effort from the Node team we are now able to use these native JavaScript modules in Node.

Unfortunately, this means a lot of blog posts and resources will be written using the older module system. It also means that many npm packages won’t use native JavaScript modules, and sometimes there will be libraries that use native JavaScript modules in incompatible ways!

Other concerns

There are a few other concerns we need to think about when writing Node. If you’re running a Node server and there is a fatal exception, the server will terminate and will stop responding to any requests. This means if you make a mistake that’s bad enough on a Node server, your app is broken for everyone. This is different from client-side JavaScript where an edge-case that causes a fatal bug is experienced by one user at a time, and that user has the option of refreshing the page.

Security is something we should already be worried about in the front end with cross-site scripting and cross-site request forgery. But a back-end server has a wider surface area for attacks with vulnerabilities including brute force attacks and SQL injection. If you’re storing and accessing people’s information with Node you’ve got a big responsibility to keep their data safe.

Conclusion

Node is a great way to use your JavaScript skills to build servers and command line tools. JavaScript is a user-friendly language we’re used to writing. And Node’s async-first nature means you can smash through concurrent tasks quickly. But there are a lot of new things to learn when getting started. Here are the resources I wish I saw before jumping in:

And if you are planning to hold data in an SQL database, read up on SQL Basics.


Comparing Node JavaScript to JavaScript in the Browser originally published on CSS-Tricks. You should get the newsletter.

The Top SIEM Challenges Modern Security Practitioners Face Today

In many ways, the tools security professionals have at their disposal have not kept up with the seismic changes in IT infrastructure and workloads brought about by the cloud. For example, most Security Information and Event Management (SIEM) platforms are still based on decades-old technology and architectures.

The deficiencies in traditional SIEM solutions are common knowledge throughout the industry, at least anecdotally, but I wanted to see actual data. To explore the firsthand experiences of security practitioners, my company, Panther Labs, commissioned an independent study to understand how well legacy SIEMs meet the needs of security teams today. 

Proxy Design Pattern in Java

In the video below, we take a closer look at the Proxy Design Pattern in Java. This video tutorial includes an introduction, real-time examples, a class/sequence diagram, and implementation. Let's get started!

5 Best Practices for Succeeding at Developer-First Application Security

Advances in developer tools, containers, code repositories, and more enable developer teams to deliver software at an unprecedented pace. However, application security teams are often understaffed, underfunded, and laboring to keep pace with software development. One demonstrated strategy that helps AppSec teams keep up and even provides them time for high-value security tasks is to shift security left in the development cycle. 

Creating a developer-first approach to security, or in other words, shifting security left requires that developers become accountable for producing secure code. Since security is not the typical developer's area of expertise, application security engineers assume the role of providing oversight and guidance needed to enable developers to succeed. 

Basic Google BigQuery Operations With a Salesforce Sync Demo in Mule 4

If we think about data storage, the first thing that comes to our mind is a regular database. This can be any of the most popular ones, like MySQL, SQL Server, Postgres, Vertica, etc., but I noticed not too many have interacted with one of the services Google provides with the same purpose: Google BigQuery. Maybe it is because of the pricing, but in the end, many companies are moving to cloud services and this service seems to be a great fit for them.

In this post, I will demonstrate in a few steps how we can make a sync job that allows us to describe a Salesforce instance and use a few objects to create a full schema of those objects (tables) into a Google BigQuery dataset. Then with the schema created, we will be able to push some data into BigQuery from Salesforce and see it in our Google Cloud Console project.