-
Notifications
You must be signed in to change notification settings - Fork 61
Spring
#Comparison with Spring Framework-based code Here, I explain some of the Akka and Spray concepts in terms that would be familiar to Spring programmers. It will be a very brief explanation and I will make criminal simplifications. If you are seasoned Akka and Scala programmer, stop reading now; otherwise, dive in and see how Akka’s patterns map to the traditional request-response Spring code. We will build trivial application with RESTful API.
#The Spring way The crucial difference is structure of the application and its instantiation model. In contemporary Spring, we might use JavaConfig to describe the components and the dependencies between the components. We then let the Spring Framework instantiate the components and satisfy the dependencies. Typically, the components are singletons—the emergency handbrake of OOP… but we all know that. Let’s get back to the point of building application. In Spring, we’ll have:
public interface UserService {
User get(Long id);
List<User> findAll();
void save(User user);
}
@Service
public class DefaultUserService implements UserService {
public User get(Long id) {
}
List<User> findAll() {
}
public void save(User user) {
}
}
This represents the service tier–it contains the UserService
component, which loads & saves the User entities to some underlying storage. Now, to provide a REST API around it, we need to write a controller:
@Controller("/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public User view(Long id) {
return this.userService.get(id);
}
@RequestMapping(value = "/", method = RequestMethod.GET)
@ResponseBody
public List<User> findAll() {
return this.userService.findAll();
}
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseBody
public User put(@Valid @RequestBody User user) {
this.userService.save(user);
return user;
}
}
##Booting Spring applications
That’s the Java code so far. However, this does not complete a Spring application. We need to give XML (or Java) configuration, and typically, we will turn this into a Java EE web application. We shall need two ApplicationContext
s, one created by the ContextLoaderListener
and one loaded by the DispatcherServlet
. The ApplicationContext
(the WebApplicationContext
to be precise!) loaded by the DispatcherServlet
sees every bean in the headless ApplicationContext
loaded by the ContextLoaderListener
.
To actually “boot” a Spring Web application, we prepare the WEB-INF/web.xml
. Our favourite servlet container then reads this file and creates the components that ultimately prepare the two application contexts. The servlet container also directs incoming HTTP requests to our DispatcherServlet and the dispatcher servlet routes the requests though the innards of our application.
#Akka & Spray
Let’s take a look at how we would structure the same application in Scala and Akka; using Spray to build the RESTful API tier. We are going to keep on separating the tiers as we did in Spring application, except now our UserService
is going to be the UserActor
and its interface is going to be the set of messages it handles and the set of replies it handles. So, turning the interface UserService
to the messages, we arrive at:
case class GetUser(id: Long)
case class Save(user: User)
case class FindAll()
The UserActor
will react to these messages by replying to the sender of the message with the appropriate response. This is the perfect opportunity to improve & refine the responses:
-
GetUser(id: Long)
replies withOption[User]
-
Save(user: User)
replies withEither[Exception, User]
-
FindAll()
replies withList[User]
This allows us to complete the implementation of the actor (and, in the future, the higher-order kinds of the responses will lend themselves nicely to interesting shapeless or scalaz code).
class UserActor extends Actor {
def receive = {
case GetUser(id) =>
// load the user, reply with None or Some(user)
val user: Option[User] = ...
sender ! user
case FindAll() =>
// find all users
val users: List[User] = ...
sender ! users
case Save(user) =>
// persist the user
sender ! Right(user)
}
}
So, we have the middle tier of our application. The Spring interface’s methods map to the messages that the actors process; the parameters of the methods map to the values the messages carry; the return types map to the response types. Onwards to the controllers.
Just like the services, even the controllers are ultimately actors. These actors react to the incoming HTTP requests, perform whatever processing is required and then write to the response. Just like Spring’s controllers, Akka & Spray allows you to write the API tier without worrying about the underlying HTTP infrastructure. We construct Route
s and then give these Route
s to the Spray actors that handle the HTTP requests.
class UserService(implicit actorSystem: ActorSystem)
extends Directives
with LiftJsonSupport
with DefaultUnmarshallers
with DefaultMarshallers {
// the name of the userActor (see trait Core)
def userActor = actorSystem.actorFor("/user/user")
val route =
path("users") {
get {
path(LongNumber) { id =>
completeWith( (userActor ? GetUser(id)).mapTo[Option[User]] )
} ~
path(Slash) {
completeWith( (userActor ? FindAll()).mapTo[List[User]] )
}
} ~
put {
content(as[User]) { user =>
completeWith( (userActor ? Save(user)).mapTo[Either[Exception, User]] )
}
}
}
}
Notice the naming: we called this the UserService
—by service here we mean functionality available to others. So, in Spray, the term service usually refers to the equivalent of Spring controllers. The controllers interpret the values of the HTTP requests, invoke the middle tier (our UserActor
) and write to the responses.
##Booting Akka & Spray applications
However, who or where creates these actors? In the Spring world, we’d sprinkle them with the @Component
annotation and ask the Spring Framework to manage them for us. Akka does not have such annotation; moreover, the actors are not usually singletons! We need to create them ourselves. We shall define components that wrap our middle tier and our API tier.
trait Core {
implicit def actorSystem: ActorSystem
actorSystem.actorOf(Props[UserActor], "user")
}
This trait defines the actors that make up the core of our system—in this example, it is the single UserActor
. This is therefore rough equivalent of Spring’s JavaConfiguration or, even more loosely, the XML configuration file.
Next, we create a component that wraps the APIs
trait API {
this: Core =>
val routes = new UserService().route :: Nil
val svc: Route => ActorRef =
route => actorSystem.actorOf(Props(new HttpService(route)))
val rootService = actorSystem.actorOf(
props = Props(new RootService(
svc(routes.head),
routes.tail.map(svc):_*
)),
name = "root-service"
)
}
The rough mapping to the Spring world is that the Core
trait defines the headless ApplicationContext
and that the API trait defines the WebApplicationContext
. To put it yet another way, the components created in the Core
trait are completely independent of any APIs; the components in the Api
trait depends on the components in the Core
trait and provide the RESTful endpoints.
Finally, we can use Spray-can to boot the components and wrap them in real HTTP server:
trait Web {
this: Api with Core =>
val ioWorker = new IoWorker(actorSystem).start()
val sprayCanServer = actorSystem.actorOf(
Props(new HttpServer(ioWorker, MessageHandlerDispatch.SingletonHandler(
actorSystem.actorOf(Props(new SprayCanRootService(rootService)))))),
name = "http-server"
)
sprayCanServer ! HttpServer.Bind("localhost", 8080)
actorSystem.registerOnTermination {
ioWorker.stop()
}
}
object Main extends App {
implicit val system = ActorSystem("App")
class Application(val actorSystem: ActorSystem)
extends Core with Api with Web
new Application(system)
sys.addShutdownHook {
system.shutdown()
}
}
Notice that our Akka/Spray application starts with equivalent of public static void main(String[] args)
! There is no need for a servlet container of application server (but there is the option of having one, of course!).
#The mapping
#The crucial difference
I cannot go on any longer ignoring the elephant in the room!
So far, you might think that the only difference is in the naming and in the code we write to create the components. In Spring, we have the various ApplicationContexts
with our components “living” in them; in Akka, we have to assemble the application ourselves. No, the crucial difference is that Akka applications are asynchronous by nature. Every operation is performed asynchronously, with the implied hope that by the time someone asks for the result, the underlying operation will have finished. This also explains the reason why Akka applications must run in Servlet 3.0 enabled container or in their own container, such as Spray-can.