Jivespace Community Blog

5 Posts tagged with the spring tag

This post is the seventh and final post in a series of blog posts about customizing for Clearspace 2.x. The previous posts covered:

  1. Customizations in Clearspace 2.x

  2. Upgrading Themes and FTL Files.

  3. Widgets in Clearspace 2.x

  4. Macros for Clearspace 2.0

  5. Web Services

  6. Custom Authentication and User Data Providers

 

Some of the most important changes in version 2 of Clearspace required that the underlying API be changed. To get things working on version 2, you'll need to change not only references to these in your Java classes, but also references to them in your FreeMarker templates.

 

The following describes the most significant changes.

  • Inject Dependencies with Spring

  • Work with Containers for Projects and Spaces/Communities

  • Use Transaction Support When Updating Multiple Tables

  • Use Manager Interfaces to Work with Managed Things

  • Handle Content As XML

  • Other API Changes

 

Inject Dependencies With Spring

For version 2, the Clearspace 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 Clearspace must use the same conventions in order to interact reliably with Clearspace. For example, as described below, dependency injection replaces version 1 conventions for using JiveContext and factory classes to get manager instances.

 

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 Clearspace 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 in Clearspace 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, Clearspace 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

 

Work with Containers for Projects and Spaces/Communities

Version 2 introduces projects, which, like spaces (known as communities in Clearspace Community), collect content such as documents, discussions, and blogs. Projects also add tasks as a content type. To organize the conceptually similar projects and spaces, the API was changed to introduce the idea of a container. The interfaces that represent projects and spaces  -- Project and Community  -- now inherit from a common JiveContainer interface.

 

In practical terms, this means that some methods taking an instance of the Community interface now take a JiveContainer instance instead. The following code works to get the latest document in either a project or space, for example, because they inherit from JiveContainer and both can contain documents. To see how your code might be affected, search the Javadoc index for occurrences of JiveContainer in method signatures and return types.

 

public Document getLatestDocument(JiveContainer container) {
    if (getDocumentCount(container) == 0) {
        return null;
    }
    DocumentResultFilter filter = new DocumentResultFilter();
    filter.setSortOrder(DocumentResultFilter.DESCENDING);
    filter.setSortField(JiveConstants.MODIFICATION_DATE);

    // Round down the lower date boundary by 1 day.
    filter.setModificationDateRangeMin(ThreadResultFilter
        .roundDate(container.getModificationDate(), 24 * 60 * 60));
    filter.setNumResults(10);
    Iterable<Document> documents = documentManager.getDocuments(container, filter);
    if (documents.iterator().hasNext()) {
        return documents.iterator().next();
    }
    else {
        return null;
    }
}

By the way, this getLatestDocument method is now exposed by the DocumentManager interface; in version 1 this method was exposed by the Document interface. Methods such as this one were moved as part of a larger effort to migrate such functionality into managers.

 

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 Clearspace 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:

 

 

Use Manager Interfaces to Work with Managed Things

Much of the Clearspace code was rewritten to clarify (or create) relationships between instances of manager interfaces and instances of what they manage. The result is an API that's more intuitive, but it also might mean changes to your code.

 

In general, methods designed to handle types of things  -- documents, attachments, discussion messages, and so on  -- were moved from a "containing" type to a manager type. For example, in version 1 the method getAttachmentCount was exposed by the interfaces BlogPost, Document, and ForumMessage ; each of these could have attachments, and getAttachmentCount was how you got the count of them. In version 2, you get the count of attachments with the AttachmentManager.getAttachmentCount(AttachmentContentResource) method. The AttachmentContentResource is a marker interface extended by BlogPost, Document, ForumMessage, and other things that can have attachments.

 

So in version 2 the rule of thumb, when looking for a way to manage types of things, is to look for a manager of those things.

 

Handle Content As XML

Version 2 includes significant changes in the way Clearspace handles content. Improving the rich text editor (which blogs, documents, and discussions threads all use) included completely reworking the way that content is handled in conversions between plain text (wiki markup), rich text, and HTML (for previewed and published content).

 

