Before we delve into this topic, I want to say thank you for reading. While I have wanted to start a blog for some time, this is my first post. I am also extremely happy to get some of my ideas down on "paper." So without further adieu, lets jump into this.

With frameworks like Spring being fairly mature and robust, why would anyone choose to rock the boat? For me, this question was simple. I hate that I have to start with many different files or to implement something as simple as a JSON Web Token, takes an endurance session of coding.  That's when I ran across Akka HTTP.

I have recently been introduced to the Akka ecosystem through my current software engineering job and have really become a fan due to the fact that it is highly concurrent, fault tolerant, and overall seems to reduce the amount of boilerplate code I have to write. The code becomes highly scalable and concurrent through the use of the actors, which send each other predefined messages using mailboxes, which prevents the concurrency issues that are all too familiar with threads. While we won't discuss the actor model too much on this post, it will be used in future posts, so if you are not too familiar with it, you can take a look here. There is not a huge need to reinvent the wheel on that. The Akka documentation is pretty spot on. There are many great tutorials on the website and I am sure that you will become a fan.

I personally like using Maven to build my Java projects. However, if that is not your cup of tea, my build file could easily be translated into sbt or Gradle build files. After the initial few lines common across .pom files, I added the following dependencies.

<dependencies>
	<dependency>
    	<groupId>com.typesafe.akka</groupId>
        <artifactId>akka-http_2.13</artifactId>
        <version>10.1.11</version>
	</dependency>
    <dependency>
    	<groupId>com.typesafe.akka</groupId>
        <artifactId>akka-http-jackson_2.13</artifactId>
        <version>10.1.11</version>
	</dependency>
    <dependency>
    	<groupId>com.typesafe.akka</groupId>
        <artifactId>akka-stream_2.13</artifactId>
        <version>2.5.29</version>
	</dependency>
    <dependency>
    	<groupId>com.typesafe.akka</groupId>
        <artifactId>akka-actor_2.13</artifactId>
        <version>2.5.29</version>
	</dependency>
</dependencies>

I will try to quickly run through what each one of these dependencies is for. We will definitely cover the items more in depth both this post and future posts.

  • The dependency for Akka HTTP will be the meat of this post and is what we use to set up routes, bind to a port, and listen for connections.

  • Akka HTTP Jackson is a JSON/XML parser that integrates flawlessly with Akka.

  • Akka Stream is needed to connect the user's HTTP Request to the method declaring the routes and send the HTTP Response information back from that method to the server. Since the method declaring the routes is a graph of what should be done, the ActorMaterializer contained in this dependency will actually execute that graph.

  • Akka Actor allows us to set up an Actor System which orchestrates everything. It also creates the actors that we will need at some point to do things like access a database.

At least at the time of writing this, these are the bare minimum dependencies needed. I also want to point out that Akka 2.12 throws compile errors due to a naming issue when Akka was building the Scala portion of their code. If you see this, double check to make sure that the Artifact ID for each dependency is updated to 2.13.

Moving forward to the build section of our .pom, we add the Maven Compiler Plugin and Maven Exec Plugin so that way we can compile and run our code without a jar. While I am not going to run unit tests in this example. We add the Maven Surefire Plugin so we can run unit test using JUnit as we build out the project. Below these you will notice the Maven Shade Plugin.

 <build>
 	<plugins>
    	<plugin>
        	<groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
            	<source>1.8</source>
                <target>1.8</target>
			</configuration>
		</plugin>

        <plugin>
			<artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0-M3</version>
            <dependencies>
            	<dependency>
                	<groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
					<version>1.3.2</version>
				</dependency>
			</dependencies>
		</plugin>

        <!-- Allows for the code to be ran using 
             mvn compile exec:exec-->
        <plugin>
        	<groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
            <configuration>
            	<executable>java</executable>
                <arguments>
                	<argument>-classpath</argument>
                    <classpath />
                    <argument>com.pelaghisoftware.server.Main</argument>
                </arguments>
            </configuration>
		</plugin>

        <!-- See When using JarJar, OneJar, Assembly or any jar-bundler -->
        <!-- https://doc.akka.io/docs/akka/current/general/configuration.html#when-using-jarjar-onejar-assembly-or-any-jar-bundler -->
        <!-- mvn clean install -->
		<plugin>
        	<groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.1</version>
            <executions>
            	<execution>
                	<phase>package</phase>
                    <goals>
                    	<goal>shade</goal>
					</goals>
					<configuration>
						<shadedArtifactAttached>true</shadedArtifactAttached>
                        <shadedClassifierName>complete</shadedClassifierName>
						<artifactSet>
                        	<includes>
                            	<include>*:*</include>
							</includes>
						</artifactSet>
                        <transformers>
                        	<transformer
                            	implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
								<resource>reference.conf</resource>
                            </transformer>
                            <transformer
                            	implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
								<manifestEntries>
                                	<Main-Class>
										com.pelaghisoftware.server.Main
                                    </Main-Class>
                                </manifestEntries>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
		</plugin>
    </plugins>
