Open source Java projects: Vert.x

Enterprise messaging and integration with Vert.x

If you were excited about Node.js, Vert.x could be the next big thing for you: a similarly architected enterprise system that is built on the JVM. This installment of the Open source Java projects series introduces Vert.x with two hands-on examples based on the newly released Vert.x 2.0: First, build a simple Vert.x web server, then discover for yourself how the Vert.x event bus handles publish/subscribe and point-to-point messaging for effective enterprise integration.

When Node.js emerged a few years ago, many developers were excited about its unusual approach to building scalable server-side applications. Rather than starting heavyweight containers that would service requests using multiple threads, Node.js starts multiple lightweight, single-threaded servers and routes traffic to them. Now a similar framework has emerged, which deploys servers inside a JVM, using JVM facilities to manage traffic to lightweight server processes. In this installment of the Open source Java projects series you'll learn about Vert.x, an event-driven framework similar to Node.js, that builds on the JVM and also extends it in some important new ways.

Highlights of Vert.x

Vert.x applications are event-driven, asynchronous, and single-threaded. Vert.x processes communicate via an event bus, which is a built-in piece of Vert.x's event-driven architecture. Combining asynchronous processing, single-threaded components, and an event bus yields a high degree of scalability, and writing single-threaded applications can be a relief for Java developers accustomed to multithreaded concurrency. Arguably, the best part of Vert.x is its modular JVM-based architecture. Vert.x applications can run on virtually any operating system, and they can be written using any supported JVM-compatible programming language. A Vert.x application can be written entirely in a single language, or it could be a mash-up of modules written in different programming languages. Vert.x modules are integrated on the Vert.x event bus.

Event-based programming in Vert.x

Like other tools and frameworks I've recently covered in this series, Vert.x speaks the language of modern enterprise development, but puts its own emergent spin on familiar technology. Vert.x's event-based programming model is a mix of standard and unique features. Vert.x applications are largely written by defining event-handlers, which do things like manage HTTP requests and pass messages through the event bus. Unlike traditional event-based applications, however, Vert.x applications are guaranteed not to block. Rather than opening a socket to a server, requesting a resource, and then waiting (blocking) for the response, Vert.x sends the response to your application asynchronously, via an event handler.

Vert.x's programming framework includes some vernacular that will be helpful to know when you work through the two demo applications later in this article:

  • A verticle is the unit of deployment in Vert.x. Every verticle contains a main method that starts it. An application may be a single verticle or may consist of multiple verticles that communicate with one another via the event bus.
  • Verticles run inside of a Vert.x instance. Each Vert.x instance runs in its own JVM instance and can host multiple verticles. A Vert.x instance ensures that verticles are isolated from each other by running each one in its own classloader, so that there is no risk of one instance modifying another's static variables. A host may run a single Vert.x instance or multiple ones.
  • A Vert.x instance guarantees that each verticle instance is always executed in the same thread. Concurrency in Vert.x is single-threaded.
  • Internally, Vert.x instances maintain a set of threads (typically one for each CPU core) that executes in an event loop: check to see if there's work to do, do it, and go to sleep.
  • Verticles communicate by passing messages using an event bus. This message-passing strategy closely resembles the Actor model employed by the Akka framework, which I profiled in May 2013.
  • While you might assume that shared data and scalability are diametrically opposed, that's only true when data is mutable. Vert.x provides a shared map and a shared-set facility for passing immutable data across verticles running in the same Vert.x instance.
  • Vert.x uses relatively few threads to create an event loop and execute verticles. But in some cases a verticle needs to do something either computationally expensive, or that might block, such as connecting to a database. When this happens Vert.x allows you to mark a verticle instance as a worker verticle, in which case it will be executed by a background thread pool. Vert.x ensures that worker verticles will never be executed concurrently, so you want to keep them to a minimum, but they are there to help you when you need them.