In particular, content is set and received as XML. In version 1, the content-related accessors that get and set content body, subject and so on received and returned String instances. In version 2, these methods handle content as instances of org.w3c.dom.Document. Version 2 interfaces that used content accessor methods  -- including Document, PrivateMessage, ForumMessage, BlogPost, and others  -- still include methods such as getPlainBody for retrieving content without markup.

 

For plugins that include actions, you can extend JiveActionSupport for several methods helpful in converting content from one format to another.

 

While version 2 features a model for setting and getting content as XML, there's also an exception: the Macro interface render method still returns content as a String. It has to parse as well-formed XML, but it's still a string.

 

Other API Changes

 

  • In some cases, methods that took a JiveObject instance now take an instance of an interface that extends JiveObject. The marker interfaces CommentContentResource and AttachmentContentResource were created to indicate an object's support for comments or attachments. Look for these types in the Javadoc index to find out whether you need to update your own code.

 

 

The information above along with more details can be found in the Upgrading Extensions to 2.0 documentation.

 

 

1 Comments Permalink

We've been noticing more and more discussion on Jivespace about people migrating older customizations to the new Clearspace 2.x architecture along with quite a few people writing new customizations. I thought that now would be a good time for a refresher series of posts on customizing for Clearspace 2.x

 

Clearspace version 2 includes a few big changes to the conventions and frameworks on which Clearspace is built. Many of these changes were made to improve Clearspace extensibility by letting the application and extensions be more loosely coupled -- and so be more durable during upgrade. Other changes that impact customizations were simply feature improvements in which some change to code is required.

 

Here's a high-level list of the changes that effect extension and customization code:

 

  • Clearspace was migrated to the Spring framework. This has both broad and deep effects on code written to run on Clearspace; such code must use the same conventions in order to integrate well. The changes include API refactoring to support dependency injection and integration of Spring modules (for security and transaction management, for example).

  • Clearspace was migrated from WebWork2 to Struts2. This has broad impact on plugins, but the changes are relatively small  -- primarily effecting syntax in FreeMarker templates and configuration files.

  • Widgets were enhanced to support use in multiple contexts, rather than just space overview pages. Changes to widget development support this new feature.

  • The macro-related API was narrowed to the area documented in version 1, essentially excluding an undocumented area that some people used. Also, macros must now return HTML that qualifies as well-formed XML.

  • Web services support is now provided through CXF, an evolution of XFire (the version 1 technology). A larger change results from the migration to Spring, which makes integrating new web services a bit more complex.

  • Custom user data providers are now based on a streamlined API. These must be rewritten, but the new model is much simpler.

  • Custom authentication providers are now based on Spring through Acegi. The change is pretty significant, including new API and configuration conventions.

  • Myriad API changes resulted from the migration to Spring and from an effort to streamline and modularize the Clearspace API. These include support for the projects feature, refactoring manager interfaces, content handling, and conventions such as dependency injection.

 

For those extensions and customizations deployed as plugins (widgets, actions, macros, and web services), there are a few changes in version 2 with broad effect.

 

  • Migration from WebWork to Struts means replacing your xwork-plugin.xml with a struts.xml

  • Migration to Spring means adding a spring.xml for certain kinds of Spring support.

  • One benefit of the new model is that modifying your plugin.xml, struts.xml, or spring.xml will provoke Clearspace to reload your plugin. This can be handy for debugging.

  • Support for a <maxserverversion> element was added. Similar to the <minserverversion> element, use the new one to specify that highest Clearspace version on which your plugin is supported. This can be useful when you want to ensure that upgrades to Clearspace prompt the plugin's users to upgrade the plugin also.

  • Plugin life cycle methods changed. If you used the version 1 com.jivesoftware.base.Plugin interface, you'll notice that its two methods have changed in order to support Spring. Semantics for the methods is unchanged.

    • Plugin.initialize(PluginManager, PluginMetaData) is now Plugin.init, a method without parameters. Use Spring dependency injection to receive a PluginManager instance; you can retrieve a PluginMetaData instance from the manager.

    • Plugin.destroyPlugin is now Plugin.destroy.

 

