Trifork Blog

Spring context configuration in Magnolia

May 31st, 2012 by
|

Here at Trifork we have quite a Magnolia portfolio. We do not only install, configure and host the actual CMS itself alone, we also design and implement pixel-perfect designs, and last but certainly not least: integrate new or existing business processes from external resources.

When we create any piece of software for such an integration, that software is 90% of the time a Spring configured application. For example, when  we need to load content from an external database into the Data module, or when we want to create a Blossom dialog that renders a select box based on results from an external web service. We create these services in a separate abstraction layer, like most of us would, so that we end up with a nice separate, reusable and testable module. The services we create are usually configured in a number of Spring contexts.

Loading your contexts in the Magnolia admin central.

Are you building your own Magnolia modules instead of Magnolia (1)? Are you letting your modules configure your Magnolia instance (2)?
If the answer to both of these questions is yes, and you do a fair bit of Spring in your modules, you might want to read on!

(1) Grégory Josephs’: Don’t build Magnolia: build your projects.
(2) Grégory Josephs’: Don’t configure Magnolia: let your projects configure it.

How would we usually configure such a module

When you want to enable any kind of Spring in your Magnolia web application, you will probably add Springs’ ContextLoaderListener in your web.xml and either load all of the contexts directly using the contextConfigLocation parameter, or perhaps import ’em all in your root application context.

Your regular contextConfigLocation looks like the web.xml excerpt below. There is nothing new going on here. Just plain old stuff.

	...
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<filter>
		<display-name>Magnolia global filters</display-name>
		<filter-name>magnoliaFilterChain</filter-name>
		<filter-class>info.magnolia.cms.filters.MgnlMainFilter</filter-class>
	</filter>
	...
	<listener>
		<listener-class>info.magnolia.cms.servlets.MgnlServletContextListener</listener-class>
	</listener>
	...

What is the issue here, you might think? Well, nothing to be quite honest. This works like a charm, and when the ContextLoaderListener is placed above the MgnlServletContextListener, your services will even be able to access the Magnolia repositories. But when you are creating multiple modules that are reusable in multiple applications, that have multiple contexts, module dependencies etcetera, setting up and maintaining these configurations can become quite a hassle.

In my opinion: when you want to add a module to Magnolia, whatever framework is used, the fact that you will possibly have to update the web.xml, modify any existing contexts to make it work is pretty annoying. This approach also does not fit in Greg’s “Don’t configure Magnolia: let your projects configure it.” principal. It should just work out of the box!

Ah, Spring in Magnolia you’re saying? You mean Blossom!!

This is something that often Springs to mind when discussing Magnolia in combination with Spring (no pun intended). Blossom is a great way of using dynamically generated Dialogs and their content validators, VirtualURIMappers or Spring MVC controllers in Magnolia (and much more). But the issue that I want to touch upon is purely the configuration of your Spring contexts in your module.

Usually there is already a ContextLoaderListener and root context present in your app. But when we actually want to enable a module like say Blossom in our existing web application, you guessed it…, you will perhaps have to edit your web.xml again and add some more contexts to your existing application.
For each and every module with an own configuration you’d have to repeat these steps.

So when I am talking about Spring context loading in Magnolia, I really just mean the context loading itself. Either for using Blossom, or some of our own many modules, the integration of the actual context loading and managing the configuration is not very subtle or centralized.

So then what would the ideal scenario look like?

Magnolia already has a very neat way of installing, updating and configuring modules. You can register dependencies in the config, or perhaps deltas in your version handler, and perform elaborate update tasks.
Why not make use of all of these great features and actually take control of Magnolia’s rich dynamic configuration mechanism. It would be very fancy if one could just create a configuration node in a module, specifying any contexts that need to be loaded. Let the Magnolia ObservedManager pick up any values that I set in the config for handling changes runtime. We could also export these configuration nodes, and bootstrap them upon version change. This way the configuration of a module is completely defined in the admincentral.

We at Trifork are using Maven a lot in our projects. With modules like this we would just have to add a dependency to the application, and that’s it! The contexts are automatically added to the root context.

This is exactly what we did a couple of years ago with our Magnolia Spring context configuration module. Simple is beautiful, as appears in just about every Magnolia adminCentral you open.

Set up instructions

The Magnolia Spring context configuration module is a maven module that you will have to add to your website’s pom.xml. From that moment on, all your context configurations will be managed inside the Magnolia admin central and not in your webapp structure.

Within the config node of your module, we can specify the spring-contexts and the spring-beans (override-able properties) that this module needs. In the example below, two contexts are loaded: the ‘spring-ws-context’ and an ‘external-db-context’. The contexts are expanded to show you the configuration needed for a context.

  • The context-path node contains the import path to your context.
  • The enabled node, gives you the option of enabling or disabling a context.
    This might be useful for enabling an application on an author instance only, or whatever case you might have.

If you have any settings that need to be configured, you can put these in the spring-beans folder as shown below:

The above configuration creates two String beans in the Spring context. A solrServerUrl String bean and a wordPressJsonApi String bean. Set the type column accordingly.
Currently supported bean types are:

  • Long / Integer
  • String
  • Boolean