Figure 1 shows the architecture of a Vert.x system consisting of Vert.x instances, verticles, JVMs, the server host, and the event bus.

Figure 1. Architecture of a Vert.x system (click to enlarge)

Vert.x core services and modules

Vert.x functionality can be divided into two categories: core services and modules. Core services are services that can be directly called from a verticle and include clients and servers for TCP/SSL, HTTP, and web sockets; services to access the Vert.x event bus; timers, buffers, flow control, file system access, shared maps and sets, logging, access configuration, SockJS servers, and deploying and undeploying verticles. Core services are fairly static and not expected to change, so all other functionality is provided by modules.

Vert.x applications and resources can easily be packaged into modules and shared via the Vert.x public module repository. Interacting with modules is asynchronous via the Vert.x event bus: send a module a message in JSON and your application will receive a response. This decoupling between modules and integration through the service bus means that modules can be written in any supported language and used by any other supported language. So, if someone writes a module in Ruby that you want to use in your Java application, nothing is stopping you from doing it!

Write a Java-based Vert.x web server

We'll start getting to know Vert.x by setting up an environment that we can use to develop our two examples for this article: a basic web server application and a message-passing system. First download Vert.x; as of this writing the latest version is 2.0.0.final. Decompress it locally and add its bin folder to your PATH. Note that you will need to install Java 7 if you haven't already.

If you're a Maven person like me, then you can simply add the following dependencies to your POM file:

Listing 1. Maven POM for Vert.x

   <dependency>
        <groupId>io.vertx</groupId>
        <artifactId>vertx-core</artifactId>
        <version>2.0.0-final</version>
        </dependency>
        <dependency>
          <groupId>io.vertx</groupId>
          <artifactId>vertx-platform</artifactId>
          <version>2.0.0-final</version>
        </dependency>

Vert.x's "Hello, World" application is a web server, a good starter application for getting to know Vert.x's event-based programming model. The in-house Vert.x tutorial demonstrates how to create a web server that serves content from a directory called webroot with just a few lines of code. I've written a Java-based variation on the demo as an introductory exercise.

Listing 2 shows the contents of my Server.java file, which is very similar to the one found on the Vert.x homepage. Download the source code for this article to see the complete file.

Listing 2. Server.java

package com.geekcap.vertxexamples;

import org.vertx.java.core.Handler;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.deploy.Verticle;

public class Server extends Verticle {
    public void start() {
        vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() {
            public void handle(HttpServerRequest req) {
                String file = req.path.equals("/") ? "index.html" : req.path;
                req.response.sendFile("webroot/" + file);
            }
        }).listen(8080);
    }
}

The first few lines in Listing 2 import the required Vert.x classes:

  • Handler is the base class for all handlers; in short: something happened asynchronously, so handle it!
  • HttpServerRequest represents a server-side HTTP request in Vert.x. An instance of this class will be created for each request that is handled by the server, then passed to your application via the Handler instance (which you will have registered with the HttpServer).
  • Verticle is the primary unit of deployment in a Vert.x application. In order to use Vert.x, you need to extend the Verticle class and override the start() method, which is the entry-point to your Verticle.

See the Vert.x Javadoc to learn more about these classes.

Notice the vertx variable in Listing 2: What is it for? The Verticle class defines vertx as a protected member variable (that your Verticle inherits), which provides access to the Vert.x runtime. The vertx variable is of type Vertx, which exposes the following methods:

  • createHttpClient() creates an HTTP/HTTPS client
  • createHttpServer() creates an HTTP/HTTPS server
  • createNetClient() creates a TCP/SSL client
  • createNetServer() creates a TCP/SSL server
  • creatSockJSServer() creates a SockJS server that wraps an HTTP server
  • eventBus() provides your application access to the event bus
  • fileSystem() provides your application access to the file system
  • sharedData() provides your application access to the shared data object, which can be used to share data between Verticles

