spring and web content management

Post on 01-Nov-2014

3.852 Views

Category:

Technology

6 Downloads

Preview:

Click to see full reader

DESCRIPTION

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.

TRANSCRIPT

© 2013 SpringOne 2GX. All rights reserved.

Spring & WebContent Management

Tobias Mattsson & Daniel LippMagnolia International

2

3

4

Sr. Software Engineer, MagnoliaLead developer of Magnolia’s Spring integration

Spring Framework user since 2005

Tobias Mattsson

5

Daniel LippSr. Software Engineer, Magnolia

17 years of experience as software architect and Java developerGrateful to Spring for inspiring improvements to JEE 6

®

7

8

@magnolia_cms#magnolia_cms

9

SpringSpring MVC

Code, beautiful codeDependency Injection

IntegrationsBusiness logic

10

CMSSecurityMulti-lingualUser interfaceImage manipulationContent versioning

11

CMSSecurityMulti-lingualUser interfaceImage manipulationContent versioning

SpringSpring MVC

Code, beautiful codeDependency Injection

IntegrationsBusiness logic

12

USES IFRAMES

LIKE IT'S 1999 13

Magnolia + Spring = Blossom 14

@Template

15

or, "How Blossom is Built"

TEMPLATEREQUEST CONTENT

CMS

16

TEMPLATEREQUEST CONTENT

CMS + Blossom

17

CONTROLLER

MODEL

VIEW

Page Template

@Controller

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

   @RequestMapping("/main")

   public String render(ModelMap model) {

       return "pages/main";

   }

}

18

PAGES CONTAIN 0:n AREAS

19

PAGE

PAGES CONTAIN 0:n AREAS

19

PAGE

AREA

AREA

AREA

PAGES CONTAIN 0:n AREAS

19

PAGE

AREA

AREA

AREA

AREAS HAVE 0:n COMPONENTSCOMPONENT

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

COMPONENTEtiam 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.

COMPONENT

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

       }

   ...

20

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

   }

...

21

Views 22

Rendering an Area

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

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

23

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>

24

About Magnolia CMS100% Java/J2EE compliantBest of breed open technology stack, including:

JSR-283 / JCR 2.0Vaadin / GWTServlet APIHTML5

Highly Customizable

25

Java Content Repository

26

“… defines an abstract model and a Java API for data storage and related services commonly used by content-oriented applications.” – JSR-283

Magnolia

JCR API

Persistence Manager

Jackrabbit Hierarchical content repository

In memoryDatabaseFile system

or or

27

Why JCR?

28

Hierarchical

Event Notification(Observation)

Role-basedsecurity

Locking

Fine-grained contentand large binaries

Full-textSearch

StructuredQueries

ReferentialIntegrity

TransactionalSchema-less or Strongly-typed

Versioning

29

Dialogs

30

GWT with server-side stateComponent-based UICross-browser compatibility (via GWT)Rich component libraryFast development pace

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

31

Page Template with Dialog

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

}

32

Page Template with Dialog

@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 -->

32

@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

@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

@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

<div id="main" style="border:${content.border}px solid #000"> <h2>${content.heading}</h2> <c:forEach items="${components}" var="component"> <cms:component content="${component}" /> </c:forEach></div> <!-- In the view -->

Component Template with Dialog

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

34

Component Template with Dialog

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

34

<h1>${content.heading}</h1><p>${cmsfn:decode(content).body}</p><!-- In the view -->

YouTube Video Component

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

35

YouTube Video Component

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

35

<iframe width="100%" height="400" src="//www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen></iframe><!-- In the view -->

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

36

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

36

<head> <meta name="description" content="${content.metaDescription}"/> <meta name="keywords" content="${content.metaKeywords}" />  <meta name="author" content="${content.metaAuthor}" /></head> <!-- In the view -->

Dynamic Dialog

@Template(id="myModule:components/bookCategory", title="Book category")

@TemplateDescription("A list of books in a given category.")@Controllerpublic 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)        );    }}

