http://www.developer.com/java/web/deploying-and-testing-magnolia-module-code.html

Back to article

Spring Framework and Magnolia CMS: Creating Complex Java-based Websites


December 8, 2010

If you're thinking about building a new Web site or re-launching an old one, it usually makes sense to consider a Content Management System (CMS). Using a CMS offers a number of advantages over hand-crafting individual pages. However, if you're building a complex Web site, an off-the-shelf CMS often won't satisfy all your requirements out of the box. You'll likely need to customize it and because each CMS its own API and architecture, you'll usually need to invest a fair amount of time learning its innards before you can start adding custom features.

From a development perspective, the ideal solution would be a CMS that can be customized without needing any special training or knowledge. If you're a Spring developer, this ideal CMS is closer than you may think. Magnolia CMS, an open-source, enterprise-grade CMS written entirely in Java, has recently added Spring support, allowing Spring developers to easily extend the base CMS with custom functionality using their existing knowledge of the Spring Framework.

In this article, I'll introduce you to Blossom, the Magnolia CMS module for Spring, and illustrate how you can use it to integrate Spring controllers and views into the Magnolia architecture. Let's get started!

Assumptions and Prerequisites

Before we dive into the code, a few notes and assumptions are worth mentioning. This tutorial assumes that you have a working knowledge of Spring 3.0.x, and that you're comfortable with deploying Web applications using Spring MVC. It also assumes that you're familiar with Java Server Pages (JSP) and the process of creating, building and deploying JAR files to an Apache Tomcat server using Apache Maven.

The Blossom module serves as a bridge between Spring and Magnolia CMS, so this tutorial also requires you to have an understanding of the Magnolia CMS architecture, especially for templating and custom modules. To obtain this understanding, you should read the WebReference article Publish Web Content Efficiently with Magnolia CMS, and then briefly review the Magnolia CMS templating tutorial and module design guide, particularly the section on creating custom modules.

In terms of software, this tutorial assumes that you have a properly configured Spring development environment, including the following components:

The code in the following sections also assumes that you have downloaded and installed:

You'll find detailed instructions on how to perform these tasks in the Magnolia CMS documentation, or in the WebReference beginner's tutorial Set Up the Magnolia CMS for Web Content Creation in Just a Few Clicks. Note that the Magnolia CMS download includes a bundled version of Apache Tomcat.

With all that out of the way, let's dive right into the code!

Introducing Blossom, the Spring and Magnolia CMS Bridge

As you may already know, Magnolia CMS uses a template-based architecture that allows users to define page types and subsections through a graphical, dialog-based interface. It then presents a composite WYSIWYG view with built-in editing controls for each subsection, allowing authors to directly modify different sections of the page and see the changes instantly. Figure 1 shows a screenshot that illustrates this in action.

Magnolia WYSIWYG Template Editor

Figure 1: Magnolia WYSIWYG Template Editor

Magnolia's templating system allows interface designers to easily create new templates to support custom use cases. There are two basic types of templates:

  1. Paragraph templates represent elements within pages.
  2. Page templates represent complete pages.

A single template can be reused multiple times to create different content pages.

To define a new template, a designer or developer must first create a template script using either JSP or FreeMarker. This script file contains the HTML markup for the template, together with placeholder variables that are replaced with actual content at runtime. This template script must then be registered with Magnolia via a template definition, which exposes basic information such as the template name and script path. As shown in this tutorial, these tasks can be easily accomplished through the Magnolia CMS browser-based administration panel.

If you're a Spring developer, though, there's an easier way to define templates: you can simply annotate your Spring controller classes with @Paragraph or @Template, deploy them as a Blossom-compatible module, and Magnolia will automatically detect and make them available as templates in the authoring interface. This can significantly speed up template development, while also offering the opportunity to attach custom workflows or functions to templates.

Magnolia Module Basics

