us navy slideshare spring apps

37
US NAVY INTEGRATES SPRING APPS WITH FLYING COLORS THANKS TO WEB CMS +

Upload: cara-lynn-mallow

Post on 20-Jan-2017

270 views

Category:

Government & Nonprofit


3 download

TRANSCRIPT

US NAVY INTEGRATES SPRING APPSWITH FLYING COLORS THANKS TO WEB CMS

+

WEBINAR

CAMPBELL EWALD

ABOUT THE WEBINARPosting Questions

Viewing the recorded webinar and related materials

Contacting Us

Tweeting - use hash tags: #magnolia_cms #campbellewald #springframework #springmvc #USNavy

2

WEBINAR

CAMPBELL EWALD

ABOUT CAMPBELL EWALD AND MAGNOLIACAMPBELL EWALDDigital AgencyMagnolia PartnerGeneral Motors, Kaiser Permanente, US Postal Service

MagnoliaJava-based Content Management SystemCommunity and Enterprise EditionsSony, Deloitte, Airbus and JBoss

4

WEBINAR

CAMPBELL EWALD

WHAT WE’LL TALK ABOUT

Overview of Magnolia Blossom module

Navy Use Cases

Step-by-Step Walkthrough

Questions

5

WEBINAR

CAMPBELL EWALD

BLOSSOM OVERVIEW

6

WEBINAR

CAMPBELL EWALD

A LITTLE PHILOSOPHY

7

The idea that sparked me writing the Blossom Module for Magnolia CMS was to bring the CMS and especially the content into Spring Web MVC not the other way around. Controllers should be the building blocks when composing pages.

– Tobias Mattsson http://tobias-mattsson-magnolia.blogspot.com/2011/03/spring-web-mvc-with-content.html

WEBINAR

CAMPBELL EWALD

WHAT’S BLOSSOM

Spring integration module for Magnolia CMS

Spring Web MVC with Content

Key features:

Annotation based API

Spring controllers exposed as template components

Pre-execution of template components

8

WEBINAR

CAMPBELL EWALD

WHY USE BLOSSOMLeverages proven Spring Framework

Easily integrate or migrate existing Spring-based applications

Spring developers will feel right at home

Dynamic dialogs, Virtual URI Mappings, Pre-execution

Easily access content in controllers

Best practice web app design patterns (MVC, IoC, AOP)

Open Source and Free to download and use

9

WEBINAR

CAMPBELL EWALD

NAVY USE CASES

10

TM

WEBINAR

CAMPBELL EWALD

ABOUT NAVY.COM

Official recruitment Website for the U.S. Navy

Redesigned in 2010, moved to Magnolia CMS

Application integration for subscription services, CRUD functionality and user-space applications

11

WEBINAR

CAMPBELL EWALD

BLOSSOM + RESTEASY

RESTEasy provided nice JAX-RS client

Aligned with Magnolia CMS roadmap

Allowed business components to be loosely-coupled to CMS

12

Navy Web Service Registry

Business Component 1

Business Component 2

Business Component 3

Business Component n

Navy.comNavy Custom Module

Blossom

WEBINAR

CAMPBELL EWALD

FIND A RECRUITER

Finds the nearest Enlisted and Officer Recruiter Locations based on the postal code entered by the site visitor

Collect and validate input, call Navy Web Service, display results.

14

WEBINAR

CAMPBELL EWALD

LIFE OPS

Personality Profile Test to help potential recruits plan their future

Wizard type interface

Collect and validate selected options from site visitor, populate model, then calculate and display results

15

WEBINAR

CAMPBELL EWALD

STANDARD TEMPLATING KITThe STK is a production-

ready website construction framework.

• Best Practices• Rapid Prototyping•Modular Architecture• Extensible• Standards-Compliant

16

WEBINAR

CAMPBELL EWALD

BUILDING LIFE OPS: STEP-BY-STEP

17

WEBINAR

CAMPBELL EWALD

GETTING STARTEDStarting Spring - Add the Blossom Servlet Context Listener to your web.xml<listener> <listener-class>info.magnolia.module.blossom.support.ServletContextExposingContextListener</listener-class></listener>

