Scalatra - Massimiliano Dessì (Energeya)

2,123 views

Published on

Scala meetup - Milan, 25 May 2013

Dopo anni di sviluppo Web in Java con le Servlet come base, vediamo come poter sviluppare usando le stesse solide fondamenta con Scala. Scalatra è un toolkit minimale che ci permette di iniziare ad approcciare Scala sul web attraverso le Servlet permettendoci di integrare Scala (o di migrare) in delle WebApp già esistenti. Vedremo in questo quickie come delle funzionalità generiche ed avanzate, implementate con SpringMVC, possono essere implementate con Scalatra.

Published in: Technology, Business
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,123
On SlideShare
0
From Embeds
0
Number of Embeds
552
Actions
Shares
0
Downloads
9
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Scalatra - Massimiliano Dessì (Energeya)

  1. 1. Scala Italy 25 may 2013Scalatra comparison with SpringMVCMassimiliano Dessì1Monday, May 27, 13
  2. 2. GDG SardegnaSpeakerMax has more than 13 years of experience in programming. He’s aproud father of three.Manager of GTUG Sardegna, Founder of SpringFramework IT,co-founder of Jug Sardegna.Author of Spring 2.5 AOP.He works in Energeya as a Product Architect and lives in Cagliari,Italy.2Monday, May 27, 13
  3. 3. GDG SardegnaScalatra - SpringMVCCRUD HTMLRESTJSONRoutingTemplateSecurityAgenda3Monday, May 27, 13
  4. 4. GDG SardegnaSpring , Since 2004 - Java Full Stack - Lot of people involvednow property of VMwareScalatra , Since 2009 - Scala Http Microframework (DSL)the BBC, LinkedIn,the Guardian,games website IGN,UK governmentrely on Scalatra.Some info4Monday, May 27, 13
  5. 5. GDG SardegnaRESTclass JellyBeans extends ScalatraServlet {get("/jellybs/:id") { ... }post("/jellybs") { ... }put("/jellybs/:id") { ... }delete("/jellybs/:id") { ... }}For browser add PUT & DELETE support client sideX-HTTP-METHOD-OVERRIDE or _method=put _method=delete in the post bodyclass JellyBeansBrowser extends ScalatraFilter with MethodOverride5Monday, May 27, 13
  6. 6. GDG SardegnaMassimiliano DessìRoutesclass JellyBeans extends ScalatraServlet {get("/jellybs/:id") {val id:Int = params.getOrElse("id", halt(400)).toInt...}get("/jellybs/download/*.*") { //with wildcard...}get("""^/j(.*)/f(.*)""".or) { //REGEX...}post("/jellybs", request.getRemoteHost == "127.0.0.1",request.getRemoteUser == "admin") {....}6Monday, May 27, 13
  7. 7. GDG SardegnaREST@Controllerpublic class JellyBeansController{@RequestMapping(value = "/jellybs/{id}", method = RequestMethod.GET)@ResponseStatus(HttpStatus.OK) //Http 200public @ResponseBody JellyB getItem(@PathVariable("id") Integer id) {...}@RequestMapping(value = "/jellybs", method = RequestMethod.POST) //Http 201@ResponseStatus(HttpStatus.CREATED) //Http 201public void createJelly(HttpServletRequest req, HttpServletResponse res,@RequestBody JellyB item) { ... }@RequestMapping(value = "/jellybs/{id}", method = RequestMethod.PUT)@ResponseStatus(HttpStatus.NO_CONTENT) //Http 204public void updateItem(@RequestBody JellyB item, @PathVariable("id") Integer id){ ... }@RequestMapping(value = "/items/{id}", method = RequestMethod.DELETE)@ResponseStatus(HttpStatus.NO_CONTENT)//204public void deleteItem(@PathVariable("id") String id) { ... }}For browser add PUT & DELETE support client sideX-HTTP-METHOD-OVERRIDE or _method=put _method=delete in the post body<filter><filter-name>hiddenMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter>7Monday, May 27, 13
  8. 8. GDG SardegnaRoutesclass JellyBeans extends ScalatraServlet {get("/jellybs/:id") {val id:Int = params.getOrElse("id", halt(400)).toInt...}get("/jellybs/download/*.*") { //with wildcard...}get("""^/j(.*)/f(.*)""".or) { //REGEX...}post("/jellybs", request.getRemoteHost == "127.0.0.1",request.getRemoteUser == "admin") {....}8Monday, May 27, 13
  9. 9. GDG SardegnaHandlerMapping@Controllerpublic class JellyBeansController{@RequestMapping(value="/jelly.pdf",method = RequestMethod.GET)public ModelAndView pdf(HttpServletRequest req,@RequestParam String id){...}@RequestMapping(value = "/jellybs/{id}", method = RequestMethod.GET)public String getItem(@PathVariable("id") Integer id) {...}@RequestMapping(value = "/jellybs", method = RequestMethod.POST)public void createJelly(@ModelAttribute(”jelly”) JellyB jelly) {...}}DefaultAnnotationHandlerMappingSimpleUrlHandlerMappingControllerClassNameHandlerMapping9Monday, May 27, 13
  10. 10. GDG SardegnaFilterclass JellyBeans extends ScalatraServlet {before() {db.acquireConnecioncontentType="text/html"}get("/") {val menu = db.findWelcome()templateEngine.layout("index.ssp", menu)}after() {db.releaseConnection}}Like Servlet Filter(or Aspect Oriented Programming)You can add logic before or theroutes10Monday, May 27, 13
  11. 11. GDG SardegnaHandler Interceptorpublic interface HandlerInterceptor{//before call method’s controllerboolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler)throws Exception;//after call method’s controllervoid postHandle(HttpServletRequest req, HttpServletResponse res, Object handler,ModelAndView mav) throws Exception;//after view render()void afterCompletition(HttpServletRequest req, HttpServletResponse res, Objecthandler, Exception ex) throws Exception;}Handler mapped on all controllers (as a property of Handlermapping config)or mapped on url path with <mvc:interceptors> tagsLike Servlet Filter(or Aspect Oriented Programming)You can add logic before or afterthe controllers methods11Monday, May 27, 13
  12. 12. GDG SardegnaHandlersHandlers are top level methods for http routinesclass JellyBeans extends ScalatraServlet {notFound {<h1>Not found</h1>}halt(status = 301, headers =Map("Location" -> "http://www.codemotion.com/"))get("/jellybs/names/*") {"Name not found!"}}12Monday, May 27, 13
  13. 13. GDG SardegnaHandlersclass JellyBeans extends ScalatraServlet {get("/jellybs/names/:who") {params("who") match {case "Cherry" => "Found Cherry!"case _ => pass() /* call the next matching route route, routes arematched from bottom up*/}}get("/jellybs/download/:id") {jellyBeanService.find(params("id")) match {case Some(jellybean) => Ok(jellybean)case None => NotFound("Sorry, jellybean not found")}}}13Monday, May 27, 13
  14. 14. GDG SardegnaRequest Response & Friendsclass JellyBeans extends ScalatraServlet {get("/jellybs/shows/:id") {//access to request,response, session and paramsrequest.body //request body as a stringrequest.cookies // cookie maprequest.isAjax // is ajaxRequestrequest.getSession // HttpServletSessionrequest.locale // user localeresponse.getOutputStream //response outputstreamservletContext.get("myIntParam") //servlet contextval idString = params("id")val id = params.getAs[Int]("id")//val id = params.getOrElse("id", halt(500)) //another way....}}14Monday, May 27, 13
  15. 15. GDG SardegnaRequest Response & Friendsclass JellyBeans extends ScalatraServlet {get("/jellybs/*/conf/*") {// Matches "GET /jellybs/one/conf/two"multiParams("splat") // Seq("one", "two")//wildcard accessible with key splat}get("""^/jelly(.*)/fla(.*)""".r) {// Matches "GET /jellybs/flavor"multiParams("captures") // == Seq("bs", "vor")}}15Monday, May 27, 13
  16. 16. GDG SardegnaGzipclass GzipJellyBeans extends ScalatraServlet with GZipSupport{get("/") {<html><body><h1>This is<a href="http://en.wikipedia.org/wiki/Sparta">Sparta</a>!</h1></body></html>}}16Monday, May 27, 13
  17. 17. GDG SardegnaFlash messageclass FlashServlet extends ScalatraServlet with FlashMapSupport{post("/jellybs/create") {flash("notice") = "jellybean created successfully"redirect("/home") //redirect}get("/home") {ssp("/home") //Scala Server Pages}17Monday, May 27, 13
  18. 18. GDG SardegnaExceptionResolvers<bean id="exceptionResolver"class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><props><prop key="org.springframework.web.HttpSessionRequiredException">welcome</prop>...</props></property></bean>or in a annotated @Controller class@ExceptionHandlerpublic String requestParamNotPresent(HttpMediaTypeException ex)18Monday, May 27, 13
  19. 19. GDG SardegnaBindingTo avoid manual binding from http and our Object Scalatra provide a binding modulecase class MyClass(id: Integer, name: String)post("/myroute") {val cmd = command[CreateMyClassCommand]...}19Monday, May 27, 13
  20. 20. GDG SardegnaBindingUnder the hoodabstract class MyClassCommand[S](implicit mf: Manifest[S])extends ModelCommand[S] with JsonCommand {  implicit def todoStringValidators(b: FieldDescriptor[String]) =new MyClassStringValidations(b)}class CreateMyClassCommand extends MyClassCommand[MyClass] {protected implicit val jsonFormats = DefaultFormatsval name: Field[String] = asType[String]("name").notBlank.minLength(3)}20Monday, May 27, 13
  21. 21. GDG SardegnaBindingUnder the hoodclass MyClassStringValidations(b: FieldDescriptor[String]) {def startsWithCap(message: String = "%s must start with a capital letter.") =b.validateWith(_ =>     _ flatMap {new PredicateValidator[String](b.name,"""^[A-Z,0-9]""".r.findFirstIn(_).isDefined,message).validate(_)   )})}21Monday, May 27, 13
  22. 22. GDG SardegnaCommandObject@RequestMapping(value = "/items/{id}", method = RequestMethod.PUT)@ResponseStatus(HttpStatus.NO_CONTENT)public String updateItem(JellyB item) {...}Spring bind the request body on the object used in the method (JellyB)Objects Validation with jsr 303 annotation or with Validator Spring InterfaceError message with JSP/JSTL tags22Monday, May 27, 13
  23. 23. GDG SardegnaUploadimport org.scalatra.ScalatraServletimport org.scalatra.servlet.FileUploadSupportimport javax.servlet.annotation.MultipartConfig@MultipartConfig(maxFileSize=10*1024)trait StorageRoutes extends FileUploadSupport {self: ScalatraServlet =>}23Monday, May 27, 13
  24. 24. GDG SardegnaJSONAutomatic Serialization and deserialization of any Case classobject JSONServlet extends ScalatraServlet with JacksonJsonSupport{case class JellyBean(id: Int, name: String, flavor:String)protected implicit val jsonFormats: Formats = DefaultFormatsbefore() {contentType = formats("json")}....24Monday, May 27, 13
  25. 25. GDG SardegnaActorsThe routes can returns a Futureclass MyActorServlet(system:ActorSystem, myActor:ActorRef)extends ScalatraServlet with FutureSupport {import _root_.akka.pattern.askimplicit val timeout = Timeout(10)protected implicit def executor: ExecutionContext = system.dispatcherget("/async") {myActor ? "Do stuff and give me an answer"}get("/fire-forget") {myActor ! "Hey, you know what?"Accepted() //if you do not want return a future}}25Monday, May 27, 13
  26. 26. GDG SardegnaActorsget("/async") {myActor ? "Do stuff and give me an answer"}get("/fire-forget") {myActor ! "Hey, you know what?"Accepted() //if you don’t want return a future}class CodemotionActor extends Actor {def receive = {case "Do stuff and give me an answer" => sender ! "The answer is 42"case "Hey, you know what?" => println("Yeah I know...")}}}26Monday, May 27, 13
  27. 27. GDG SardegnaAkkaAkka start in the Bootstrap of Scalatraclass ScalatraBootstrap extends LifeCycle {val system = ActorSystem()val codemotionActor = system.actorOf(Props[CodemotionActor])override def init(context: ServletContext) {context.mount(new JSONServlet, "/jellybs/*")context.mount(new FrontServlet, "/template/*")context.mount(new MyActorServlet(system, codemotionActor), "/actors/*")}override def destroy(context:ServletContext) {system.shutdown()}}27Monday, May 27, 13
  28. 28. GDG SardegnaDeploymentScalatra is based on regular Java Servlet 3.0it can start from:Standalone from jetty embeddedFrom Servlet containerHerokuJelasticCloudBeesGAE (not out of the box )web.xml<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><listener><listener-class>org.scalatra.servlet.ScalatraListener</listener-class></listener><servlet-mapping><servlet-name>default</servlet-name><url-pattern>/img/*</url-pattern><url-pattern>/css/*</url-pattern><url-pattern>/js/*</url-pattern><url-pattern>/assets/*</url-pattern></servlet-mapping></web-app>28Monday, May 27, 13
  29. 29. GDG SardegnaJSONAutomatic Serialization and deserialization of any Case classobject JSONServlet extends ScalatraServlet with JacksonJsonSupport{...get("/jellybs/all"){jellyBeanRepo.all //from class to json}post("/jellybs/create") {val jb = parsedBody.extract[JellyBean] //from json to class...}}29Monday, May 27, 13
  30. 30. GDG SardegnaJSON30JSON with third party lib es Jackson@JsonCreatorpublic JellyB(Map map) {...}@JsonAnyGetterpublic Map toMap() {...}Automatic bindingpublic @ResponseBody JellyB getItem(...)public void updateItem(@RequestBody JellyB item)Monday, May 27, 13
  31. 31. GDG SardegnaTemplateHtml inline,ScalateTwirl (Play2 template)ScalatemeanSSP (Scala Server Page)ScamlMustacheJade31Monday, May 27, 13
  32. 32. GDG SardegnaTemplateclass ScalateServlet extends ScalatraServlet with ScalateSupport{get("/"jellybeans/ssp) {contentType="text/html"ssp("/index", "foo" -> "uno", "bar" -> "two")// the layout used it’s WEB-INF/layouts/default.ssp}get("/jellybeans/jade") {jade("/index", "layout" -> "", "foo" -> "one", "bar" -> "two")// render without a layout.}get("/jellybeans/direct") {templateEngine.layout("/jellybeans/index.ssp")//direct invoking of scalate}32Monday, May 27, 13
  33. 33. GDG SardegnaTemplateSSP<%@ var body: String %><%@ var title: String = "Some Default Title" %><%@ var head: String = "" %><html><head><title>${title}</title><%-- page specific head goes here --%>${unescape(head)}</head><body><p>layout header goes here...</p>${unescape(body)}<p>layout footer goes here...</p></body></html>33Monday, May 27, 13
  34. 34. GDG SardegnaTemplateJADE!!! 5html(lang="en")headtitle= pageTitle:javascriptif (foo) {bar()}bodyh1 Jade - node template engine#container- if (youAreUsingJade)p You are amazing- elsep Get on it!:coffeescriptalert "Hello, Coffee!"34Monday, May 27, 13
  35. 35. GDG SardegnaTemplateMustacheHello {{name}}You have just won ${{value}}!{{#in_ca}}Well, ${{taxed_value}}, after taxes.{{/in_ca}}35Monday, May 27, 13
  36. 36. GDG SardegnaTemplateSCAML!!! XML!!!%html%head%title Myspace%body%h1 I am the international space station%p Sign my guestbook36Monday, May 27, 13
  37. 37. GDG SardegnaViewResolverJSP, JSTL, Tiles, FreeMarker, XSLT, PDF, Velocity, JExcelApi,JSON, Atom, RSS, JasperReportsExplicit view with different ViewResolver with different ordervalues, or with ContentTypeNegotiationViewResolvermapping different mediaTypes37Monday, May 27, 13
  38. 38. GDG SardegnaAuthentication & SecurityScalatra uses Scentry a porting of Ruby Warden authenticationclass OurAuthStrategy(protected override val app: ScalatraBase, realm: String)extends BasicAuthStrategy[User](app, realm) {protected def validate(userName: String, password: String): Option[User] = {if(userName == "myusername" && password == "secret") Some(User("myusername"))else None}protected def getUserId(user: User): String = user.id}case class User(id: String)38Monday, May 27, 13
  39. 39. GDG SardegnaAuthentication & SecurityNow we need to combine our strategy and ScentrySupporttrait AuthenticationSupport extends ScentrySupport[User] with BasicAuthSupport[User] {self: ScalatraBase =>val realm = "Scalatra Basic Auth Example"protected def fromSession = { case id: String => User(id) }protected def toSession = { case usr: User => usr.id }protected val scentryConfig = (new ScentryConfig {}).asInstanceOf[ScentryConfiguration]}39Monday, May 27, 13
  40. 40. GDG SardegnaAuthentication & SecurityNow we need to combine our strategy and ScentrySupporttrait AuthenticationSupport extends ScentrySupport[User] with BasicAuthSupport[User] {....override protected def configureScentry = {scentry.unauthenticated {scentry.strategies("Basic").unauthenticated()scentry.strategies("")}}override protected def registerAuthStrategies = {scentry.register("Basic", app => new OurBasicAuthStrategy(app, realm))}protected def validate(userName: String, password: String): Option[User] = {if(userName == "scalatra" && password == "scalatra") Some(User("scalatra"))else None}}40Monday, May 27, 13
  41. 41. GDG SardegnaAuthentication & SecurityNow we can use the Authentication for all routes defined in our Authenticated Servletsclass AuthenticatedServlet extends ScalatraServlet with AuthenticationSupport{//every route goes under authentication}Unauthenticated user will see a browser prompt login41Monday, May 27, 13
  42. 42. GDG SardegnaSpringSecurityFilterServlet to protect urls, roles and AOP intercepting based on User Roles<filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern></filter-mapping><sec:http>" " <sec:form-login login-page="/login.jsp"login-processing-url="/j_security_check"default-target-url="/"" " " always-use-default-target="true"authentication-failure-url="/loginError.jsp"/>" " <sec:logout logout-url="/logout.jsp" invalidate-session="true"logout-success-url="/login.jsp" />" " <sec:intercept-url pattern="/styles/**" filters="none"/>" " <sec:intercept-url pattern="/scripts/**" filters="none"/>" " <sec:intercept-url pattern="/img/**" filters="none"/>" " <sec:anonymous username="guest" granted-authority="ROLE_GUEST" />"42Monday, May 27, 13
  43. 43. GDG SardegnaTestTest with Specs2class FrontServletSpec extends ScalatraSpec {def is ="GET / on FrontServlet" ^"should return status 200" ! root200 ^endaddServlet(classOf[FrontServlet], "/*")def root200 = get("/") {status must_== 200}}43Monday, May 27, 13
  44. 44. GDG SardegnaQ & AGTUG Sardegna44Monday, May 27, 13
  45. 45. GDG SardegnaThanks for your attention!Massimiliano: @desmax74GTUG Sardegna45Thanks for you attention!Massimiliano: @desmax74Monday, May 27, 13

×