The information above along with more details can be found in the Upgrading Extensions to 2.0 documentation.

 

 

0 Comments Permalink

Clearspace 2.0 made several improvements to the internal security mechanisms within the product that serve to streamline and standardize the way authentication, authorization and auditing occur within the application. Following on Dolan's blog about Spring in Clearspace 2.0, this post will cover the Acegi-related changes for 2.0 security.

 

Motivation

There were several motivations for using Acegi Security in Clearspace 2.0:

  • Reuse of standard, community reviewed code - At the time of writing, Acegi is planned to be incorporated into Spring proper in the near future. Particularly with security-related code, community review is essential to producing a more robust end result.

  • Removal of unnecessary, Jive-specific code - There really wasn't a need for us to manage our own authentication framework, authorization framework, X509 handling, etc. when Acegi has already made available a solid implementation. Additionally, Acegi gives us out of the box AuthenticationProvider and Filter implementations for things that previously we had to roll from scratch such as SiteMinder integration or X509 authentication. Acegi's implementations will still require some customization, largely due to the way we perform authorization, but the jive-managed code can be substantially reduced.

  • Community - As with Spring, Acegi has a healthy community that we can leverage, both through shared code as well as by contributing back to the project.

  • Flexibility - Acegi is well written and tends to give us extension points where we need them. For example, we needed to support existing LDAP configurations from Clearspace 1.x installations. This required us to inject custom LDAP search properties into the Acegi BindAuthenticator which was easily done through the setUserSearch method on the authenticator.

  • Leverage Existing Enterprise Competencies - Acegi and Spring are fairly well known in Java enterprise development. Existing skill sets around Acegi will translate to SSO implementations with Clearspace 2.0 as opposed to the 1.x model which required a customer's developers to learn an entirely new security model.

 

Implementation

Authentication

The Clearspace 2.0 authentication model follows standard Acegi authentication, defining a Spring-managed FilterChainProxy bean in web.xml which then delegates to URL-mapped filter chains. These chains manage various security-related concerns including Session expiration, authentication cookie management, password encoding, user profile loading and federated identity features of Clearspace. One notable change is the move to a stronger password hash using a SHA-256 hash of a salted password. Additionally, the amount of Jive-specific LDAP code has been dramatically reduced instead delegating to Acegi's LDAP lookup and bind implementation.

 

Authorization

Clearspace 2.0 authorization has not changed substantially from 1.x with the exception that contextual information about a user is now accessed via the Acegi SecurityContext rather than explicitly passed through the application as AuthToken objects. This change focused the APIs on business concerns and moved security concerns to more of a cross-cutting realm, a change we're leveraging in 2.1 to make authorization driven by annotation rather than proxied objects. The hope for 2.1 is that this will greatly reduce the amount of authoirzation code while improving security.

 

Auditing

Acegi fundamentally feeds into the new auditing features of Clearspace 2.0. Based on contextual authentication information accessed by Acegi's SecurityContext, the auditing functionality logs operations performed by the effective user performing an action in the system.

 

Customization

 

Customization of authentication and authorization have several extension mechanisms in Clearspace 2.0 and the required APIs have been simplified. The older AuthFactory class has been removed and implementing jive-specific interfaces is no-longer required to customize the authentication mechanism. The goal for 2.0 authentication customization has shifted focus to a more modular, composition and inversion of control-driven approach. This better aligns with the Spring-standard Acegi approach and focuses customization on Spring-managed filters and/or AuthenticationProvider implementations. The Clearspace 2.0 documentation has more information on creating new authentication customizations or migrating 1.x authentication customizations.

 

 

 

 

The hope for these changes is that they will improve, standardize and simplify security as it exists in Clearspace. Let us know your feedback!

 

 