</build>

Since I like to use an executable jar to run the program, the Maven Shade Plugin will bundle all of the Akka and other dependencies into a single executable that can be run independently.

Having set up the .pom file we should be ready to start building our Main class. This is where Akka is a bit nicer than some of the other frameworks. The .pom at this point is actually larger than the code needed to get the server up and running. Lets just get a simple example up and running. The first few things we will need are an ActorSystem, ActorMaterializer, and to instantiate the app itself.

public static void main(String[] args) throws Exception
{
        //Create the parent actor system that will be used
        //to process everything
        ActorSystem system = ActorSystem.create("routes");

		//Server instance
        final Http http = Http.get(system);

		//Used to process the requests into responses
        final ActorMaterializer materializer =
        	ActorMaterializer.create(system);

        //In order to access all directives we need an instance 
        //where the routes are defined. I used Main as my class
        //name. Use whatever you name your class.
        Main app = new Main();
}

We actually are not too far from having a fully functioning server at this point. From here we will go ahead and create our first Route to produce an HTML page with the familiar "Hello World!" Akka is based in Scala and translated under the hood for Java. Therefore, we will be using some lambda expressions here.

Using the concat method given to us by Akka HTTP, we concat our endpoints here using path. The first argument for path is simply the endpoint. In the case of this server it corresponds to http://localhost:8099/hello. The second argument is a function where we will set the HTTP Methods that are available for the endpoint. Finally complete will return the HTTP Response.

private Route createRoute()
{
	return concat(
    	path("hello", () ->
        	get(() ->
            	complete(
                	HttpEntities.create(
                    	ContentTypes.TEXT_HTML_UTF8,
                        "<html>" +
                        	"<body>" + 
                            	"<h1>Hello world!</h1>" +
                        	"</body>" +
                        "</html>")))));
}

With that, if you fire up the server, and navigate to the endpoint, you should see the all to familiar, "Hello World!"

Now as an added bonus, I wanted to throw in marshalling and unmarshalling data. Again, this is fairly simple and I like the control I have. I will first set up an entity class (Later, I will use the entities to perform CRUD operations with a database and to use the information throughout the program). Note that I placed annotations in the code. These allow Jackson to convert incoming data into a user, and a User into JSON.  Other than that, for now it is a pretty standard POJO.

public class User
{
    private String userName;
    private String password;

    public User(){};

    @JsonCreator
    public User(
            @JsonProperty("userName") String userName,
            @JsonProperty("password") String password)
    {
        this.userName = userName;
        this.password = password;
    }

    @JsonGetter("userName")
    public String getUserName()
    {
        return userName;
    }

    @JsonGetter("password")
    public String getPassword()
    {
        return password;
    }
}

I will now just set up another endpoint to test the new functionality. We will change the output data later on, but this is just to get something going both directions to show how to do it.

private Route createRoute()
{
	return concat(
    	path("hello", () ->
        	get(() ->
            	complete(
                	HttpEntities.create(
                    	ContentTypes.TEXT_HTML_UTF8,
                    	"<html>" +
                        	"<body>" +
                            	"<h1>Hello world!</h1>" +
                            "</body>" +
                        "</html>")))),
        path("user", () ->
        	get(() ->
            	complete(
                	StatusCodes.OK,
                    new User("test", "test"),
                    Jackson.<User>marshaller()))),
            post(() ->
            	entity(
                	Jackson.unmarshaller(User.class), user ->
                    {
                    	System.out.println("Username: " +
                        	user.getUserName() + 
                            " Password: " + 
                            user.getPassword());
                        return complete(StatusCodes.OK);
                    })));
}

So here you can see exactly how to set up multiple endpoints, as well as, handling multiple HTTP Methods on one endpoint. To marshall data is simple enough, you pass the Status Code, object, and call the marshaller with the type of the object to be marshalled. To unmarshall use the entity method which will pipe the unmarshaller to a function that allows you to manipulate the data. In this example, I just print out the information to the console to verify that we are getting the right information and then return a 200 Status Code as the HttpResponse.  I am using JSON throughout, so the input for the post should be a properly formatted JSON such as the example below.

{
  "userName": "Test",
  "password": "test"
}

I did not notice a ton of examples for Java, or at least many that were up to date. I tried to keep it short and as useful as possible. If you have any suggestions, questions, or anything in particular please leave feedback below in the comments. So with that I will let you get back to your busy day and I hope this helps.

If you want to see everything all at once, the source code for this can be found at https://github.com/jmross14/PelaghiSoftwareWebServer/tree/master/AkkaHTTP-1.

Ci vediamo presto!