Getting Healthy with Magnolia, Blossom and Spring

62
1 Blossom in the Real World

description

This presentation was given at Amplify Miami 2014 by Jan Haderka, Senior Developer at Magnolia International, and Casey Dement, VP of Architecture at Sharecare, Inc. Casey showed how Sharecare integrated Magnolia and Blossom with Spring MVC and Akamai to build a highly flexible and scalable website. Jan gives an introduction and an update on the new features in Blossom 3.

Transcript of Getting Healthy with Magnolia, Blossom and Spring

Page 1: Getting Healthy with Magnolia, Blossom and Spring

!1

Blossom in the Real World

Page 2: Getting Healthy with Magnolia, Blossom and Spring

Sr. Software Engineer, Magnolia Lead developer of Blossom Module Spring Framework user since 2005

Tobias Mattsson

2@sigget

Page 3: Getting Healthy with Magnolia, Blossom and Spring

3

Jan HaderkaHead of Support, Magnolia

@rah003

Page 4: Getting Healthy with Magnolia, Blossom and Spring

4

Casey DementVP of Architecture, Sharecare

@casey_dement

Page 5: Getting Healthy with Magnolia, Blossom and Spring

#Mplify

5

Page 6: Getting Healthy with Magnolia, Blossom and Spring

Magnolia + Spring = Blossom 6

Page 7: Getting Healthy with Magnolia, Blossom and Spring

@Template

7

Page 8: Getting Healthy with Magnolia, Blossom and Spring

TEMPLATEREQUEST CONTENT

CMS

8

Page 9: Getting Healthy with Magnolia, Blossom and Spring

TEMPLATEREQUEST CONTENT

CMS + Blossom

9

CONTROLLER

MODEL

VIEW

Page 10: Getting Healthy with Magnolia, Blossom and Spring

Page Template

@Controller @Template(id="myModule:pages/main", title="Main") public class MainTemplate { !

   @RequestMapping("/main")    public String render(ModelMap model) {        return "pages/main";    } }

10

Page 11: Getting Healthy with Magnolia, Blossom and Spring

11

PAGE

Page 12: Getting Healthy with Magnolia, Blossom and Spring

PAGES CONTAIN 0:n AREAS

12

PAGE

AREA

A R E A

AREA

Page 13: Getting Healthy with Magnolia, Blossom and Spring

PAGES CONTAIN 0:n AREAS

13

PAGE

AREA

A R E A

AREA

AREAS HAVE 0:n COMPONENTS

COMPONENT

COMPONENT Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.

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.

C O M P O N E N T

Page 14: Getting Healthy with Magnolia, Blossom and Spring

Area Template

@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";        }    ...

14

Page 15: Getting Healthy with Magnolia, Blossom and Spring

Component Template

@Controller @Template(id="myModule:components/shoppingCart",          title="Shopping Cart") @TemplateDescription("Shopping cart") public class ShoppingCartComponent {    @RequestMapping("/shoppingCart")    public String handleRequest() {        ...        return "components/shoppingCart";    } ...

15

Page 16: Getting Healthy with Magnolia, Blossom and Spring

SERVLET CONTAINER

RENDERING FILTER

RENDERING ENGINE

BLOSSOM DISPATCHER SERVLET CONTROLLER

16

How Blossom Works

Page 17: Getting Healthy with Magnolia, Blossom and Spring

Blossom 3.0

17

What’s new?

Page 18: Getting Healthy with Magnolia, Blossom and Spring

Blossom 3.0

18

Update for Magnolia 5 series Requires Magnolia 5.1 new ways of building dialogs availability annotation

Page 19: Getting Healthy with Magnolia, Blossom and Spring

19

Dialogs

Page 20: Getting Healthy with Magnolia, Blossom and Spring

Fluent Builder-style API

!

@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")  ); }

20

Page 21: Getting Healthy with Magnolia, Blossom and Spring

Dialogs and the Class Hierarchy

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")    );  } }

21

Page 22: Getting Healthy with Magnolia, Blossom and Spring

Input Validation

... 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()) ); } ...

22

Page 23: Getting Healthy with Magnolia, Blossom and Spring

Dynamic Dialog

!!@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(Node content, ModelMap model) 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)        );    } }

23

Page 24: Getting Healthy with Magnolia, Blossom and Spring

Dynamic Dialog

