Lesson Weekend

So far, all our Java applications have run in the command line. We've made some pretty cool stuff, but without a more visual front-end user interface our interactions with users are fairly limited.

This week we'll add an additional layer of complexity by creating entire web applications for our Java projects. To do this, we'll use a tool called Spark. In this lesson we'll begin exploring how to setup, configure, create, and launch a basic Spark application.

Spark

Spark is an awesome, lightweight web framework that will allow us to create web applications with robust Java back-ends. (Note: when Googling, there is Apache Spark and Java Spark aka the Spark framework. Keep that in mind.)

Spark sticks quite closely to its blueprint, Sinatra, a framework developed for Ruby, which is the gold standard of frameworks: By learning Spark, you will be able to understand how many other frameworks do what they do, Sinatra in Ruby, Silex in PHP, Nancy in C#... it’s very cross-applicable! Spark will be responsible for creating user interfaces for our applications that can be viewed in a web browser, and handling interactions between our user interfaces and back-end logic.

Friend Letter Application

To get a general sense of how Spark works, we will quickly walk through creating a website to display a letter to our friends. Then, we'll explore the individual elements of a Spark web application in further detail throughout upcoming lessons.

Let’s begin by creating a new, simple application with Spark.

Setup

First, create a new project in IntelliJ as outlined in the Getting Started with IntelliJ lesson. Use friendletter for the GroupId, and friend-letter for the ArtifactId.

Walk through the rest of the setup screens as demonstrated in the Getting Started lesson. Make sure you tick all the right boxes.

Once your project is loaded, add a .gitignore file.

Then, open the build.gradle file, and add the following lines:

build.gradle
dependencies {
   testCompile group: 'junit', name: 'junit', version: '4.12'
   compile "com.sparkjava:spark-core:2.6.0"
   compile "com.sparkjava:spark-template-handlebars:2.5.5" //we'll need this for later!
   compile 'org.slf4j:slf4j-simple:1.7.21' //to ensure easy to read logging.
}

Great! You may be prompted by Gradle that it needs to download the files that make up spark-core, spark-template-handlebars, and slf4j-simple (a logging tool that provides better error messaging). You can also do this yourself: click “gradle” in the right sidebar, and then the circular arrow “refresh” button. Gradle will download your dependencies for you.

Now, create an App.java file by right-clicking on the src/main/java directory and adding a Java class called App.java.

App.java File

Next, let's set up our App.java file. Last week our App.java files contained code to interact with users through the command line. With Spark, App.java will still be responsible for our user interfaces, but the code will look different. We'll need to use the format and methods expected by the Spark framework.

We'll add the following to App.java:

friend-letter/src/main/java/App.java
import static spark.Spark.*;

public class App {
  public static void main(String[] args) {
     get("/", (request, response) -> "Hello Friend!");
  }
}

As you type, you’ll notice that red squiggly lines appear - this means that IntelliJ is reading the code we are writing and checking it for errors! You’ll also potentially see a red line on the right hand side - this means something is wrong with this line. A yellow line means a warning or an inconsistency, and if all is well, you’ll see a green checkmark up at the top!

Note: If you see an error message that reads: "Cannot resolve symbol ‘spark’" make sure you have included spark dependencies in your build.gradle. Go check it.

Let’s walk through this short code:

  • import static spark.Spark.*; imports the Spark library Gradle has located and downloaded for us.

  • The next part, beginning with get(), is new. Let's discuss this portion in further detail.

Spark Routes

This line:

get("/", (request, response) -> "Hello Friend!");

...is known as a route. Routes like these are the primary building blocks of a Spark application.

This part is important - make sure you understand what a route is and what it does before moving on. Check with the pair near you or with your teacher if this feels confusing!

