Spring and Web Content Management

  • 6,739 views
Uploaded on

Speakers: Daniel Lipp and Tobias Mattsson …

Speakers: Daniel Lipp and Tobias Mattsson
Want Spring seamlessly available inside a CMS? How about being able to integrate existing Spring apps into your CMS without rewriting a bunch of code? What about a robust CMS solution for Grails? Meet Magnolia, a mature open source CMS written in Java on the best of the Java stack (including Spring and Groovy.)
This session will introduce Magnolia's Spring integration and give you a tour of its architecture, key features and use. Along the way, you'll also get insights into the development of Magnolia's Spring integration, an overview of Magnolia's key features (like workflows, innovative multi-channel support and a damn fine user experience that includes touch devices), and brief tutorials on solving some key content management challenges faced by Spring developers. There will also be a quick detour into Magnolia's Groovy shell and MagLev, a Grails plugin for Magnolia.

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

Views

Total Views
6,739
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
4
Comments
0
Likes
0

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. Spring & Web Content Management Tobias Mattsson & Daniel Lipp Magnolia International © 2013 SpringOne 2GX. All rights reserved.
  • 2. 2
  • 3. 3
  • 4. 4
  • 5. Tobias Mattsson Sr. Software Engineer, Magnolia Lead developer of Magnolia’s Spring integration Spring Framework user since 2005 5
  • 6. Daniel Lipp Sr. Software Engineer, Magnolia 17 years of experience as software architect and Java developer Grateful to Spring for inspiring improvements to JEE 6
  • 7. ® 7
  • 8. 8
  • 9. @magnolia_cms #magnolia_cms 9
  • 10. Spring Spring MVC Code, beautiful code Dependency Injection Integrations Business logic 10
  • 11. CMS Security Multi-lingual User interface Image manipulation Content versioning 11
  • 12. Spring Spring MVC Code, beautiful code Dependency Injection Integrations Business logic CMS Security Multi-lingual User interface Image manipulation Content versioning 12
  • 13. USES IFRAMES LIKE IT'S 1999 13
  • 14. Magnolia + Spring = Blossom 14
  • 15. @Template or, "How Blossom is Built" 15
  • 16. CMS REQUEST CONTENT TEMPLATE 16
  • 17. CMS + Blossom REQUEST CONTENT TEMPLATE CONTROLLER MODEL VIEW 17
  • 18. @Controller @Template(id="myModule:pages/main", title="Main") public class MainTemplate {    @RequestMapping("/main")    public String render(ModelMap model) {        return "pages/main";    } } Page Template 18
  • 19. PAGES CONTAIN 0:n AREAS PAGE 19
  • 20. PAGES CONTAIN 0:n AREAS AREA A R E A PAGE AREA 19
  • 21. PAGES CONTAIN 0:n AREAS AREA COMPONENT AREAS HAVE 0:n COMPONENTS C O M P O N E N T A R E A COMPONENT PAGE Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. AREA COMPONENT Etiam porta sem malesuada magna mollis euismod. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere consectetur est at lobortis. 19
  • 22. @Controller @Template(id="myModule:pages/main",title="Main Template") public class MainTemplate {    @Controller    @Area("main")    public static class MainArea {        @RequestMapping("/main/mainArea")        public String render() {            return "areas/main";        }    ... Area Template 20
  • 23. @Controller @Template(id="myModule:components/shoppingCart",          title="Shopping Cart") @TemplateDescription("Shopping cart") public class ShoppingCartComponent {    @RequestMapping("/shoppingCart")    public String handleRequest() {        ...        return "components/shoppingCart";    } ... Component Template 21
  • 24. Views 22
  • 25. FreeMarker [@cms.area name="main" /] JSP <cms:area name="main" /> Rendering an Area 23
  • 26. FreeMarker [#list components as component]    [@cms.component content=component /] [/#list] JSP <c:forEach items="${components}" var="component">    <cms:component content="${component}" /> </c:forEach> Rendering Components in an Area 24
  • 27. About Magnolia CMS 100% Java/J2EE compliant Best of breed open technology stack, including: JSR-283 / JCR 2.0 Vaadin / GWT Servlet API HTML5 Highly Customizable 25
  • 28. Java Content Repository “… defines an abstract model and a Java API for data storage and related services commonly used by content-oriented applications.” – JSR-283 26
  • 29. Magnolia JCR API Jackrabbit Hierarchical content repository Persistence Manager or File system or Database In memory 27
  • 30. Schema-less or Strongly-typed Hierarchical Structured Queries Full-text Search Transactional Referential Integrity Locking Why JCR? Fine-grained content and large binaries Versioning Role-based security Event Notification (Observation) 28
  • 31. Dialogs 29
  • 32. GWT with server-side state Component-based UI Cross-browser compatibility (via GWT) Rich component library Fast development pace 30
  • 33. @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body"), cfg.fields.websiteLink("categoryLink").label("Link"), cfg.fields.basicUpload("image").label("Image"), cfg.fields.checkbox("inlineImage").label("Inline Image")  ); } Fluent Builder-style API 31
  • 34. @Controller @Template(title="Article", id="myModule:pages/article") public class ArticleTemplate {    @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {        tab.fields(cfg.fields.text("title").label("Title"));    } } Page Template with Dialog 32
  • 35. @Controller @Template(title="Article", id="myModule:pages/article") public class ArticleTemplate {    @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {        tab.fields(cfg.fields.text("title").label("Title"));    } } <title>${content.title}</title> <!-- In the view --> Page Template with Dialog 32
  • 36. @Controller @Template(title="Article", id="myModule:pages/article") public class ArticleTemplate { @Area("main") @Controller public static class MainArea { @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("heading").label("Heading"), cfg.fields.text("border").label("Border width") ); Area Template with Dialog 33
  • 37. @Controller @Template(title="Article", id="myModule:pages/article") public class ArticleTemplate { @Area("main") @Controller public static class MainArea { @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("heading").label("Heading"), cfg.fields.text("border").label("Border width") ); Area Template with Dialog 33
  • 38. @Controller @Template(title="Article", id="myModule:pages/article") public class ArticleTemplate { @Area("main") @Controller public static <div id="main" class MainArea { style="border:${content.border}px solid #000"> @TabFactory("Content") <h2>${content.heading}</h2> public void contentTab(UiConfig cfg, TabBuilder tab) { <c:forEach items="${components}" var="component"> tab.fields( <cms:component content="${component}" /> cfg.fields.text("heading").label("Heading"), </c:forEach> </div> cfg.fields.text("border").label("Border width") <!-- In the view --> ); Area Template with Dialog 33
  • 39. @Controller @Template(title="Text", id="myModule:components/text") public class TextComponent { @RequestMapping("/text") public String render() { return "components/text"; } @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body") Component Template with Dialog 34
  • 40. @Controller @Template(title="Text", id="myModule:components/text") public class TextComponent { @RequestMapping("/text") public String render() { return "components/text"; } @TabFactory("Content") <h1>${content.heading}</h1> public void contentTab(UiConfig cfg, TabBuilder tab) { <p>${cmsfn:decode(content).body}</p> tab.fields( <!-- In the view --> cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body") Component Template with Dialog 34
  • 41. @Controller @Template(title="YouTube Video", id="myModule:components/youtube") @TemplateDescription("Embed a YouTube video") public class YoutubeComponent { @RequestMapping("/youtube") public String render(Node node, ModelMap model) throws RepositoryException { model.put("videoId", node.getProperty("videoId").getString()); return "components/youtube"; } @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("videoId").label("Video ID") ); } } YouTube Video Component 35
  • 42. @Controller @Template(title="YouTube Video", id="myModule:components/youtube") @TemplateDescription("Embed a YouTube video") public class YoutubeComponent { @RequestMapping("/youtube") public String render(Node node, ModelMap model) throws RepositoryException { model.put("videoId", node.getProperty("videoId").getString()); return "components/youtube"; } @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { <iframe width="100%" height="400" src="//www.youtube.com/ tab.fields( embed/${videoId}" frameborder="0" allowfullscreen></iframe> cfg.fields.text("videoId").label("Video ID") <!--); the view --> In } } YouTube Video Component 35
  • 43. public abstract class BasePageTemplate { @TabFactory("Meta") public void metaTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("metaAuthor").label("Author"),     cfg.fields.text("metaKeywords").label("Keywords"),     cfg.fields.text("metaDescription").label("Description")    );  } } Dialogs and the Class Hierarchy 36
  • 44. public abstract class BasePageTemplate { @TabFactory("Meta") public void metaTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("metaAuthor").label("Author"),     cfg.fields.text("metaKeywords").label("Keywords"),     cfg.fields.text("metaDescription").label("Description")    ); <head>  } name="description" content="${content.metaDescription}"/> <meta } <meta name="keywords" content="${content.metaKeywords}" />  <meta name="author" content="${content.metaAuthor}" /> </head> <!-- In the view --> Dialogs and the Class Hierarchy 36
  • 45. @Template(id="myModule:components/bookCategory", title="Book category") @TemplateDescription("A list of books in a given category.") @Controller public class BookCategoryComponent {    @Autowired    private SalesApplicationWebService service;    @RequestMapping("/bookcategory")    public String render(ModelMap model, Node content) throws RepositoryException {        String category = content.getProperty("category").getString();        model.put("books", service.getBooksInCategory(category));        return "components/bookCategory";    } @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {        Collection<String> categories = service.getBookCategories();        tab.fields( cfg.fields.select("category").label("Category").options(categories)        );    } } Dynamic Dialog 37
  • 46. ... @TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { Collection<String> categories = service.getBookCategories(); tab.fields( cfg.fields.select("category").label("Category").options(categories)  ); } ... Dynamic Dialog 38
  • 47. ... public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("name").label("Name").required(), cfg.fields.text("email").label("Email")⏎ .validator(cfg.validators.email()) ); } ... Input Validation 39
  • 48. @Controller @Template(title="Article", id="myModule:/pages/article") public class ArticleTemplate {    ...    @Available    public boolean isAvailable(Node node) {        return node.getPath().startsWith("/articles/");    } } Page Template Availability 40
  • 49. @Controller @Area("promos") @AvailableComponentClasses({TextComponent.class,                            ShoppingCartComponent.class}) public static class PromosArea {    @RequestMapping("/main/promos")    public String render() {        return "areas/promos";    } } Available Components 41
  • 50. @Controller @Area("promos") @Inherits @AvailableComponentClasses({TextComponent.class,                            ShoppingCartComponent.class}) public static class PromosArea {    @RequestMapping("/main/promos")    public String render() {        return "areas/promos";    } } Area Inheritance 42
  • 51. SERVLET CONTAINER RENDERING FILTER RENDERING ENGINE How Blossom Works BLOSSOM DISPATCHER SERVLET CONTROLLER 43
  • 52. Magnolia Server Modules STK Custom Theme Custom module Custom module Imaging Workflow DMS Data Module JAR - templates - resources Magnolia Core 44
  • 53. Web container (Tomcat) Web Archive Magnolia CMS Filter chain Request Context CMS Security Custom filter Cache Aggregation Rendering Response Content Hierarchical content repository Rendering engine 45
  • 54. /* Standard annotations omitted */ public class ContactFormComponent { @RequestMapping(value="/contact", method=RequestMethod.GET)  public String viewForm(@ModelAttribute ContactForm contactForm) { return "components/contactForm"; } @RequestMapping(value="/contact", method=RequestMethod.POST) public String handleSubmit(@ModelAttribute ContactForm contactForm, ⏎ BindingResult result) { new ContactFormValidator().validate(contactForm, result); if (result.hasErrors()) { return "components/contactForm"; } return "redirect:/home/contact/thankyou.html"; } Form Submission 46
  • 55. But wait a minute … Caused by: org.springframework.web.util.NestedServletException: Reque java.lang.IllegalStateException at org.springframework.web.servlet.FrameworkServlet.processRequest(F at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkS at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at info.magnolia.module.blossom.render.BlossomDispatcherServlet.forw at info.magnolia.module.blossom.render.BlossomTemplateRenderer.rende ... 92 more Caused by: java.lang.IllegalStateException … the response has been sent. 47
  • 56. Controller Pre-execution PAGE C C M V Area Component C M V C M V Component Component C M V C M V 48
  • 57. Code in View <form> <blossom:pecid-input /> <input type=”text” name=”email” /> ... HTML Output <form> <input type=”hidden” name=”_pecid” value=”ff6cefa6-d958-47b1-af70-c82a414f17e1” /> <input type=”text” name=”email” /> ... CPE Implementation 49
  • 58. Spring Web MVC + Content 50
  • 59. Spring Web Flow REQUEST LANDING PAGE SPRING WEB FLOW 51
  • 60. Spring Web Flow REQUEST LANDING PAGE FORM SUBMIT? SPRING WEB FLOW 52
  • 61. Deployment & Scaling 53
  • 62. Internal Network Internet Public A Magnolia JCR Activate Author JCR Magnolia Public B Firewall Load Balancer Magnolia JCR 54
  • 63. Clustering Magnolia Author Backend Application DB JCR Magnolia Magnolia Public Public JCR JCR 55
  • 64. Dealing with Change 56
  • 65. Filter Chain in JCR 57
  • 66. <blossom:observed-bean default-class="com.sample.DiscountService" path="/modules/myModule/beans/discountService" /> Configuring Spring Beans 58
  • 67. example.com/vanity 59
  • 68. Changing Resources Live CSS FreeMarker Images Javascript Views 60
  • 69. What Do You Win? 61
  • 70. 62
  • 71. 63
  • 72. Security Maglev Shorter Learning Curve Scalability Spring Idioms Test-driven Development Staging Mobile & Touch Faster Development Code-driven Dialogs @Annotations MVC Loose Coupling Use Existing Dependency Spring Apps & Injection Components 64
  • 73. Security Maglev Shorter Learning Curve Scalability Staging Mobile & Touch Faster Development Spring Idioms Test-driven Development @Annotations MVC Questions? Code-driven Dialogs Loose Coupling Use Existing Dependency Spring Apps & Injection Components 64
  • 74. Want to Learn More? Talk to us in the exhibit hall Attend Blossom Q & A webinar on Sept. 26 http://magnolia-cms.com/blossom-qa Visit http://magnolia-cms.com/spring Dashboard for all things Spring in Magnolia 65
  • 75. Thank You! 66
  • 76. Image Credits #2: "Long Drive" by Nicholas A. Tonelli (CC-BY 2.0) #3: "Sluggish" by Nicholas A. Tonelli (CC-BY 2.0) #4: Still from "The Matrix" #7: "South Beach Miami Sunset" by Justin Ornellas (CC-BY 2.0) #14: "HBW - Magnolia Edition" by Nana B Agyei (CC-BY 2.0) #16: "Worker" designed by James Fenton from The Noun Project #16: "Database" designed by Ed Jones from The Noun Project #16: "Document" designed by Timur Zima from The Noun Project #22: "Three levels" by Paolo Fefe (CC-ND 2.0) #43:"Headset" designed by Benoît Bâlon from The Noun Project #43: "Engine" released into the Public Domain #43: "Flower" designed by Danilo Gusmão Silveira from The Noun Project #43: "Video Game Controller" designed by "Michael Rowe" from The Noun Project #51: "Anvil" designed by Masrur Mahmood from The Noun Project #52: "Hand" designed by Mikhail Bazilevsky from The Noun Project #53: "NYC Steam" by Don O'Brien (CC-BY 2.0) #56: "Floods 2013: Riverfront Ave" by Ryan Quan (CC-SA 2.0) #61: "3 Strikes for £2.50" by Julian Frost (CC-BY 2.0) 67
  • 77. Trademarks Magnolia The Pulse } are trademarks of Magnolia International Limited. Other trademarks are the property of their respective owners. 68