37

Dynamic Dialog

...

@TabFactory("Content")public void contentTab(UiConfig cfg, TabBuilder tab) {

Collection<String> categories = service.getBookCategories();

tab.fields(

cfg.fields.select("category").label("Category").options(categories)

 );}

...

38

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

}

...

39

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

40

Available Components

@Controller

@Area("promos")

@AvailableComponentClasses({TextComponent.class,                            ShoppingCartComponent.class})public static class PromosArea {

   @RequestMapping("/main/promos")

   public String render() {

       return "areas/promos";

   }

}

41

Area Inheritance

@Controller

@Area("promos")

@Inherits@AvailableComponentClasses({TextComponent.class,

                           ShoppingCartComponent.class})

public static class PromosArea {

   @RequestMapping("/main/promos")

   public String render() {

       return "areas/promos";

   }

}

42

SERVLETCONTAINER

RENDERINGFILTER

RENDERINGENGINE

BLOSSOM DISPATCHERSERVLET CONTROLLER

43

How Blossom Works

Magnolia Server

Dat

a

Wor

kflo

w

STK Mod

ules

Magnolia Core

Cus

tom

m

odul

e

Cus

tom

m

odul

e

DM

S Imag

ing

Cus

tom

Th

eme

Module JAR - templates - resources

44

Web container (Tomcat)

Web Archive

Magnolia CMS

Filter chain CMS

Context Security Cache Aggregation

Hierarchical content repository

Content

Custom filter

RequestRendering

Rendering engine

Response

45

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

}

46

But wait a minute …

47

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 moreCaused by: java.lang.IllegalStateException

… the response has been sent.

Controller Pre-execution

48

C M V

PAGE

C M V

Area

C M V

Component

C M V

Component

C M V

Component

C

CPE Implementation 49

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

...

Spring Web MVC+

Content

50

Spring Web Flow

51

REQUEST LANDINGPAGE

SPRING WEB FLOW

Spring Web Flow

52

REQUEST LANDINGPAGE

FORMSUBMIT?

SPRING WEB FLOW

Deployment & Scaling 53

Author

MagnoliaPublic A

Magnolia

Public B

Magnolia

Firewall

Activate

Internal Network Internet

Load

Bal

ance

r

JCR

JCR

JCR

54

55

Backend Application

DB

Author

Magnolia

JCR

Public

Magnolia

JCRPublic

Magnolia

JCR

Clustering

Dealing with Change 56

Filter Chain in JCR

57

Configuring Spring Beans 58

<blossom:observed-bean

default-class="com.sample.DiscountService"

path="/modules/myModule/beans/discountService" />

example.com/vanity

59

Changing Resources Live

60

CSSFreeMarkerImagesJavascriptViews

What Do You Win? 61

62

63

64

ShorterLearning

Curve

MVC

Use ExistingSpring Apps &Components

@Annotations

LooseCoupling

DependencyInjection

Code-drivenDialogs

Test-drivenDevelopment

SpringIdiomsSecurity

Scalability

Mobile& Touch

FasterDevelopment

FasterDevelopment

Cycles

Staging

Maglev

64

ShorterLearning

Curve

MVC

Use ExistingSpring Apps &Components

@Annotations

LooseCoupling

DependencyInjection

Code-drivenDialogs

Test-drivenDevelopment

SpringIdiomsSecurity

Scalability

Mobile& Touch

FasterDevelopment

FasterDevelopment

Cycles

Staging

Questions?

Maglev

Want to Learn More?

65

Talk to us in the exhibit hall

Attend Blossom Q & A webinar on Sept. 26http://magnolia-cms.com/blossom-qa

Visit http://magnolia-cms.com/springDashboard for all things Spring in Magnolia

Thank You!

66

Image Credits

67

#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)

68

Trademarks

Other trademarks are the property of their respective owners.

MagnoliaThe Pulse are trademarks of

Magnolia International Limited.}

top related