Create a module for your custom Spring application components, quickest way:$ mvn archetype:generate -DarchetypeCatalog=http://nexus.magnolia-cms.com/content/groups/public/

Have your module class extend BlossomModuleSupport and implement start and stop methods:public class BlossomSampleModule extends BlossomModuleSupport implements ModuleLifecycle { public void start(ModuleLifecycleContext moduleLifecycleContext) { initRootWebApplicationContext("classpath:/applicationContext.xml"); initBlossomDispatcherServlet("blossom", "classpath:/blossom-servlet.xml"); } public void stop(ModuleLifecycleContext moduleLifecycleContext) { destroyDispatcherServlets(); closeRootWebApplicationContext(); }}

18

WEBINAR

CAMPBELL EWALD

SPRING BEAN CONFIGURATION

Create a Spring bean config file called applicationContext.xml<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:blossom="http://www.magnolia-cms.com/schema/blossom" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.magnolia-cms.com/schema/blossom http://www.magnolia-cms.com/schema/blossom-1.2.xsd"> <blossom:configuration /> </beans>

19

WEBINAR

CAMPBELL EWALD

SPRING BEAN CONFIGURATION

Create a Spring bean config file called blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="..."> <context:annotation-config /> <context:component-scan base-package="com.c_e.webinar.magnolia.module.blossomsample" use-default-filters="false"> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.Paragraph" /> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.Template" /> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.DialogFactory" /> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.VirtualURIMapper" /> <context:include-filter type="assignable" expression="info.magnolia.cms.beans.config.VirtualURIMapping" /> </context:component-scan> ...</beans>

20

WEBINAR

CAMPBELL EWALD

SPRING BEAN CONFIGURATIONAdd Spring Handler Adapters and Mappings to blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans ...> ... <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="customArgumentResolver"> <bean class="info.magnolia.module.blossom.web.BlossomWebArgumentResolver" /> </property> </bean> <bean class="info.magnolia.module.blossom.preexecution.BlossomHandlerMapping"> <property name="targetHandlerMappings"> <list> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="useDefaultSuffixPattern" value="false" /> </bean> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /> </list> </property> </bean> ...</beans>

21

WEBINAR

CAMPBELL EWALD

SPRING BEAN CONFIGURATIONAdd Spring View Resolvers to blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans...> ... <bean class="info.magnolia.module.blossom.view.UuidRedirectViewResolver"> <property name="order" name="1" /> </bean> <bean class="info.magnolia.module.blossom.view.TemplateViewResolver"> <property name="order" name="2" /> <property name="prefix" name="/ce-webinar-templating-kit/templates/blossomsamples" /> <property name="suffix" name=".ftl" /> <property name="viewRenderer"> <bean class="info.magnolia.module.blossom.view.FreemarkerTemplateViewRenderer" /> </property> </bean> <bean class="info.magnolia.module.blossom.view.ParagraphViewResolver"> <property name="order" name="3" /> <property name="prefix" name="/ce-webinar-templating-kit/paragraphs/blossomsamples" /> <property name="suffix" name=".ftl" /> <property name="viewRenderer"> <bean class="info.magnolia.module.blossom.view.FreemarkerParagraphViewRenderer" /> </property> </bean></beans>

22

WEBINAR

CAMPBELL EWALD

LIFE OPS MODEL

Uses Bean Validation API (JSR 303)

Excerpt:public class LifeOps implements Serializable { ... @NotNull @Min(0) @Max(18) private Integer advisor; ... @NotNull @Min(0) @Max(18) private Integer doer; ...}

23

WEBINAR

CAMPBELL EWALD

LIFE OPS MODEL

Add in bean validation support to blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans ...> ... <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> ... <property name="webBindingInitializer"> <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"> <property name="validator" ref="validator" /> </bean> </property> </bean> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /> ...</beans>

24

WEBINAR

CAMPBELL EWALD

LIFE OPS MODELConstructorpublic LifeOps(Integer advisor, Integer doer, Integer innovator, Integer persuader, Integer planner, Integer problemSolver) { this.advisor = advisor; this.doer = doer; this.innovator = innovator; this.persuader = persuader; this.planner = planner; this.problemSolver = problemSolver;}