To see how this works, let's create a new Magnolia module that exposes a Spring controller as a paragraph template. In your Spring development workspace, create a new project (I'll call it example-module) and within the project directory, create the directory structure shown in Figure 2.

Initial Magnolia Project Directory Structure

Figure 2: Initial project directory structure

This is the conventional directory structure followed for all Magnolia modules. Java classes are stored in the src/main/java directory, while additional resources such as JSP scripts or bootstrap files are stored in the src/main/resources directory.

Every Magnolia module must include a module description file, expressed in XML. This file contains information on the module, such as its name, version number and dependencies (if any). This information is displayed in Magnolia CMS during and after the module deployment process.

To create this file, open a text editor and save the following content to src/main/resources/META-INF/magnolia/example.xml:

<!--l version="1.0" encoding="UTF-8-->
<!--CTYPE module SYSTEM "module.dtd-->
example
Example Module
info.magnolia.module.example.ExampleModule
1.0.0

This specifies the name of the module (Example Module), its version number and the package it belongs to. By convention, Magnolia modules are packaged in the info.magnolia.module.moduleName namespace.

This ExampleModule class is responsible for starting Spring, initializing the application context and setting up the dispatcher servlet. It's generally recommended that this primary class extend the BlossomModuleSupport class; doing this ensures that the module is started and stopped correctly by Magnolia.

Here's what the code looks like:

package info.magnolia.module.example;
import info.magnolia.module.ModuleLifecycle;
import info.magnolia.module.ModuleLifecycleContext;
import info.magnolia.module.blossom.module.BlossomModuleSupport;
/**
* Module class that starts and stops Spring when called by Magnolia.
*/
public class ExampleModule 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();
}
}

Save this file as src/main/java/info/magnolia/module/example/ExampleModule.java.

At this point, all the basic pieces are in place, and we can move on to actually creating templates with Spring.

As previously noted, every Magnolia CMS template has two components: a template script, written in either JSP or FreeMarker, and a template definition. Spring comes in particularly handy when defining the latter.

To illustrate, let's expose a Spring controller for a Web form as a paragraph template. Here's the controller code, which should be saved to src/main/java/info/magnolia/module/PizzaFormParagraph.java.

package info.magnolia.module.example;
import info.magnolia.module.blossom.annotation.Paragraph;
import info.magnolia.module.blossom.annotation.ParagraphDescription;
import info.magnolia.module.blossom.annotation.TabFactory;
import info.magnolia.module.blossom.dialog.TabBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* Displays a pizza order form and a confirmation page after the form is submitted.
*/
@Controller
@Paragraph(value="Pizza Form", name="pizzaForm")
@ParagraphDescription("A form for users to order a pizza")
public class PizzaFormParagraph {
@RequestMapping("/pizza/new")
public String form(ModelMap model, HttpSession session, HttpServletRequest request) {
if ("POST".equals(request.getMethod())) {
return "pizzaFormResult";
}
return "pizzaForm";
}

@TabFactory("Content")
public void contentTab(TabBuilder tab) {
tab.addStatic("This paragraph requires no configuration");
tab.addHidden("bogus", "");
}
}

Notice the @Paragraph and @ParagraphDescription annotations in the controller; these specify the paragraph template label, internal name and description, and they're used by Blossom when auto-detecting and registering templates with Magnolia CMS. The name and description, in particular, are displayed to content authors when creating pages using the template.

The handleRequest() method will be automatically called by Spring's DispatcherServlet, and returns the name of the template script, either pizzaForm.jsp or pizzaFormResult.jsp. These scripts contain the HTML markup for the template.

Here's what pizzaForm.jsp looks like:

<!--taglib uri="cms-taglib" prefix="cms"-->
<!--taglib uri="blossom-taglib" prefix="blossom"--><h2>Order a Pizza</h2>
<form action="?" enctype="application/x-www-form-urlencoded" method="post" target=newFrame>
Size: <select name="size" target=newFrame> <option selected="selected" value="7" target=newFrame>Small</option> <option value="10" target=newFrame>Medium</option> <option value="12" target=newFrame>Large</option> <option value="18" target=newFrame>Monster</option></select>