A route is comprised of three individual pieces:

  1. A verb depicting what the route is doing. In our route, this verb is get(). Remember when we learned about HTTP Requests? The get() method represents an HTTP GET request. And an HTTP GET request is responsible for getting information from a web server to return to the client. It means that something or someone is trying to GET information from the server: A list of restaurants, or students, some information, or an image.

  2. A path. That is, what part of the web application the verb is interacting with. In the example above, we see get("/".... This means that we're executing an HTTP GET request to our server, to retrieve the content for the / area of our site. If we later published our site online at the domain www.myreallycoolsparkapp.com, the route above would be responsible for getting the content at www.myreallycoolsparkapp.com/.

  3. A callback. That is, the (request, response) -> portion. Don't worry about the details of this different-looking line of code yet, just know that when a client requests the resources of the specific webpage located at /, the server will return the string "Hello Friend!" back to the client in the HTTP response body.

When our browser makes an HTTP request to our Spark application, Spark matches it to the first route in App.java that matches the request. So, for instance, if we make a GET request with the path /photos, Spark will read through the App.java file, and then execute the request when it hits the first route that begins with get(/photos....

Make a mental note: The App.java file gets read top to bottom! So, note that if you have multiple similar routes, Spark will map the request to the first route it finds that matches the request. We probably won't encounter this until our applications get much larger; but keep this in mind for the future.

Launching Spark Applications

Let's launch our app!

Find the Run menu at the top of your window and select Run. The first time you do this, IntelliJ will ask you what exactly you wish to run - because you can run projects in more than one way. Don’t worry about this now, simply select App.

After a few moments, your app should compile, and you should see this in the Run pane at the bottom of your screen:

If you don’t see the below, fix any errors you may have in your code, double check your dependencies, and try again.

The messages above are informing us that our Spark application has been successfully launched, or "ignited" in Spark's terminology, and it's 'located' at 0.0.0.0:4567. We can visit http://localhost:4567/ in the browser and see our "Hello Friend!" message:

hello-friend-first-spark-application

Congratulations, you just wrote your first Spark application!! That was really pretty straightforward, wasn’t it?

Integrating HTML into Spark

But we should probably include more than just "Hello Friend", don't you think? Let's write an entire letter to our friend using HTML:

friend-letter/src/main/java/App.java
import static spark.Spark.*;

public class App {
  public static void main(String[] args) {
    get("/", (request, response) ->
       "<!DOCTYPE html>" +
        "<html>" +
        "<head>" +
          "<title>Hello Friend!</title>" +
          "<link rel='stylesheet' + href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>" +
        "</head>" +
       "<body>" +
          "<h1>Hello From Afar</h1>" +
          "<p>Dear Friend,</p>" +
          "<p>How are you? I hope that you are having a nice weekend. I'm vacationing in the Iceland while I learn programming! </p>" +
          "<p>Friend, you would not believe how cold it is here. I should have gone to Hawaii instead.</p>" +
          "<p>But I like programming a lot, so I've got that going for me. </p>" +
          "<p>Looking forward to seeing you soon. I'll bring you back a souvenir. </p>" +
          "<p>Cheers,</p>" +
          "<p>Travel Enthusiast Jane</p>" +
        "</body>" +
      "</html>"
   );

The project directory should now look like this:

friend-letter
├── build.gradle
├── .gitignore
└── src
    └── main
        ├── java
            └── App.java

When we add HTML to a route like this, it needs to be one long String. However, to make it read like an HTML file, we concatenate multiple Strings across multiple lines with the + operator.

Updating Spark Applications During Development

If we refresh the browser after adding our HTML nothing happens! What went wrong?

Remember, Java is a compiled language. The computer doesn't run the source code in the .java files we write. Instead, this source code must be compiled into a format the computer can read. So, after making changes we need to recompile and restart the server to ensure the new content is present in the compiled code that Spark is referencing.

Let’s do that now.

Either select Run > Stop ‘App’ from the main menu, or hit the red square either in the top right corner or bottom left corner. If you are looking at the menu in the bottom left corner, you can also choose Rerun App.

If you check localhost:4567/ now, you’ll see your changes.

Let's add images to spruce things up!

First, we'll need to create a place to house our images and other resources.

  • In src/main folder, create another subdirectory called resources. This folder will store any non-Java files required to run your application, such as CSS, HTML, images, videos, music files, etc.

  • In resources, create another folder called public. The public folder is where we'll place content that should be visible to the outside world (ie: Things that users should be able to see. Like the images we want to display in our app.)

  • Finally, in the public folder, create an images folder. This is where our images will reside.

Images

Next, let's add some image files to our new friend-letter/src/main/resources/public/images directory. Unsplash is a great resource for free stock photographs. Pick out a few images to use in your own project. (You may also want to re-size them. To do this on a Mac, open an image in the "Preview" program, and visit Tools > Adjust Size... ).

For this lesson, I'm using the following two images from Unsplash. I've saved them as rockycoast.jpeg, and foggymountain.jpeg. Then, I resized them to a width of 1000 pixels. For your program, feel free to either download your own images, or save copies of these:

stock-image-for-web-app-1

stock-image-for-web-app-2

The project directory should now look like this:

friend-letter
├── build.gradle
├── .gitignore
└── src
    └── main
        ├── java
        │   └── App.java
        └── resources
            └── public
                └── images
                    ├── foggymountain.jpeg
                    └── rockycoast.jpeg

Sweet. Now let’s include some code in our template to render these:

Using Image Resources

To do this, we'll create another new route in App.java. Up until now, we were only accessing pages under the root URL, namely localhost:4567/.

Now, let's use these new images in our application! To do this, we'll create another new route in App.java. We can create a new page in our app by adding a new "entry" with another instance of Spark's get() method.

We also need to let Spark know where static files like images are going to live.

We can do this by including the line staticFileLocation("/public"); in the main() method. This will let Spark know it should always look in the /public directory for any additional resources we link, and load images assuming /public as a starting point.

friend-letter/src/main/java/App.java
import static spark.Spark.*;

public class App {
  public static void main(String[] args) {

  staticFileLocation("/public"); //this line has changed!

    get("/", (request, response) ->
       "<!DOCTYPE html>" +
        "<html>" +
        "<head>" +
          "<title>Hello Friend!</title>" +
          "<link rel='stylesheet' + href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>" +
        "</head>" +
       "<body>" +
          "<h1>Hello From Afar</h1>" +
          "<p>Dear Friend,</p>" +
          "<p>How are you? I hope that you are having a nice weekend. I'm vacationing in the Iceland while I learn programming! </p>" +
          "<p>Friend, you would not believe how cold it is here. I should have gone to Hawaii instead.</p>" +
          "<p>But I like programming a lot, so I've got that going for me. </p>" +
          "<p>Looking forward to seeing you soon. I'll bring you back a souvenir. </p>" +
          "<p>Cheers,</p>" +
          "<p>Travel Enthusiast Jane</p>" +
        "</body>" +
      "</html>"
   );
//new route below!
   get("/favorite_photos", (request, response) ->
    "<!DOCTYPE html>" +
      "<html>" +
      "<head>" +
        "<title>Hello Friend!</title>" +
        "<link rel='stylesheet'  href='https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>" +
      "</head>" +
      "<body>" +
       "<h1>Favorite Traveling Photos</h1>" +
          "<ul>" +
            "<li><img src='/images/foggymountain.jpeg' alt='A photo of a mountain.'/></li>" +
            "<li><img src='/images/rockycoast.jpeg' alt='A photo of a a rocky beach.'/></li>" +
          "</ul>" +
      "</body>" +
      "</html>"
    );

  }
}

If your image files are named something different, make sure the file path in your <img> tags reflect your unique images. If we quit the server, we should now be able to visit http://localhost:4567/favorite_photos. Our web app now has multiple pages!

Now, if you restart the server, you should be able to navigate to http://localhost:4567/favorite_photos and see your images!

hello-friend-with-images

This is super neat, clearly.

That said, I think we can agree, that, even with the added HTML, our website is still a little plain. And typing out the html with concatenation is tiresome. There must be a better way! (and there is.)

Enter the template!

Introduction to templates

What is a template? A special kind of crockery? No, not exactly.

Our application is really quite tiny. At the moment, it only contains two pages, with small pieces of text and a few images. Can you imagine what the App.java file would look like in a large application with many, many pages? And what if each page contained more content than ours? App.java would be huge and unwieldy! And what if we wanted to add a bunch of images, video, or other content? Oh dear.

Templates with Handlebars

Thankfully, we can easily slim down our App.java file as our applications grow. In this lesson we'll learn how to create templates to house our HTML. In the context of Spark, templates are separate files outside of App.java that contain the HTML for individual web pages within our application.

This allows us to re-use pages with different content, or to break pages down into sections - some may change based on which route we are trying to access, some may stay the same. It also allows us to insert bits of java code in our frontend to make showing dynamic data much, much easier.

Spark cannot render templates on its own, so we'll use a special tool called Handlebars.

If you have worked with JavaScript frontend frameworks before, you may have heard of Handlebars: Handlebars.js is used by many different kinds of platforms to render easy-to-read templates. Like Spark, it’s cross-applicable. While Handlebars was originally designed for use with JS, the Java version is exactly the same and uses the same syntax! Super cool.

Let’s get cracking.

We’ve already added Handlebars to our build.gradle, so that’s taken care of.

We’ll need to modify our App.java route a bit, and add some new directories to store our templates.

Creating Templates

If you don't already have one in place, create a subdirectory called resources inside of src/main. Make a directory called public, and another inside that called images.

In addition to housing any images used in our applications, Spark will also use the resources folder for templates.

Next, let's create a templates folder inside of resources. As you may have guessed, this is where our template files will reside.

Our project directory should look like this:

hello-friend
├── build.gradle
├── .gitignore
└── src
    └── main
        ├── java
        │   ├── App.java
        │   
        └── resources
            ├── public
            │   └── images
            └── templates

Good stuff!

Now we can create our first template. Within src/main/resources/templates create a file called hello.hbs. Handlebars template files have a .hbs extension, which stands for, you guessed, Handlebars.

Let’s move on over to the next lesson and crank out some templates.