Spring: Frequently Asked Questions

Spring: Frequently Asked Questions

Jive SBS uses various pieces of the Spring framework for dependency injection, security, data access, and more.

Why do I need to care about Spring?

For version 2, the codebase was refactored to support Spring, a framework with modules for inversion of control, transaction management, authentication, and more. This has potentially the biggest impact on your code because components that extend the application must use the same conventions in order to interact reliably. For example, as described below, dependency injection replaces version 1 conventions for using JiveContext and factory classes to get manager instances.

How do I learn more about Spring?

What is dependency injection?  How do you inject dependencies with Spring?

Dependency injection is a way to obtain a dependency, such as an object on which your code relies, by having the instance "injected" at run time -- that is, the instance is set with a JavaBeans-style accessor method. In application code, manager class instances are generally now injected rather than obtained via a context or factory class method.

A class that supports injection includes a class-level ++ variable for holding the instance it depends on and provides a set* accessor method through which Spring can inject the instance (in other words, your code provides a property of the interface's type). Your setter implementation assigns the injected instance to the variable, which your code can then use to access the instance. By specifying the interface on which your code depends -- rather than an implementation of the interface -- you have a loosely-coupled dependency. This lets you avoid breakage that might occur if the implementation changes.

Note: You can ensure that the setter you provide actually has a configured type associated with it by  annotating the setter method with @org.springframework.beans.factory.annotation.Required. If the injected type is not known to Spring configuration, then the Spring container will throw an exception at run time.

The following example illustrates the basics of dependency injection with Spring: declare private class-level variables for manager instances and declare setter methods through which Spring will inject the instances. Code for version 1 techniques has been commented out in favor of the Spring conventions.

// Variables to hold the manager instances.
private DocumentManager documentManager;
private FreemarkerManager freemarkerManager;

// Setters for injecting manager instances.
public void setDocumentManager(DocumentManager documentManager) {
     this.documentManager = documentManager;
}


public void setFreemarkerManager(FreemarkerManager freemarkerManager) {
     this.freemarkerManager = freemarkerManager;

}

private String getDocContent(String documentID)
{
     // ... Declare variables ...
   
         // Don't use JiveContext (from JiveApplication) to get a DocumentManager
         // instance. The instance has been injected through the setter above.
         // DocumentManager documentManager = JiveApplication.getContext(AuthFactory
         //    .getSystemAuthToken()).getDocumentManager();
            
         // Use the manager to get a document by its ID.
         document = documentManager.getDocument(documentID);
         Map properties = new HashMap();
            
         // ... Set FreeMarker properties from the document, then apply
         // a template with them ...
         result = applyFreemarkerTemplate(properties, FREEMARKER_HTML_FILE);
            
     // ... catch exceptions ...

     return result;
}

private String applyFreemarkerTemplate(Map properties,
              String templateName) {
            
     // ... Declare variables ...
        
     // Don't use getInstance to get the FreemarkerManager instance. It has
     // been injected.
     // FreemarkerManager freemarkerManager = FreemarkerManager.getInstance();
            
     // ... Use the manager to set up FreeMarker configuration ...
     config = freemarkerManager.getConfiguration(ServletActionContext.getServletContext());
     if (properties != null) {
         // Process the FreeMarker template and store the resulting HTML as a String
         result = applyFreemarkerTemplate(config, properties, templateName);
     }
            
     // ... catch exceptions ...

     return result;
}

While you can optionally name the specific interface implementation you want to have injected, Spring supports a feature known as autowiring. In autowiring, you merely include the setter method (using JavaBeans naming conventions) with a single parameter of the interface's type. To optionally specify the implementation you want injected, you include a spring.xml file that associates the implementation class with your setter.

Remove JiveContext uses that retrieve managers. In version 1 the JiveContext interface was a popular convention to get instances of the various manager interfaces -- CommunityManager, UserManager, DocumentManager, and so on. In version 2 this convention is, depending on the case, either unavailable or unreliable. For example, if you've written a plugin that has a static initializer that tries to bootstrap with call to JiveContext, application startup will likely fail.

Instead, use dependency injection as described above. In fact, to stay out of trouble, the general rule in version 2 is "Don't use JiveContext."

Remove getInstance calls that retrieve managers. This includes FreemarkerManager.getInstance, but also all of the *Factory.getInstance() methods you might have used in version 1. In version 2 most of the factory classes have been removed. You should replace your calls to their getInstance() methods with a property for setting the instance from Spring as described above. Here's a list of the removed classes:

  •   GroupManagerFactory
  •   UserManagerFactory
  •   AvatarManagerFactory
  •   BanManagerFactory
  •   PollManagerFactory
  •   SearchQueryLoggerFactory
  •   StatusLevelManagerFactory
  •   TagManagerFactory
  •   WidgetDAOFactory

What do we use the Spring framework for?  What parts are we using?

  • Spring Core -- The base framework of Spring is a dependency injection container that we're using for lifecycle and dependency mangement for our back-end: DAOs, managers and actions.
  • Spring Jdbc -- We use this nice package for DAO operations.
  • Spring Security -- Our login mechanism (formerly known as Acegi).
  • Spring AOP -- Used for doing dependency injection -- or inversion of control (IoC) -- of domain objects (non-singletons such as RenderManagers or UserProfile).  Also used to enable transactions.
  • Spring LDAP -- The basis of some of our LDAP access.
  • Spring Transactions -- This will be coming soon.

How is Spring bootstrapped? Where are the config files?

A ServletContextListener calls JiveApplication.initialize(), which bootstraps a Spring context.

How do I override a Spring bean definition?

Create a new bean definition xml with the same id. This can be in plugin-spring.xml or by creating your own Spring configuration xml file in jiveHome/etc. You could also do that programmatically. For example:
  

<!-- Root application definition - in spring-managerContext.xml -->
  <bean id="ratingManagerImpl" class="com.jivesoftware.clearspace.community.RatingManagerImpl"
      parent="jiveManager">
      . . .
  </bean>

  <!-- Override -- possibly in your plugin spring.xml -->
  <bean id="ratingManagerImpl" class="com.my.customized.MyRatingManagerImpl">
  </bean>

What is @Configurable?

This is a Spring aspect. Here are  some examples of how to use it.

Are my actions/widgets/plugins auto-wired with Spring beans?

Yes.

How do I use Spring in plug-ins?

Add a spring.xml to your plugin to define beans.

How can I disable web services and why might I like to do so?

The web services layer increases the startup time of your application.  If you are developing and need to do frequent restarts, and don't need web services, you would want to disable them.  To disable put this java property on your commandline:
  

-Djive.ws.disabled=true

How can I use PropertyOverrideConfigurer and JivePropertyOverrideConfigurer to make life simple again?

What this nifty class does is allow you to override properties for any bean definition. So, say you want to change the cron expression for the user sync task. Whip up a spring.xml file in <jiveHome>/etc, and add this bit of XML:

<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
     <property name="properties">
         <util:map>
             <entry key="userDataSynchronizationTask.cronExpression" value="0 0 1 * * ?"></entry>
         </util:map>
     </property>
</bean>

Boom -- you've just changed the task timing of task from midnight everyday to 1AM (assuming I got the cron expression right ). It gets better: you're not limited to overriding scalar values. You can actually override what bean gets injected where by using "value-ref" instead of "value".

JivePropertyOverrideConfigurer does the same thing, but it grabs the value to override from JiveGlobals. Here it is in action setting which group manager implementation to use:

<bean class="com.jivesoftware.community.lifecycle.spring.JivePropertyOverrideConfigurer">
     <property name="jivePropertyMappings">
         <util:map>
             <!-- beanID.beanProperty = jiveProperty value -->
             <entry key="groupManagerImpl.groupManagerClassName" value="GroupManager.className"></entry>
         </util:map>
     </property>
</bean>

Use transaction support when updating multiple tables

Version 2 supports transaction management through Spring with the @Transactional annotation. When the work of your code results in updates (including deletes) to data in multiple database tables, supporting transactional behavior can help to ensure that the updates are made consistently (that is, all of the effected tables are updated or none are).

If you're explicitly supporting transactions with @Transactional, you'll need to add an AspectJ compilation step. The AspectJ compiler weaves into your code the support needed to include at load time your transactional method calls in a transaction context.

You'll want to add transaction support if your code updates multiple database tables as while executing a method. While the need to support transactions is fairly rare if you're doing all your work through the API, keep in mind that your calls to API set* methods might result in database updates. If you're not sure whether your code's work results in database updates, it's not a bad idea probably to add the @Transactional annotation to the method.

Adding support for transactions is pretty easy. Here's what you do:

Add the @Transactional annotation to any methods whose execution might result in updates to multiple tables. The annotation is in the package org.springframework.transaction.annotation.

Compile your code with the AspectJ compiler. There are also Ant tasks.