Toppings: <input name="toppings[]" type="checkbox" value="cheese" />Cheese
<input name="toppings[]" type="checkbox" value="tomato" />Tomato
<input name="toppings[]" type="checkbox" value="onion" />Onion
<input name="toppings[]" type="checkbox" value="ham" />Ham
<input name="toppings[]" type="checkbox" value="bacon" />Bacon
<input name="toppings[]" type="checkbox" value="anchovies" />Anchovies

Crust: <input name="crust" type="radio" value="thin" />Thin and crisp
<input name="crust" type="radio" value="thick" />Thick and cheesy

Name: <input name="name" type="text" />

Email address: <input name="email" type="text" />

Notes: <textarea class="textinput" cols="40" rows="5" name="message" target=newFrame></textarea>

<input name="submit" type="submit" value="Save" />
</form></pre>
And here's what pizzaFormResult.jsp looks like:
<pre><!--taglib uri="cms-taglib" prefix="cms"--><h2>Thanks for your pizza order.</h2>
We will be in touch with you shortly.

These scripts should be saved to the src/main/resources/mgnl-files/WEB-INF/example/paragraphs directory.

You're almost done! Add an applicationContext.xml file containing the Blossom configuration for your Spring application to src/main/resources/applicationContext.xml, as shown below:

<!--l version="1.0" encoding="UTF-8-->
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.1.xsd">

Then, add a servlet configuration file that specifies the location of the template scripts (both page and paragraph templates) for the module:

<!--l version="1.0" encoding="UTF-8-->
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

Save this file as src/main/resources/blossom-servlet.xml.

At this point, your source directory structure should look something like Figure 3.

Magnolia Project Directory Structure

Figure 3: Project Directory Structure

The basic module is now ready, and all that's left is to build and deploy it. By convention, Magnolia modules are packaged as JAR archives and deployed to the WEB-INF/lib of the Magnolia CMS authoring and public instances. When Magnolia CMS restarts, it will automatically detect and install the new modules.

The easiest way to compile the new module is with Apache Maven. The included source code archive includes a pom.xmlproject build file that you can use for this purpose. Switch to the project directory, and run the following command at your shell prompt:

shell> mvn tomcat:deploy

You should see something like Figure 4 as Maven builds the project.

Apache Maven JAR Deployment

Figure 4: Apache Maven JAR Deployment

Assuming no errors, Maven will create a JAR file and place it in the project's example-module/target directory.

To deploy the module to Magnolia CMS, first shut down Magnolia CMS using the Magnolia control script:

shell> magnolia_control.sh stop (on UNIX)
shell> magnolia_control.bat stop (on Windows)

Copy the module JAR file from the project's example-module/target directory to the WEB-INF/lib directory of the Magnolia authoring instance.

If this is the first time you are deploying a Blossom module to Magnolia CMS, you will also need to update the Magnolia authoring instance's web.xml file with an additional context listener, as shown below. Note that this should be placed before Magnolia's own context listener:

info.magnolia.module.blossom.support.ServletContextExposingContextListener
Note: This section assumes that the Magnolia Blossom module and Spring Framework libraries are already present in the WEB-INF/lib directory of the Magnolia authoring instance. If this is not the case, you will have problems deploying and running the module. You can either manually copy these dependencies to the WEB-INF/lib directory of the Magnolia authoring instance, or (recommended) use Maven to manage these dependencies. You can read more about using Maven for dependency management here.

When you're done with all of the above tasks, restart Magnolia CMS:

shell> magnolia_control.sh start (on UNIX)
shell> magnolia_control.bat start (on Windows)

The Magnolia CMS installer should detect your new module(s) and prompt you for an update, as shown in Figure 5.

