Scala4sling

  • 1,064 views
Uploaded on

 

More in: Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,064
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
18
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Scala for Sling Building RESTful Web Applications with Scala for Sling http://people.apache.org/~mduerig/scala4sling/ Michael Dürig Day Software AG 10080 LOGO SPEAKER‘S COMPANY
  • 2. 2 AGENDA > Introduction > What is Apache Sling? > What is Scala? > Scala for Sling > Summary and questions
  • 3. 3 Introduction > Michael Dürig – Developer for Day Software – http://michid.wordpress.com/ > Michael Marth – Technology Evangelist for Day Software – http://dev.day.com/
  • 4. 4 Overview > What to expect – Proof of concept – Experimental code > What not to expect – Product showcase, tutorial – Live coding (demo code available from http://people.apache.org/~mduerig/scala4sling/) > Prerequisites – Basic understanding of Java content repositories (JCR) – Prior exposure to Scala a plus
  • 5. 5 AGENDA > Introduction > What is Apache Sling? > What is Scala? > Scala for Sling > Summary and questions
  • 6. 6 Sling builds on JCR > Web application framework for JCR – JCR (JSR-170/JSR-283): Apache Jackrabbit – OSGi-based: Apache Felix – http://incubator.apache.org/sling/ > Scriptable application layer for JCR – JSR-223: Scripting for the Java platform > REST over JCR – Content resolution for mapping request URLs to JCR nodes – Servlet resolution for mapping JCR nodes to request handlers (i.e. scripts)
  • 7. 7 URL decomposition GET /forum/scala4sling.thread.html
  • 8. 8 URL decomposition GET /forum/scala4sling.thread.html Repository
  • 9. 9 URL decomposition GET /forum/scala4sling.thread.html Repository Repository path
  • 10. 10 URL decomposition GET /forum/scala4sling.thread.html Repository Repository path Application selection
  • 11. 11 URL decomposition GET /forum/scala4sling.thread.html Repository Repository path Script selection Application selection
  • 12. 12 AGENDA > Introduction > What is Apache Sling? > What is Scala? > Scala for Sling > Summary and questions
  • 13. 13 Scala builds on the JVM > Multi-paradigm language for the JVM – Conceived by Martin Odersky and his group (EPFL, Lausanne) – Fully interoperable with Java – IDE plugins for Eclipse and IntelliJ IDEA – http://www.scala-lang.org/ > Concise, elegant, and type safe – Touch and feel of a genuine scripting language – Smoothly integrates object oriented and functional features – Great for creating DSLs
  • 14. 14 Type inference: the scripting touch > Values val x = 42 // x has type Int val s = x.toString // s has type String val q = s.substring(s) // type mismatch; found String, required Int > Type parameters class Pair[S, T](s: S, t: T) def makePair[S, T](s: S, t: T) = new Pair(s, t) val p = makePair(42.0, "Scala") // p has type Pair[Double, String]
  • 15. 15 Type inference: the scripting touch > Values val x = 42 // x has type Int val s = x.toString // s has type String val q = s.substring(s) // class Pair<S, T> type mismatch; found String, required Int { public final S s; public final T t; > Type parameters public Pair(S s, T t) { super(); class Pair[S, T](s: S, t: =T) this.s s; def makePair[S, T](s: S, t: t; = new Pair(s, t) this.t = T) } val p = makePair(42.0, "Scala") // p has type Pair[Double, String] } public <S, T> Pair<S, T> makePair(S s, T t) { return new Pair<S, T>(s, t); } public final Pair<Double, String> p = makePair(42.0, "Scala");
  • 16. 16 XML <pre>literals</pre> > HTML? Scala! val title = "Hello Jazoon 09" println { <html> <body> <h1>{ title }</h1> { (for (c <- title) yield c) .mkString(" ") } </body> </html> }
  • 17. 17 XML <pre>literals</pre> > HTML? Scala! val title = "Hello Jazoon 09" println { <html> println { <body> Elem(null, "html", Null, TopScope, <h1>{ title }</h1> "body", Null, TopScope, Elem(null, { Elem(null, "h1", Null, TopScope, (for (c <- title) yield c) Text(title) .mkString(" ") ), } Text( </body> (for (c <- title) yield c) </html> .mkString(" ") } ) ) ) }
  • 18. 18 XML <pre>literals</pre> > HTML? Scala! val title = "Hello Jazoon 09" println { <html> <body> <h1>{ title }</h1> { (for (c <- title) yield c) .mkString(" ") } </body> <html> </html> <body> } <h1>Hello Jazoon 09</h1> H e l l o J a z o o n 0 9 </body> </html>
  • 19. 19 Implicits: pimp my library > Implicit conversion implicit def translate(s: String) = new { def toGerman = s match { case "Example" => "Beispiel" // ... case _ => throw new Exception("No translation for " + s) } } val german = "Example".toGerman > Déjà vu? – Similar to extension methods in C# – Similar to conversion constructors in C++ – Equivalent to type classes in Haskell
  • 20. 20 Objects are functions are objects… > Object as function object Twice { def apply(n: Int) = 2*n } println(Twice(21)) // prints 42 > Function as object def twice(n: Int) = 2*n val doubler = twice(_) println(doubler(21)) // prints 42
  • 21. 21 AGENDA > Introduction > What is Apache Sling? > What is Scala? > Scala for Sling > Summary and questions
  • 22. 22 Demo application: Forum
  • 23. 23 Forum: html.scala package forum { object html { import html_Bindings._ // ... println { <html> <body> Welcome to the { currentNode("name") } forum &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } { ThreadOverview.render(currentNode) } </body> </html> } }
  • 24. 24 Forum: html.scala GET /forum.html package forum { object html { import html_Bindings._ // ... println { <html> <body> Welcome to the { currentNode("name") } forum &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } { ThreadOverview.render(currentNode) } </body> </html> } }
  • 25. 25 Forum: html.scala package forum { object html { Put Sling variables into scope import html_Bindings._ (currentNode, request, response, etc.) // ... println { <html> <body> Welcome to the { currentNode("name") } forum &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } { ThreadOverview.render(currentNode) } </body> </html> } }
  • 26. 26 Forum: html.scala package forum { object html { /** import html_Bindings._ // ... * Print out an object followed by a new line character. * @param x the object to print. */ println { def println(x: Any): Unit = out.println(x) <html> <body> Welcome to the { currentNode("name") } forum &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } { ThreadOverview.render(currentNode) } </body> </html> } }
  • 27. 27 Forum: html.scala package forum { object html { /** import html_Bindings._ // ... * Print out an object followed by a new line character. * @param x the object to print. */ println { def println(x: Any): Unit = out.println(x) <html> <body> Welcome to the { currentNode("name") } forum &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } { ThreadOverview.render(currentNode) } </body> </html> } }
  • 28. 28 Forum: html.scala package forum { object html { import html_Bindings._ // implicit def rich(node: Node) = new { ... def apply(property: String) = node.getProperty(property).getString ... println { } <html> <body> Welcome to the { currentNode("name") } forum &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } { ThreadOverview.render(currentNode) } </body> </html> } }
  • 29. 29 Forum: thread overview object ThreadOverview { // imports omitted def render(node: Node) = emptyUnless(node.hasNodes) { <h1>threads</h1> <ul>{ node.nodes map toListItem }</ul> } private def toListItem(node: Node) = { <li> { node("subject") } (<a href={ node.path + ".thread.html"}>show thread</a>) </li> } }
  • 30. 30 Forum: thread overview object ThreadOverview { def emptyUnless(condition: Boolean)(block: => NodeSeq) = // imports omitted if (condition) block else Empty def render(node: Node) = emptyUnless(node.hasNodes) { <h1>threads</h1> <ul>{ node.nodes map toListItem }</ul> } private def toListItem(node: Node) = { <li> { node("subject") } (<a href={ node.path + ".thread.html"}>show thread</a>) </li> } }
  • 31. 31 Forum: form data object ThreadNewForm { def render = { <h1>start a new thread</h1> <form action="/content/forum/*" method="POST" enctype="multipart/form-data"> subject <input name="subject" type="text" /> <textarea name="body"></textarea> logo <input name="logo“ type="file" /> <input type="submit" value="save" /> <input name=":redirect" value="/content/forum.html" type="hidden" /> </form> } }
  • 32. 32 Forum: form data object ThreadNewForm { def render = { <h1>start a new thread</h1> <form action="/content/forum/*" method="POST" enctype="multipart/form-data"> subject <input name="subject" type="text" /> <textarea name="body"></textarea> logo <input name="logo“ type="file" /> <input type="submit" value="save" /> <input name=":redirect" value="/content/forum.html" type="hidden" /> </form> } }
  • 33. 33 Forum: form data object ThreadNewForm { def render = { POST should add new child node to /content/forum/ <h1>start a new thread</h1> <form action="/content/forum/*" method="POST" enctype="multipart/form-data"> subject <input name="subject" type="text" /> <textarea name="body"></textarea> logo <input name="logo“ type="file" /> <input type="submit" value="save" /> <input name=":redirect" value="/content/forum.html" type="hidden" /> </form> } }
  • 34. 34 Forum: form data object ThreadNewForm { def render = { POST should add new child node to /content/forum/ <h1>start a new thread</h1> <form action="/content/forum/*" with properties subject and body ... method="POST" of enctype="multipart/form-data"> type String, subject <input name="subject" type="text" /> <textarea name="body"></textarea> logo <input name="logo“ type="file" /> <input type="submit" value="save" /> <input name=":redirect" value="/content/forum.html" type="hidden" /> </form> } }
  • 35. 35 Forum: form data object ThreadNewForm { def render = { POST should add new child node to /content/forum/ <h1>start a new thread</h1> <form action="/content/forum/*" with properties subject and body ... method="POST" of enctype="multipart/form-data"> type String, subject <input name="subject" and a child node logo of type nt:file. ... type="text" /> <textarea name="body"></textarea> logo <input name="logo“ type="file" /> <input type="submit" value="save" /> <input name=":redirect" value="/content/forum.html" type="hidden" /> </form> } }
  • 36. 36 Forum: form data object ThreadNewForm { def render = { <h1>start a new thread</h1> <form action="/content/forum/*" method="POST" enctype="multipart/form-data"> subject <input name="subject" type="text" /> <textarea name="body"></textarea> logo <input name="logo“ type="file" /> <input type="submit" value="save" /> <input name=":redirect" value="/content/forum.html" type="hidden" /> </form> } Redirect to forum.html on success }
  • 37. 37 Extracting form fields > Sling post servlet – No action required: used by default – Creates nodes and properties for form fields – Tweaked with hidden form fields – Sensible defaults > Custom POST request handler – Write a script POST.scala – Process form fields in code – Full control over request processing
  • 38. 38 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... val node = addNodes(session.root, newPath) node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 39. 39 Forum: custom POST request handler package forum { POST /forum.html object POST { import POST_Bindings._ // ... val node = addNodes(session.root, newPath) node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 40. 40 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... Add node for this post val node = addNodes(session.root, newPath) node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 41. 41 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... Set properties for body and subject val node = addNodes(session.root, newPath) node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 42. 42 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... val node = addNodes(session.root, newPath) If the request contains a logo node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 43. 43 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... val node = addNodes(session.root, newPath) If the request contains a logo node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) ... add a child node logo of type nt:file if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 44. 44 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... val node = addNodes(session.root, newPath) If the request contains a logo node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) ... add a child node logo of type nt:file if (request("logo") != "") { val logoNode = node.addNode("logo", set properties jcr:lastModified, ... and "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") jcr:mimeType and jcr:data val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) } session.save response.sendRedirect(request(":redirect")) } }
  • 45. 45 Forum: custom POST request handler package forum { object POST { import POST_Bindings._ // ... val node = addNodes(session.root, newPath) node.setProperty("body", request("body")) node.setProperty(“subject", request(“subject")) if (request("logo") != "") { val logoNode = node.addNode("logo", "nt:file") val contentNode = logoNode.addNode("jcr:content", "nt:resource") val logo = request.getRequestParameter("logo") contentNode.setProperty("jcr:lastModified", Calendar.getInstance) contentNode.setProperty("jcr:mimeType", logo.getContentType) contentNode.setProperty("jcr:data", logo.getInputStream) Save changes and send redirect } session.save response.sendRedirect(request(":redirect")) } }
  • 46. 46 Forum: unit testing object html_Bindings extends MockBindings { override def currentNode = new MockNode with MockItem override def request = new MockSlingHttpServletRequest with MockHttpServletRequest with MockServletRequest } object Test extends Application { forum.html }
  • 47. 47 Forum: unit testing object html_Bindings extends MockBindings { override def currentNode = new MockNode with MockItem override def request = new MockSlingHttpServletRequest with MockHttpServletRequest with MockServletRequest } object Test extends Application { forum.html }
  • 48. 48 Forum: unit testing object html_Bindings extends MockBindings { override def currentNode = new MockNode with MockItem override def request = new MockSlingHttpServletRequest with MockHttpServletRequest with MockServletRequest } object Test extends Application { forum.html }
  • 49. 49 Forum: unit testing <html> <head> <link href="/apps/forum/static/blue.css" rel="stylesheet"></link> object html_Bindings extends MockBindings { </head> <body> override def currentNode = new MockNode <div id="Header"> Welcome to the forum with MockItem &mdash; Wed Jun 17 17:12:48 CEST 2009 </div> <div id="Menu"> override def <p>search all threads: request = new MockSlingHttpServletRequest <form action="/content/forum.search.html" with MockHttpServletRequest enctype="multipart/form-data" method="GET"> <input value="" type="text" size="10" name="query"></input> with MockServletRequest <input value="search" type="submit"></input> } </form> </p> </div> <div id="Content"> object Test extends Application { <h1>start a new thread</h1> <span id="inp"> forum.html <form action="/content/forum/*" enctype="multipart/form-data" method="POST"> } <p>subject</p> <p><input type="text" name="subject"></input></p> <p><textarea name="body"></textarea></p> <p>logo</p> <p><input type="file" name="logo"></input></p> <p><input value="save" type="submit"></input></p> <input value="/content/forum.html" type="hidden" name=":redirect"></input> </form> </span> </div> </body> </html>
  • 50. 50 AGENDA > Introduction > What is Apache Sling? > What is Scala? > Scala for Sling > Summary and questions
  • 51. 51 Conclusion > Advantages – Scala! – No language boundary – Tool support (i.e. IDE, ScalaDoc, safe refactoring, unit testing) > Disadvantages – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 52. 52 Conclusion > Advantages – Scala! – No language boundary – Tool support (i.e. IDE, ScalaDoc, safe refactoring, unit testing) > Disadvantages – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 53. 53 Conclusion > Advantages – Scala! <p>Welcome to { currentNode("name") } forum</p> – No language boundary &mdash; { Calendar.getInstance.getTime } { ThreadNewForm.render } – Tool support (i.e. IDE, ScalaDoc, { ThreadOverview.render(currentNode) } safe refactoring, unit testing) > Disadvantages – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 54. 54 Conclusion > Advantages – Scala! – No language boundary – Tool support (i.e. IDE, ScalaDoc, safe refactoring, unit testing) > Disadvantages – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 55. 55 Conclusion > Advantages – Scala! – No language boundary – Tool support (i.e. IDE, ScalaDoc, safe refactoring, unit testing) > Disadvantages – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 56. 56 Conclusion > Advantages – Scala! – No language boundary object html_Bindings { – Tool support (i.e. IDE, ScalaDoc, def currentNode = new MockNode safe refactoring, unit testing) ... } object Test extends Application { > Disadvantages forum.html } – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 57. 57 Conclusion > Advantages – Scala! – No language boundary – Tool support (i.e. IDE, ScalaDoc, safe refactoring, unit testing) > Disadvantages – IDE support shaky, improves quickly though – Not much refactoring support as of today
  • 58. 58 Summary > Apache Sling – Great for building RESTful web applications – Pluggable scripting support (JSR-223) > Scala – Great for scripting – Supports «on the fly» templates through XML literals – Easy unit testing
  • 59. Michael Dürig michael.duerig@day.com Michael Marth michael.marth@day.com Day Software AG http://www.day.com/ References: • Scala for Sling: http://people.apache.org/~mduerig/scala4sling/ • The Scala programming language: http://www.scala-lang.org/ • Apache Sling: http://incubator.apache.org/sling/ LOGO SPEAKER‘S COMPANY