The code in Listing 2 creates a new HTTP server, retrieves its request-handler reference, and sets the request handler to a newly created HttpServerRequest handler. The Handle interface defines a method named handler() and uses generics to define the type of instance that is passed to it in the class definition; in this case HttpServerRequest. The HttpServerRequest then defines the following fields:

  • method is a String containing the method of the given request, such as GET, POST, PUT, DELETE, and so forth.
  • path is a String containing the requested path, such as /index.html.
  • query is a String containing the query parameters, such as the part that follows the question mark in the following: ?key=value.
  • response is a reference to an HttpServerResponse that represents the response to the HTTP request.
  • uri is the complete URI of the request.

Listing 2 completes by mapping an empty request -- "/" -- to index.html, and then invoking the HttpServerResponse's sendFile() method to tell Vert.x to stream the specified file back to the caller.

In summary, the Server class accesses the Vert.x runtime, asks it to create a new HTTP server, and registers a Handler (that expects an HttpServerRequest variable) with the HttpServer. In the Handler's handle() method, the Server class serves files from the filesystem located in the webroot directory, which is relative to wherever you launched the Server Verticle from.

Building the web server

Let's build the sample application, then we'll use Vert.x to execute it. The Maven POM file for this project is shown in Listing 3.

Listing 3. Maven POM to build the web server

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.geekcap</groupId>
    <artifactId>vertx-examples</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>vertx-examples</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

    <dependencies>
        <dependency>
                <groupId>io.vertx</groupId>
                <artifactId>vertx-core</artifactId>
                <version>2.0.0-final</version>
        </dependency>
        <dependency>
          <groupId>io.vertx</groupId>
          <artifactId>vertx-platform</artifactId>
          <version>2.0.0-final</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

The two dependencies added to the POM file are vertx-core and vertx-lang-java, which are needed to develop Vert.x applications in Java. To build this application, execute the following Maven command:

mvn clean install

This will yield a file named vertx-examples-1.0-SNAPSHOT.jar that will need to be in your CLASSPATH in order to launch your verticle. This sample application serves up web resources found in the webroot directory relative to where the application is launched. You'll therefore need to create a webroot directory and build some resources to serve from it. To launch this verticle, execute the vertx application in Vert.x's bin directory, like so:

$VERTX_HOME/bin/vertx run com.geekcap.vertxexamples.Server -cp vertx-examples-1.0-SNAPSHOT.jar

The vertx command accepts several options including the one we're using, which is run. The run option requires a verticle called main, which is just the name of the class that extends Verticle and contains a start() method and an optional set of arguments. In this case we set the CLASSPATH using the -cp argument and passing in the JAR file that we just created. The server will start without outputting anything to the screen, but you can point your browser to the URL: http://localhost:8080.

For my example I created a simple HTML file that says "Hello, Vert.x," named it index.html and placed it in my webroot directory. Here is my output:

$ curl http://localhost:8080

<html>
<head><title>Hello, Vert.x</title></head>
<body>
<p>Hello, Vert.x</p>
</body>
</html>

Messaging with Vert.x

One of the most important features of Vert.x is its event bus. The Vert.x event bus allows verticles, potentially written in different programming languages, to communicate with one another using either point-to-point messaging or publish/subscribe messaging. In this section you'll get a feel for how to integrate functionality from different verticles using both approaches.

Before we begin, why would you want to use messaging over a more traditional event-based programming model? For one thing, messaging supports the integration of applications and components written in different programming languages. It also enables loose coupling, which means that you can write multiple task-focused pieces of code rather than a single, complex program. Finally, asynchronous communication between verticles increases system scalability. Asynchronous communication enables you to define the system capacity as it evolves. Messages might back up as your system load increases, but they will eventually be processed. Vert.x's support for a distributed event bus also gives you the option to start up additional verticles to handle increased load.

In order to set up a Vert.x messaging system, you need to gain access to the event bus. Start by executing the eventBus() method on a vertx class instance:

EventBus eb = vertx.eventBus();    