!@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(Node content, ModelMap model) 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)        );    } }

24

Page 25: Getting Healthy with Magnolia, Blossom and Spring

@AvailableComponentClasses

25

Page 26: Getting Healthy with Magnolia, Blossom and Spring

Components Availability

!    @ComponentCategory @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Banner { } ! @Area("banners") @AvailableComponentClasses({Banner.class}) @Controller    public class BannersArea { … } ! @Banner @Template(id="myModule:components/largePromoBanner"), title="Large banner”) @Controller("banner")    public class LargePromoBannerComponent { …    }

26

Page 27: Getting Healthy with Magnolia, Blossom and Spring

@More

27

Page 28: Getting Healthy with Magnolia, Blossom and Spring

Content, content, more content

!@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(Node content, ModelMap model) 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)        );    } }

28

Page 29: Getting Healthy with Magnolia, Blossom and Spring

Now The Real World 29

Page 30: Getting Healthy with Magnolia, Blossom and Spring

Thank You!

30

Page 31: Getting Healthy with Magnolia, Blossom and Spring

31

magnolia-cms.com/spring

Page 32: Getting Healthy with Magnolia, Blossom and Spring

32

Page 33: Getting Healthy with Magnolia, Blossom and Spring

How do I get started on my project?

33

Page 34: Getting Healthy with Magnolia, Blossom and Spring

mvn archetype:generate -DarchetypeCatalog=http://nexus.magnolia-cms.com/content/groups/public/ !choose magnolia-blossom-module-archetype !Define value for property 'groupId': : com.acme Define value for property 'artifactId': : acme-module Define value for property 'version': 1.0-SNAPSHOT: Define value for property 'package': com.acme: Define value for property 'magnolia-version': : 4.5.11 Define value for property 'module-class-name': : AcmeModule Define value for property 'module-name': acme-module: acmeModule !!!! http://wiki.magnolia-cms.com/display/WIKI/

Creating+a+new+Blossom+project+using+maven+archetypes

Page 35: Getting Healthy with Magnolia, Blossom and Spring

How would I build a REST interface to my CMS content using Blossom?

35

Page 36: Getting Healthy with Magnolia, Blossom and Spring

REST servlet in module descriptor