0 Comments Permalink

For those of you who are fans of Spring, or those who are just plain curious, I'd like to briefly go over the parts of the framework we used in the latest release of Clearspace.  One of our goals for the new release was to take advantage of the power the framework offered where it made sense to us, and in the end it turned out Spring and Clearspace are a good fit for eachother.

 

Core Context and Struts: The JiveContext now extends the Spring ApplicationContext.  Everything registered with the context is a Spring bean.  Struts actions and interceptors are autowired using these bean definitions.  Writing Actions just got a whole lot simpler.

 

Data Access: We changed a lot of our code to use Spring's JdbcTemplate and SimpleJdbcTemplate.  This has the advantage of making our DAO code much simpler and less error prone, as well as quicker to write.  Some classes ended up half the size after this effort.  We used annotation-based transactions, wired in using Spring's AspectJ support via the AnnotationTransactionAspect.  This allowed us to quickly add transactional coverage of far more DAO methods than before, and more easily make methods transactional in future development.  Finally we used Spring's LDAPTemplate in several places to simplify directory access code.

 

Security: We're using Acegi (Spring Security) for authentication.  This allows the possibility to authenticate against more than one data store.  For example, you could have one account that authenticates against LDAP, and a separate machine account that authenticates against a database.  It also provides a well documented, peer-reviewed framework to use as a platform for developing custom authentication solutions.  We are investigating using Acegi more fully for authorization in future releases.

 

Tasks: We worked hard to externalize timed tasks so that they each have bean definitions.  This gives greater insight into what is being fired, why, and where.  It also makes it much easier to control when a task is firing, or turn it off completely for testing purposes.

 

Plugins: We allow plugins to add their own Spring bean definitions to the context, so that Plugin actions and interceptors can be autowired as expected.  This is done via the plugin's "spring.xml" file.

 

Web Services and AJAX: Apache CXF is now used to expose SOAP and REST style web services.  It relies heavily on Spring for its configuration.  We are also using Spring for DWR configuration, via the Spring 2.0 DWR namespace.

 

Customizations: As part of the new release we also parse any XML file in the <jive home>/etc directory with the expectation that it's a Spring configuration file.  Developers can use this to extend or override bean definitions in the core application context.

 

In the end, we found Spring very helpful to simplifying our codebase and providing a point of cohesion.  We look forward to using it even more fully as we continue to develop Clearspace, and as the Spring framework itself evolves.

0 Comments Permalink

Spring Is Coming

Posted by Dolan Halbrook Jan 17, 2008

Among the changes being made to Clearspace during 2008 is the adoption of the Spring framework in many parts of the application.  The first and most obvious feature of Spring of which we are taking advantage is the well-known and robust configuration and dependency injection service.  Much of what was formerly done using the JiveContext is now delegated to a Spring ApplicationContext, and this trend will continue.  This enables easier integration with existing libraries, more pluggability and testablility, and greater familiarity to the developer world.  What is more, with our upgrade to Struts2 (from WebWork2) all Struts objects are created using Spring as the object factory.  The immediate benefit to the developer is that any Spring-managed dependencies in a Struts object (actions, interceptors, etc) will automagically be set using Spring autowiring.

 

JDBC and Transactional support are other areas where we have attempted to make use of the facilities provided by the Spring framework.  Indeed anyone who has used the code provided by the framework for those two areas will be clear as to why we have chosen to do so.  Because of the improved ease of layering transactions declaratively, much more of the codebase will be transactional than before, leading to improved data integrity across the system.  We have also taken advantage of Spring LDAP support, DWR integration, CXF integration, and OSWorkflow integration.  Last but not least, we have worked to integrate the Acegi framework for authentication, and are working to use it for authorization as well.

 

The inclusion of the Spring framework into our project lays the groundwork for many benefits in upcoming versions of Clearspace.  We hope that you're as excited as we are to explore the possibilities and reap the benefits of its arrival.

0 Comments Permalink