Once you're connected to an EventBus you can publish messages in one of two ways:

  • publish() publishes a message to an address using publish/subscribe messaging, meaning that every subscriber to a given address will receive the published message. Addresses are just Strings, so you want to choose something meaningful, but in the end what matters is that both publisher and subscriber are configured to use the same string. If you are familiar with Java Message System (JMS), publish() acts similarly to publishing a message to a topic.
  • send() sends a message to an address using point-to-point messaging, meaning that only one subscriber will receive the message. If there are multiple subscribers to the address then Vert.x will use a round-robin algorithm to send the message. The benefit of using a round-robin algorithm is scalability: if you do not have enough resources on a single Vert.x instance to support your load, then you can simply start additional Vert.x instances and register them as listeners to the specified address. In JMS terms, issuing send() is similar to publishing a message to a queue.

Publish/subscribe vs point-to-point messaging

In a publish/subscribe messaging model, a publisher sends a message to a topic that is broadcast out to all subscribers. Using publish/subscribe over point-to-point messaging in an event-driven architecture means that a component is only responsible for publishing events as they occur. The publisher does not need to be aware of its subscribers in order to broadcast to them. Figure 2 is a flow diagram of a typical Vert.x publish/subscribe messaging architecture.

Figure 2. Publish/subscribe messaging

In point-to-point messaging, a message is sent from a publisher directly to a consumer via a queue. Point-to-point messaging is a good choice when you want a message consumed exactly once, or when two components want to communicate with each other asynchronously. A point-to-point messaging architecture is shown in Figure 3.

Figure 3. Point-to-point messaging

We'll use Vert.x to explore both messaging systems in the next sections.

Publish/subscribe messaging example

Listing 4 updates my original Server class (from Listing 1) in a few ways. First, it deploys a new verticle called AuditVerticle (which is defined in Listing 5) by invoking the deployVerticle() method on the container instance. The container instance, which is defined as part of the parent Verticle class, provides access to the container in which a verticle runs; therefore, it is the appropriate place to deploy new verticles.

Listing 4. Server.java extended for point-to-point messaging

package com.geekcap.vertxexamples;

import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Verticle;

public class Server extends Verticle {
    public void start() {

        // Create our dependent verticles
        container.deployVerticle("com.geekcap.vertxexamples.AuditVerticle");

        // Create an HTTP Server that serves files
        vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() {
            public void handle(HttpServerRequest req) {
                Logger logger = container.logger();

                if (logger.isDebugEnabled()) {
                    logger.debug("Received a request for resource: " + req.path());
                }
                logger.fatal("Where are my logs!?!?");
                logger.info("Here is an info message");

                // Serve up our files
                String file = req.path().equals("/") ? "index.html" : req.path();
                req.response().sendFile("webroot/" + file);

                // Let's tell the world (via the event bus) that we received a request
                EventBus eb = vertx.eventBus();
                eb.publish( "com.geekcap.vertxexamples.Server.announcements", "We received a request for resource: " + req.path() );

            }
        }).listen(8080);
    }
}

Listing 4 executes deployVerticle() to deploy the AuditVerticle. The deployVerticle() method deploys a standard Verticle to the container, which maintains its own event-loop. After handling an incoming HTTP request (as shown in Listing 1), Listing 4 publishes a message to the event bus. First, it obtains access to the event bus through the vertx instance variable, then it executes the eventBus() method. Once it has the EventBus object it invokes its publish() method, which is the gateway to publishing messages in a publish/subscribe fashion.

Messaging for loose coupling

For the past three years I have worked in an event-driven architecture, and I have found that publish/subscribe messaging, sometimes called topics, is a great way to loosely couple systems. Message publishers do not need to know anything about their subscribers, so new subscribers can be added at any time without affecting the publisher.

The publish() method accepts a destination, which in this case is "com.geekcap.vertxexamples.Server.announcements". Recall that the naming of this destination is arbitrary, but prefacing the type of notification (announcement in this example) with the fully-qualified name of the class makes it clear where the message came from.