<servlets>   <servlet>     <name>rest</name>     <class>org.springframework.web.servlet.DispatcherServlet</class>     <mappings>       <mapping>/rest/*</mapping>     </mappings>     <params>       <param>         <name>contextConfigLocation</name>         <value>classpath:/rest-servlet.xml</value>       </param>     </params>   </servlet> </servlets>

36

Page 37: Getting Healthy with Magnolia, Blossom and Spring

REST Controller

@Controller public class ProductsRestController { !

@RequestMapping("/products/{productId}") public Product findProduct(@PathVariable String productId) { // implementation omitted } }

37

Page 38: Getting Healthy with Magnolia, Blossom and Spring

Can I leverage Magnolia's caching functionality to improve the performance of my Spring app?

38

Page 39: Getting Healthy with Magnolia, Blossom and Spring

Influencing caching 39

@Controller @Template(title="Text", id="myModule:components/text") public class TextComponent { !

@RequestMapping("/text") public String render(HttpResponse response) { response.setHeader("Cache-Control", "no-cache"); return "components/text.jsp"; } }

Page 40: Getting Healthy with Magnolia, Blossom and Spring

How can I present dialogs in different languages using Blossom?

40

Page 41: Getting Healthy with Magnolia, Blossom and Spring

!@Controller @Template(title = "Text", id = "blossomSampleModule:components/text") @I18nBasename("info.magnolia.blossom.sample.messages") public class TextComponent { !

@TabFactory("textComponent.contentTab.label") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("heading").label("textComponent.contentTab.heading") ); } }

41I18n Blossom Dialog

Page 42: Getting Healthy with Magnolia, Blossom and Spring

!!/info/magnolia/blossom/sample/messages_en.properties textComponent.contentTab.label = Content textComponent.contentTab.heading = Heading !!!/info/magnolia/blossom/sample/messages_sv.properties textComponent.contentTab.label = Innehåll textComponent.contentTab.heading = Rubrik !

42I18n Blossom Dialog

Page 43: Getting Healthy with Magnolia, Blossom and Spring

How do I migrate my existing site to Magnolia/Blossom?

43

Page 44: Getting Healthy with Magnolia, Blossom and Spring

How can I integrate Spring Security?

44

Page 45: Getting Healthy with Magnolia, Blossom and Spring

45

Page 46: Getting Healthy with Magnolia, Blossom and Spring

How can I dependency inject spring beans into RenderingModels?

46

Page 47: Getting Healthy with Magnolia, Blossom and Spring

Autowired RenderingModel

public class MyRenderingModel extends RenderingModelImpl<TemplateDefinition> { ! @Autowired private MyService service; ! public MyRenderingModel(ServletContext servletContext, Node content, TemplateDefinition definition, RenderingModel<?> parent) { super(content, definition, parent); WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(servletContext); AutowireCapableBeanFactory beanFactory = wac.getAutowireCapableBeanFactory(); beanFactory.autowireBean(this); }

47

Page 48: Getting Healthy with Magnolia, Blossom and Spring

Autowired RenderingModel

!public class MyRenderingModel extends AbstractAutowiredRenderingModel<TemplateDefinition> { ! @Autowired private MyService service; ! public MyRenderingModel(ServletContext servletContext, Node content, TemplateDefinition definition, RenderingModel<?> parent) { super(content, definition, parent, servletContext); } !!!!

48

Page 49: Getting Healthy with Magnolia, Blossom and Spring

Views 49

Page 50: Getting Healthy with Magnolia, Blossom and Spring

Rendering an Area

FreeMarker [@cms.area name="main" /] !

JSP <cms:area name="main" />

50

Page 51: Getting Healthy with Magnolia, Blossom and Spring

Rendering Components in an Area

FreeMarker [#list components as component]    [@cms.component content=component /] [/#list] !

JSP <c:forEach items="${components}" var="component">    <cms:component content="${component}" /> </c:forEach>

51

Page 52: Getting Healthy with Magnolia, Blossom and Spring

Page Template Availability

@Controller @Template(title="Article", id="myModule:/pages/article") public class ArticleTemplate {    ...    @Available    public boolean isAvailable(Node node) {        return node.getPath().startsWith("/articles/");    } }

52

Page 53: Getting Healthy with Magnolia, Blossom and Spring

Available Components

@Controller @Area("promos") @AvailableComponentClasses({TextComponent.class,                            ShoppingCartComponent.class}) public static class PromosArea {    @RequestMapping("/main/promos")    public String render() {        return "areas/promos";    } }

53

Page 54: Getting Healthy with Magnolia, Blossom and Spring

Area Inheritance

@Controller @Area("promos") @Inherits @AvailableComponentClasses({TextComponent.class,                            ShoppingCartComponent.class}) public static class PromosArea {    @RequestMapping("/main/promos")    public String render() {        return "areas/promos";    } }

54

Page 55: Getting Healthy with Magnolia, Blossom and Spring

Form Submission

/* 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";

}

55

Page 56: Getting Healthy with Magnolia, Blossom and Spring

But wait a minute …

56

Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at info.magnolia.module.blossom.render.BlossomDispatcherServlet.forward(BlossomDispatcherServlet.java:123) at info.magnolia.module.blossom.render.BlossomTemplateRenderer.render(BlossomTemplateRenderer.java:78) ... 92 more Caused by: java.lang.IllegalStateException at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:435)

… the response has been sent.

Page 57: Getting Healthy with Magnolia, Blossom and Spring

Controller Pre-execution

57

C M V

PAGE

C M V

Area

C M V

Component

C M V

Component

C M V

Component

C

Page 58: Getting Healthy with Magnolia, Blossom and Spring

CPE Implementation 58

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” /> ...

Page 59: Getting Healthy with Magnolia, Blossom and Spring

Spring Web MVC +

Content

59

Page 60: Getting Healthy with Magnolia, Blossom and Spring

Spring Web Flow

60

REQUEST LANDING PAGE

SPRING WEB FLOW

Page 61: Getting Healthy with Magnolia, Blossom and Spring

Spring Web Flow

61

REQUEST LANDING PAGE

FORM SUBMIT?

SPRING WEB FLOW

Page 62: Getting Healthy with Magnolia, Blossom and Spring

62

Trademarks

Other trademarks are the property of their respective owners.

MagnoliaThe Pulse are trademarks of

Magnolia International Limited.}