Only these three types are available due to lack of effort by me. You could create brilliant structures to configure complete beans with references, lists, maps etc., but these eventually need to be in your context, and not in your configuration. This might be a feature for the future, but in the last years, these bean types were more than sufficient.

NOTE: Providing a sample-config node in your module as shown in the illustration,
could prove useful to show all of the available properties that are available in 
your config.

Upon first start up, I usually run into the fact that I forgot to configure my ContextLoaderListener all together. But that might be just me.. This would result in the following error in your log:

ERROR SpringContextLoadingModule: No WebApplicationContext found. The Spring listener must be loaded first in the web.xml, above the Magnolia listener.

When you start up Magnolia after fixing the above and with the configuration in place as discussed earlier, Magnolia will startup clean and you can see the following entries in your log:

DEBUG SpringContextLoadingModule: Context configuration 'spring-ws-context' is enabled.
DEBUG SpringContextLoadingModule: Context configuration 'external-db-context' is enabled.
INFO  SpringContextLoadingModule: Loaded 2 contexts for my-module
DEBUG SpringContextLoadingModule: Creating String bean wordPressJsonApi
DEBUG SpringContextLoadingModule: Creating String bean solrServerUrl
INFO  SpringContextLoadingModule: Loaded 2 bean definitions for my-module

When your configuration is finished, just export the configuration nodes, add them to your VersionHandler that will bootstrap them upon install. You are also free to do some version management, like removing old definitions, or renaming or adding new ones to your module.

	installOrUpdateTasks.add(new BootstrapConditionally(
					"Context config",
					"Bootstrap the context configuration for MyModule",
					"/path/to/context-config.xml"));
NOTE: Use the BootstrapConditionally task instead of the BootstrapSingleResource
for your spring config folders. You might not want to overwrite any pre-existing 
configuration settings.

Ok, that’s what I need. let me put it in my pom to give it a try!

We are working on a way of sharing our numerous Magnolia modules with you. For now if you are interested in this one, click here, fill out the form and we will send it to you…and then you can read this blog and take some action for yourself! Enjoy and remember if you need any help just contact us.

11 Responses

  1. May 31, 2012 at 10:17 by Hans Westerbeek

    Great post Erik, bridging the gap between pure-app .wars and ‘just cms’ projects has been a tricky technical problem and it seems you’ve crunched it

  2. May 31, 2012 at 14:11 by Grégory Joseph

    Hey Erik, great post !

    How about sharing your modules on the Magnolia Forge ? That way you won’t have to care about the infrastructure, we’ll provide it 😉 http://forge.magnolia-cms.com (git support coming up)

  3. June 1, 2012 at 14:49 by Boris

    Great post. The best way by far to share modules is visiting the Magnolia forge and talking to Greg about it.

    This way everybody in the community gets to see your contributions and you raise your visibility within the community. Of course you also benefit from the extensive infrastructure we provide.

    See you at the next Magnolia conference in September. You did propose a talk I hope?

    Cheers
    Boris

  4. March 16, 2013 at 00:36 by yong

    Erik, the link to form for request for example file is not there. Please put the link to download.

    Thanks,

    Chigo

    • June 10, 2013 at 18:33 by Erik Alphenaar

      Howdy Chigo,
      Updated the links! Should be OK now..

  5. August 27, 2013 at 16:44 by Arthur Oysgelt

    Thank you for your post. I was wondering whether you might be able to suggest an approach for a task I’m doing. I’m trying to create a bootstrap setup on our website such that the version handler bootstraps only those xml files that are in a subdirectory whose name matches the module version. Can you tell me how I might specify to the ModuleManager where to look for bootstraps?

    • August 27, 2013 at 17:21 by Erik Alphenaar

      Hi Arthur,

      We filter module specific property files using Maven.
      Our module property file (module-name.properties) contains: mgnl.module.version=${pom.version}
      This way we load the filtered property file in the version handler, and get the current projects’ version. We have an abstract version handler that takes care of all of the loading etc.
      In your case you could then loop through all XML files in the bootstrap directory for corresponding version number, and add them to your install tasks.
      Hope this helps!

      Rgds,
      Erik

  6. September 30, 2013 at 13:48 by FJ Giner

    I’m using your module for a new project, using Spring Data JPA too, but I found an error when the module is loading the context. For Spring Data I’m using Hibernate as persistence, but your module loads 2 times (at least) the persistence.xml or the persistence unit, and throws an error. I think your module is loading 2 times the app context and that causes the error, but i can’t find a solution. Can you explain me how to fix that?

    • October 21, 2013 at 16:56 by Erik Alphenaar

      Hi,

      Thanks for giving the module a shot, hope you like it!
      Yes, I stumbled upon the same issue too. The beans of the context are copied to the root context, and then re-instantiated. This causes the warning message “: HHH000436: Entity manager factory name (a name) is already registered.” you are probably referring to. The JPA stuff is working OK for me though.
      Could you create an issue on the JIRA?
      http://jira.magnolia-cms.com/browse/OCONTEXT
      I will then look in to this asap.

  7. May 20, 2014 at 13:04 by ropo

    There are two images missing in this blog. It is hard to understand the configuration without the images. Also the SpringComponentsConfigurer does not compile. it does not match SpringComponentConfiguration