Listing 5 shows the source code for the AuditVerticle class.

Listing 5. AuditVerticle.java

package com.geekcap.vertxexamples;

import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Verticle;

public class AuditVerticle extends Verticle {

    @Override
    public void start() {
        // Let's register ourselves as a listener to Server notifications
        EventBus eb = vertx.eventBus();
        Handler<Message> auditHandler = new Handler<Message>() {
            @Override
            public void handle(Message message) {
                Logger logger = container.logger();
                logger.info( "AuditVerticle here, someone requested resource: " + message.body() );
            }
        };
        eb.registerHandler( "com.geekcap.vertxexamples.Server.announcements", auditHandler );
    }
}

The AuditVerticle in Listing 5 acts much like a reporting engine: it listens for "announcements" from the Server class and then writes those out as informational log messages. When something of interest happens in the Server class, it can publish it to its announcements topic and different subscribers can do different things, such as logging the message or inserting it in a Hadoop cluster for later analysis.

Listing 5 then creates a Handler in-line instance (an anonymous inner class is created and assigned to a variable without creating the class in a separate file) that logs the message. Next, it registers a handler to the "com.geekcap.vertxexamples.Server.announcements" address by invoking the EventBus's registerHandler() method. Now, any time that the Server class publishes a message to this destination, the AuditHandler's handle() method will be invoked.

Point-to-point messaging example

Point-to-point messaging is used either when you want a message to be processed only by a single consumer or as a mechanism for components to communicate with one another asynchronously. In this section I demonstrate the latter by creating a new class that relies on a worker verticle to do its work for it, and then that worker verticle communicates the result back to the Server2.

Listing 6 shows the source code for the Server2 class.

Listing 6. Server2.java

package com.geekcap.vertxexamples;

import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.deploy.Verticle;

import java.util.concurrent.ConcurrentMap;

public class Server2 extends Verticle {
    public void start() {

        // Create our dependent verticles
        container.deployWorkerVerticle("com.geekcap.vertxexamples.MyWorkerVerticle");

        // Start a server that handles things with point-to-point messaging
        vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() {
            @Override
            public void handle(final HttpServerRequest req) {

                // Set a shared variable
                ConcurrentMap<String, String> map = vertx.sharedData().getMap("mymap");
                map.put("mykey", "myvalue");

                // Let's send a message to a worker verticle and wait for it to respond
                EventBus eb = vertx.eventBus();
                eb.send("request.worker", req.path, new Handler<Message<String>>() {
                    @Override
                    public void handle(Message<String> message) {
                        Logger logger = container.getLogger();
                        logger.info( "Received a reply from our worker: " + message.body );
                        req.response.headers().put("Content-Length", Integer.toString(message.body.length()));
                        req.response.write(message.body);
                    }
                });
            }
        }).listen(8080);
    }
}

The Server2 class starts by deploying a worker verticle. Worker verticles are different from standard verticles in that they do not contain an event look and are expected to be triggered by an event bus message. Worker verticles are deployed by obtaining access to the Vert.x container and invoking its deployWorkerVerticle() method.

Next, the Server2 obtains access to the EventBus, again by invoking the eventBus() method on the vertx instance variable. This time the Server2 invokes the send() method, which is the gateway to sending messages in a point-to-point fashion. In this case it sends the request path to a destination named "request.worker". The first parameter to the send() method is the destination, the second parameter is the data to send to the destination, and an optional third parameter is a Handler that can be called back by the recipient of the message.

The MyWorkerVerticle, which is shown in Listing 7, is designed to construct a response for the specified request path and send that response back to the Server2's Handler. The handler logs the response and then writes that response back to the HttpServerRequest that initiated the action. The only thing that is a little challenging is that before we're able to write back to the HttpServerRequest we need to specify the HTTP Content-Length of the response, which is just the length of the string we're returning.