Magnolia Module Installation

Figure 5: Magnolia Module Installation

Install the new module, and then log in to the Magnolia CMS AdminCentral. Select the AdminCentral ->Configuration menu and browse through the modules list to the node modules/blossom/paragraphs/autodetected. If all has gone well, Blossom will have auto-detected your new paragraph template and it will appear in the list, as shown in Figure 6.

Magnolia Template Auto-detection by Blossom

Figure 6: Template Auto-detection by Blossom

You can now utilize this paragraph template in any page template. Although the module created in the earlier section doesn't include a page template, it's still possible to manually create a page template in Magnolia CMS and use the new paragraph template inside it. To do this, select the AdminCentral -> Configuration menu and browse through the modules list to the node modules/blossom/templating/templates/. Then, create a new template definition as shown in Figure 7.

New Template Definition in Magnolia

Figure 7: New Template Definition in Magnolia

After this has been created, create the corresponding template script:

<!--page contentType="text/html;charset=UTF-8" language="java"-->
<!--taglib uri="cms-taglib" prefix="cms"-->
<h2>${content.title}</h2>
<div id="content" target=newFrame>
</div>

Most of this is explained in the templating tutorial, but it's worth pointing out the element, which lists the available paragraphs for this template. Notice that the name specified for the paragraph template matches that specified in the @Paragraph annotation in the controller.

Save this script as $TOMCAT/webapps/magnoliaAuthor/templates/example/example.jsp, and then go back to the Magnolia AdminCentral. You should now be able to use the AdminCentral -> Website -> New Page command to create a new page using this template (see Figure 8).

New Page Creation in Magnolia

Figure 8: New Page Creation in Magnolia

Once the page is created, you can preview it by selecting it in the AdminCentral -> Website workspace and using the Open Page... command. Click the New button in the resulting preview window, and Magnolia will present you with a list of available paragraph templates, as shown in Figure 9.

New Paragraph Template Insertion

Figure 9: New Paragraph Template Insertion

You can now select the new paragraph template (remember that this is actually a Spring controller). Save your changes, and you should see the template in the preview window (see Figure 10).

Web Form Generated by Paragraph Template

Figure 10: Web Form Generated by Paragraph Template

Enter some input into the form and submit it. The input will go back to the Spring controller, which will process your input and return the name of the result template. Figure 11 shows what you should see.

Magnolia Form Submission Result

Figure 11: Form Submission Result

Tip: Blossom also supports the use of Spring controllers as page templates. The process is very similar to that for paragraph templates, except that you will annotate the controller with @Template instead of @Paragraph. Look here for an illustrative example.

Conclusion

Of course, this is just the tip of the iceberg. With Spring in play, all manner of interesting things become possible in Magnolia CMS. Still, the examples in this tutorial illustrate how Magnolia's Blossom module makes it easy to integrate advanced features of the Spring Framework with Magnolia's existing templating architecture. This allows developers a great deal of creativity and flexibility when building complex Web sites with custom workflows and non-standard use cases. The use of Spring annotations further simplifies the process, making it possible to reuse existing Spring controllers as Magnolia templates and thereby reduce the overall time, cost and risk of development. Try it out yourself, and see how easy it is!

Code Download

  • Magnolia-Spring_src
  • For Further Reading

  • Set Up the Magnolia CMS for Web Content Creation in Just a Few Clicks (from WebReference)
  • Publish Web Content Efficiently with Magnolia CMS (from WebReference)
  • The Magnolia CMS templating tutorial (from documentation.magnolia-cms.com)
  • About the Author

    Vikram Vaswani is a consultant specializing in open-source tools and technologies. He has more than 11 years of experience deploying open-source tools in corporate intranets, high-traffic Web sites, and mission-critical applications. He is a frequent contributor of articles and tutorials to the open source community, and the author of six books on PHP, MySQL and XML technologies.

    Sitemap | Contact Us