Constructor using transients:public LifeOps(String[] activities, String[] interests, String[] careers) { this.activities = activities; this.interests = interests; this.careers = careers; this.resetScores(); this.calculateScores(activities); this.calculateScores(interests); this.calculateScores(careers);}

25

WEBINAR

CAMPBELL EWALD

LIFE OPS SPRING VALIDATORpublic class LifeOpsValidator implements Validator {

@Override public boolean supports(Class<?> clazz) { return LifeOps.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { LifeOps lifeOps = (LifeOps) target; if (lifeOps.getActivities() == null || lifeOps.getActivities().length == 0) { errors.rejectValue("activities", "activities.required", "Please choose at least one activity before proceeding."); } if (lifeOps.getInterests() == null || lifeOps.getInterests().length == 0) { errors.rejectValue("interests", "interests.required", "Please choose at least one interest before proceeding."); } if (lifeOps.getCareers() == null || lifeOps.getCareers().length == 0) { errors.rejectValue("careers", "careers.required", "Please choose at least one career before proceeding."); } }}

26

WEBINAR

CAMPBELL EWALD

LIFE OPS CONTROLLER@Controller@RequestMapping("/life-ops")@Paragraph("Life Ops form")@ParagraphDescription("Adds a Life Ops form and displays Life Ops Results")@I18nBasename("com.c_e.webinar.magnolia.module.blossomsample.messages")public class LifeOpsController {

private static final Logger log = LoggerFactory.getLogger(LifeOpsController.class); private final String LIFE_OPS_FORM_PATH = "life-ops/form"; private final String LIFE_OPS_SHOW_PATH = "life-ops/show"; @Autowired private Validator validator; @RequestMapping(method = RequestMethod.GET) public String form(LifeOps lifeOps) { return LIFE_OPS_FORM_PATH; } ...}

27

WEBINAR

CAMPBELL EWALD

LIFE OPS CONTROLLER@RequestMapping(method = RequestMethod.POST)public String processSubmit(@Valid LifeOps lifeOps, BindingResult result, Model model) { new LifeOpsValidator().validate(lifeOps, result); if (result.hasErrors()) { List<FieldError> fieldErrors = result.getFieldErrors(); Map<String, String> errorsMap = new HashMap<String, String>(); for (int i = 0; i < fieldErrors.size(); i++) { FieldError thisError = fieldErrors.get(i); errorsMap.put(thisError.getField(), thisError.getDefaultMessage() ); } lifeOps.setErrors(errorsMap); model.addAttribute("lifeOps", lifeOps); return LIFE_OPS_FORM_PATH; } else { lifeOps = new LifeOps(lifeOps.getActivities(), lifeOps.getInterests(), lifeOps.getCareers() ); model.addAttribute("lifeOps", lifeOps); return "redirect:/" + STKUtil.getSite().getName() + "/life-ops/results" + "/" + lifeOps.getAdvisor() + "/" + lifeOps.getDoer() + "/" + lifeOps.getInnovator() + "/" + lifeOps.getPersuader() + "/" + lifeOps.getPlanner() + "/" + lifeOps.getProblemSolver(); }}

28

WEBINAR

CAMPBELL EWALD

LIFE OPS CONTROLLER@RequestMapping(method = RequestMethod.GET, params = "advisor")public String show( @RequestParam Integer advisor, @RequestParam Integer doer, @RequestParam Integer innovator, @RequestParam Integer persuader, @RequestParam Integer planner, @RequestParam Integer problemSolver, Model model) { try { LifeOps lifeOps = new LifeOps(advisor, doer, innovator, persuader, planner, problemSolver); model.addAttribute("lifeOps", lifeOps); } catch (Exception e) { log.error("LifeOpsController.show() Exception:" + e); } return LIFE_OPS_SHOW_PATH;}

29

WEBINAR

CAMPBELL EWALD

LIFE OPS CONTROLLER@TabFactory("Settings")public void addTab(TabBuilder builder) { builder.addEdit("activitiesText", "Activities Text", "A short description for the activities fieldset"); builder.addEdit("interestsText", "Interests Text", "A short description for the interests fieldset"); builder.addEdit("careersText", "Careers Text", "A short description for the interests fieldset"); builder.addFckEditor("resultsText", "Results Text", "A short intro for the results page");}

30

WEBINAR

CAMPBELL EWALD

LIFE OPS VIRTUAL URI MAPPING@VirtualURIMapperpublic class LifeOpsURIMapper {

private Pattern pattern; public LifeOpsURIMapper() { this.pattern = Pattern.compile("^/life-ops/results/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/?"); }

public String mapping(String uri, HttpServletRequest request) { this.pattern = Pattern.compile("^/(" + STKUtil.getSite().getName() + "/)?life-ops/results/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/?"); Matcher matcher = pattern.matcher(uri); if (matcher.matches()) { return matcher.replaceAll("forward:/" + STKUtil.getSite().getName() + "/life-ops/results/?advisor=$2&doer=$3&innovator=$4&persuader=$5&planner=$6&problemSolver=$7"); } return null; }}

31

WEBINAR

CAMPBELL EWALD

LIFE OPS FORM VIEW[#assign cms=JspTaglibs["cms-taglib"]][#assign form=JspTaglibs["http://www.springframework.org/tags/form"]]

<div> [@cms.editBar /] [@form.form id="life-ops-form" modelAttribute="lifeOps"] ... <fieldset> <legend>Activities</legend> ... <dl class="prop"> <dt><span>What do you like to do?</span></dt> <dd class="value"> <ul> <li> <input id="activities1" name="activities" value="D" type="checkbox" /> <label for="activities1"><strong>Set up a home computer network</strong></label> </li> <li> <input id="activities2" name="activities" value="S" type="checkbox" /> <label for="activities2"><strong>Track the path of a hurricane</strong></label> </li> ... </ul> </dd> </dl> </fieldset>

32

WEBINAR

CAMPBELL EWALD

LIFE OPS RESULTS VIEW[#assign results = [ {"name": "problemSolver", "label": "Problem Solver", "score": lifeOps.problemSolver}, {"name": "planner", "label": "Planner", "score": lifeOps.planner}, {"name": "persuader", "label": "Persuader", "score": lifeOps.persuader}, {"name": "innovator", "label": "Innovator", "score": lifeOps.innovator}, {"name": "doer", "label": "Do-er", "score": lifeOps.doer}, {"name": "advisor", "label": "Advisor", "score": lifeOps.advisor}]]...

33

WEBINAR

CAMPBELL EWALD

LIFE OPS RESULTS VIEW<div class="super-list"> [@cms.editBar /] <ul> [#list results?sort_by("score")?reverse as result] [#assign profileDivId = result.label?lower_case?trim?replace(' ', '-')] [#assign scoreWidth = (result.score/2)*11+"%"] [#assign contentCollectionText = result.name + "Text"] [#assign contentCollectionExtras = result.name + "Extras"] <li> <h3> <a rel="bookmark" href="#${profileDivId}" title="${result.label}"> <dfn style="width:${scoreWidth};"><strong>${result.label}</strong></dfn> </a> </h3> ... [#if contentCollectionText?exists] [@cms.contentNodeIterator contentNodeCollectionName=contentCollectionText] [@cms.includeTemplate/] [/@cms.contentNodeIterator] [/#if] [@cms.newBar contentNodeCollectionName=contentCollectionText paragraph="stkTextImage, stkTeaserGroup" /] ... </li> [/#list] </ul></div><!-- end super-list -->

34

WEBINAR

CAMPBELL EWALD

35

QUESTIONS

WEBINAR

CAMPBELL EWALD

FURTHER READINGVisit the Magnolia documentation site to download Magnolia and find tutorials and guideshttp://documentation.magnolia-cms.com/index.html

The Blossom reference documentationhttp://documentation.magnolia-cms.com/modules/blossom.html

Blossom Sample Webapphttp://documentation.magnolia-cms.com/modules/blossom.html#GettingStarted

Tobias Mattsson's bloghttp://tobias-mattsson-magnolia.blogspot.com/

Ask your questions and see what others are doing at the Magnolia community forumhttp://forum.magnolia-cms.com/forum.html

37