Two additional Vert.x features are added to the Server2 class:

  • Logging: The container variable has a method named getLogger() that provides access to Vert.x's logger. This logger is very similar to log4j and provides methods like debug(), info(), and fatal() to log messages at different logging levels. By default, logging information will be echoed to standard output and will be written to a file named vertx.log located in the TMPDIR-defined directory.
  • Shared data: Sharing data between verticles is accomplished by executing the sharedData() method on the vertx instance and then invoking one of the shared data accessor methods. In Listing 4, we store data in a Map that is retrieved by invoking getMap(); you can likewise retrieve shared data in a Set by invoking getSet(). All of the verticles in your Vert.x instance have access to the same shared data using the same paradigm, so it is a way for you to share immutable data between verticles.

Listing 7 shows the source code for the MyWorkerVerticle class.

Listing 7. MyWorkerVerticle.java

package com.geekcap.vertxexamples;

import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.EventBus;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Verticle;

import java.util.concurrent.ConcurrentMap;

public class MyWorkerVerticle extends Verticle {
    @Override
    public void start() {
        // Register a listener
        EventBus eb = vertx.eventBus();
        Handler<Message> workerHandler = new Handler<Message>() {
            @Override
            public void handle(Message message) {
                Logger logger = container.logger();
                logger.info( "MyWorkerVerticle just received a request for: " + message.body() );

                // Examine our shared map
                ConcurrentMap<String, String> map = vertx.sharedData().getMap("mymap");
                logger.info( "Shared data: " + map.get( "mykey" ) );

                message.reply( "<html><head><title>Worker Response</title></head><body>Hello, from the worker verticle</body></html>" );
            }
        };
        eb.registerHandler( "request.worker", workerHandler );

    }
}

The MyWorkerVerticle class creates a new Handler instance with a handle() method that processes messages from the Server2 class. Recall from Listing 6 that one of the arguments passed to the send() method was a Handler instance that could be invoked by the message recipient. Listing 7 invokes message.reply(), which sends a response back to the originator (which in this example is the Server2's Handler.)

The MyWorkerVerticle class obtains access to the EventBus and then registers its handler to listen to messages sent to the "request.worker" destination, to complete the loop.

As far as functionality, the MyWorkerVerticle simply constructs an HTML document and returns it back to the Server2 class. You could build upon this example by connecting to a database or reading data from another server to retrieve the data with which to build the response.

And you'll notice that MyWorkerVerticle retrieves the shared data from the vertx's sharedData() map.

In conclusion

As enterprise systems evolve in complexity, integration has become one of the biggest programming challenges for software developers. Vert.x addresses the complexity of integration in a couple of ways: First, it is built around an event bus that loosely couples verticles while supporting multiple programming languages. Regardless of whether code is written in Java, Ruby, Python, or JavaScript, you can seamlessly integrate it inside the Vert.x event bus. Additionally, the event bus natively supports asynchronous messaging and an event-driven architecture, which yields high-scalability and loose-coupling.

This article has presented an overview of Vert.x, its unique vernacular, and the core components that it combines to build highly scalable enterprise solutions. I demonstrated both a web server and a messaging system written in Vert.x, using the latter example to develop a publish/subscribe messaging and a point-to-point messaging solution. In the latter example I also demonstrated event logging, shared data, and the difference between standard and worker verticles. While this article has been introductory, I've touched on some of the major features that illustrate the power of Vert.x, a solution similar to Node.js but built on the JVM. I hope that you are inspired to learn more about Vert.x and the types of programming challenge it resolves.

Steven Haines is a technical architect at Kit Digital, currently working onsite at Disney in Orlando. He is the founder of www.geekcap.com, an online education website, and has written hundreds of Java-related articles as well as three books: Java 2 From Scratch, Java 2 Primer Plus, and Pro Java EE Performance Management and Optimization. He lives with his wife and two children in Apopka, Florida.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more