How to configure the DynamicMapper sample to pull categories based on group membership
Out of the box, WCM allows you to map categories within WCM to user profile objects. This is useful for WCM Profiling, so that you can profile WCM objects to a user based on the categories that the user has associated with their profile.
Although this is useful, if there is a large user base, it may be desirable to assign categories to the user based on their group membership. That way, all users within a given group will be assigned the categories that have been assigned to the group.
In order to do this, you will leverage the DynamicMapper sample that is provided with WCM within the /WCMHOME/samples/DynamicMapper directory of the WCM product install. The way that the sample is provided is very basic, and is not usable for this process. This document will walk you through setting up WCM to pull categories based on the group membership, and add those categories to the WCM user profile.
The way that this process works is that when a WCM user logs in, and they access a WCM object that needs to determine what categories that the user has associated, WCM will make a call to the dynamic mapper sample code if it exists. So if it does not exist, WCM will only get the categories for the user that either the users themselves have selected, or that they have been assigned through the WCM Authoring UI. An example of when WCM will lookup the user category information is for when the profilecmpnt tag is used, or when a menu component thats pulling the current user categories as a search rule.
If the code does exist, it will make a call to the code, passing the current session to the code. The code will then take the session thats passed, and set a session attribute for the session of a List object of the categories that the user will have assigned to them.
1. WP is stand alone. You can set this up in a cluster easily, but we will focus on stand alone instance for this example. If you have a cluster, all that you would have to do is checkin the WMM files to the cluster.
2. LDAP security is enabled. You can use this same sample for other configurations, but you will have to change where the wmm attribute is stored, and where its retrieving its values from.
For this example, we will be pulling LDAP attribute values from groups. In order to do so, we need to create a WMM mapping from a WMM attribute, to the LDAP attribute that we will be retrieving the values from.
This will allow us to retrieve the WMM attribute when we try to get the group categories and keywords.
Now that we have the WMM attributes, we need to map these wmm attributes to values within the LDAP. In the wmmLDAPServerAttributes.xml file, add the following:
So for this example, I am mapping the category names to the cn value from the group. You can map this to any string attribute within your LDAP, including creating your own attribute. For this example, I am mapping the Groups attribute to the CN values of my groups, which is basically a test to prove that the process works. You can use this if you want to just create categories that match the group names, and that may be useful, but you can also map to any attribute that holds a string. The Keywords mapping is shown for illustrative purposes, I won't be using the values for this example.
Since we will be pulling categories based on the group cn value in the above example, we will have to create those categories within WCM so that the user can have the categories associated. For my user in this example, my test user is a member of many different groups. For the context of this example, however, I will only focus on 2 groups, wcmadmins and wpsadmins.
Login to WP as a wcm administrator, and navigate to the authoring UI. Using the library that you will be using for the rest of the example, we will create the necessary categories.
If you do not already have a taxonomy created, create a taxonomy. Then, create categories that will match the users group membership. For my example, I just created 2 categories named wcmadmins and wpsadmins:
Finally, in a WCM presentation template, add the following so that we can view the current user's categories:
When we render a piece of content that references that profilecmpnt tag, we will see the current user's category selections.
Once the attributes have been set up, now we have to create the code that will actually map the atttributes to the user session. In order to do so, we have to implement some coding. Just a note, not all of the files will be used for the sample, but in order for the implementation to be used, they all have to be present, including the /wcm/attributemapping.properties
The /WPHOME/wcm/samples/DynamicMapper directory has a source directory. You will import this into your development environment, so that you have com/ibm/workplace/wcm/sample/dynamicmapper, with all of the .class and .java files for the classes that are necessary for the sample. Once you import in (here's from eclipse):
Note that WMMDynamicMapperSample is just a project that I created within eclipse. Once you have the files in eclipse, you have to set up classpath variables for your project so that you can change and compile code. I added entries to:
This will give us references to the objects we need to complete our code.
For this example, we can leave all of the existing classes and .java files mostly alone.
First, we have to add some information to the existing .java files. Open the SampleWCMSessionKeyConstants.java file, and add the following to the list of constants:
That way, we will be able to use this as a constant for later use.
Now, we will create a new class in the same com.ibm.workplace.wcm.sample.dynamicmapper package named WCMGroupProcessor:

/********************************************************************/
/* Licensed Materials - Property of IBM */
/* (c) Copyright IBM Corp. 2005. All rights reserved. */
/* */
/* US Government Users Restricted Rights - Use, duplication or */
/* disclosure restricted by GSA ADP Schedule Contract with IBM */
/* Corp. */
/* */
/* DISCLAIMER OF WARRANTIES. The following [enclosed] code is */
/* sample code created by IBM Corporation. This sample code is */
/* not part of any standard or IBM product and is provided to you */
/* solely for the purpose of assisting you in the development of */
/* your applications. The code is provided "AS IS", without */
/* warranty of any kind. IBM shall not be liable for any damages */
/* arising out of your use of the sample code, even if they have */
/* been advised of the possibility of such damages. */
/********************************************************************/
package com.ibm.workplace.wcm.sample.dynamicmapper;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import com.ibm.portal.puma.*;
import com.ibm.wps.puma.User;
import com.ibm.wps.puma.Group;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.List;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
/**
* WCMGroupProcessor looks for a Portal User in a passed HttpSession.
* If a Portal User is found, then we will add 2 keys to the user for Dynamic
* Categories and Keywords. These values come from fields in the ldap user records.
* This would also apply to any external repository connected through WMM.
*/
public class WCMGroupProcessor
{
/** The logger for this class */
/** Gets the separater used when processing keyword/group lists */
private static final String m_separater = SampleMappingProperties.getSeparator();
private boolean isDebug = Boolean.getBoolean(SampleMappingProperties.getProperty("DebugMode"));
public WCMGroupProcessor() {
System.out.println("WCMGroupProcessor constructor");
System.out.println("WCMGroupProcessor debug mode "+isDebug);
}
private boolean getDebug() {
return isDebug;
}
private void setDebug(boolean debug) {
isDebug = debug;
}
/**
* This method is called when a category or keyword based user search is done.
* If the user's dynamic categories and keywords do not exist, then we will
* add them in this method.
* @param p_event
*/
public void processGroupInfo(HttpSession session)
{
if ( getDebug() ) {
System.out.println("WCMGroupProcessor processGroupInfo called");
}
//ResourceBundle sampleBundle = ResourceBundle.getBundle(SampleBundleKeyConstants.BUNDLE_NAME);
User user = null;
if (session != null)
{
user =
(User) session.getAttribute(
com.ibm.wps.util.Constants.SESSION_USER);
if (user == null)
{
user = (User) session.getAttribute("wcm_session_user");
}
}
String username = "";
try {
username = user.getCommonName();
} catch (Exception e) {
System.out.println("WCMGroupProcessor Error getting username");
}
if ( getDebug() ) {
System.out.println("WCMGroupProcessor processGroupInfo called for user "+username);
}
//if the User exists, then that means that the user has logged into portal and we can add dynamic categories
//and keywords onto the user. For each case, we will get the values from the ldap user's group records.
//Then we will convert the value of the property into an arraylist and put it into the session.
//The keys for the properties are WCM:CATEGORYS and WCM:KEYWORDS.
if (user != null)
{
// First get the existing user categories.
List cats = new ArrayList();
String userCats = (String) user.getAttribute(SampleWCMSessionKeyConstants.USER_CATEGORIES_KEY);
// Next get the existing user keywords.
List keys = new ArrayList();
String userKeys = (String) user.getAttribute(SampleWCMSessionKeyConstants.USER_KEYWORDS_KEY);
if ((userCats == null) && (userKeys == null))
{
// Then get each group and loop through them, concatenating the lists.
// Get the group hierarchy for this user
List groups = null;
groups = getGroups(user);
for(Iterator iter = groups.iterator(); iter.hasNext();)
{
Group group = (Group) iter.next();
// Get this group's Categories
String groupCats = getGroupAttribute(group, SampleWCMSessionKeyConstants.GROUP_CATEGORIES_KEY);
if (groupCats != null)
{
cats.addAll(getPropertiesAsList(groupCats));
}
// Get this group's Keywords
String groupKeys = getGroupAttribute(group, SampleWCMSessionKeyConstants.GROUP_KEYWORDS_KEY);
if (groupKeys != null)
{
keys.addAll(getPropertiesAsList(groupKeys));
}
}
// Finally, save the extended lists to the session.
if ( getDebug() ) {
Iterator catIterator = cats.iterator();
Iterator keyIterator = keys.iterator();
while(catIterator.hasNext()) {
System.out.println("WCMGroupProcessor adding category to the session "+(String)catIterator.next());
}
while(keyIterator.hasNext()) {
System.out.println("WCMGroupProcessor adding keyword to the session "+(String)keyIterator.next());
}
}
session.setAttribute(SampleWCMSessionKeyConstants.USER_CATEGORIES_KEY, cats);
session.setAttribute(SampleWCMSessionKeyConstants.USER_KEYWORDS_KEY, keys);
}
}
}
/**
* This is a helper method that takes in a property value and converts it to an array list using the SampleMappingProperties.getSeparator()
* as the Tokenizer.
* @param p_values
* @return
*/
private List getPropertiesAsList(String p_values)
{
ArrayList valuesList = new ArrayList();
if (p_values != null)
{
StringTokenizer st = new StringTokenizer(p_values, m_separater);
while (st.hasMoreTokens())
{
String value = st.nextToken();
valuesList.add(value);
if ( getDebug() ) {
System.out.println("WCMGroupProcessor getPropertiesAsList adding "+value);
}
}
}
return valuesList;
}
/**
* This is a helper method to retrieve the user's nested groups and handle
* all possible thrown exceptions from that call.
* @param user
* @return
*/
private List getGroups (User user)
{
List groups = null;
try
{
if ( getDebug() ) {
System.out.println("WCMGroupProcessor getGroups called for "+user.getName());
}
groups = user.getNestedGroups();
}
catch(com.ibm.portal.puma.MemberNotFoundException ignore)
{
}
catch(com.ibm.portal.puma.InvalidMemberIdException imie)
{
System.out.println(
"WCMGroupProcessor.getGroupAttribute() Error accessing groups for user "+user.getName());
System.out.println(imie.toString());
}
catch(com.ibm.portal.puma.SchemaViolationException ignore)
{
}
catch(com.ibm.wps.util.DataBackendException dbe)
{
System.out.println(
"WCMGroupProcessor.getGroupAttribute() Error Error accessing data backend");
System.out.println(dbe.toString());
}
return (groups);
}
/**
* This is a helper method to retrieve a named attribute from a group object
* and handle all possible thrown exceptions from that call.
* @param group
* @param attributeKey
* @return
*/
private String getGroupAttribute (Group group, String attributeKey)
{
String attributeValue = null;
try
{
if ( getDebug() ) {
System.out.println("WCMGroupProcessor getGroupAttribute called for "+group.getName());
}
// CMK need to append the library name
String libraryName ="";
libraryName = SampleMappingProperties.getProperty("DefaultLibrary");
if(libraryName != "") {
libraryName=libraryName+"|";
}
attributeValue = libraryName+(String) group.get(attributeKey);
}
catch(com.ibm.portal.puma.MemberNotFoundException mnfe)
{
System.out.println(
"WCMGroupProcessor.getGroupAttribute() Error accessing group "+group.getName());
System.out.println(mnfe.toString());
}
catch(com.ibm.portal.puma.InvalidMemberIdException imie)
{
System.out.println(
"WCMGroupProcessor.getGroupAttribute() Error accessing group "+group.getName());
System.out.println(imie.toString());
}
catch(com.ibm.portal.puma.SchemaViolationException ignore)
{
}
catch(com.ibm.wps.util.DataBackendException dbe)
{
System.out.println(
"WCMGroupProcessor.getGroupAttribute() Error Error accessing data backend");
System.out.println(dbe.toString());
}
if ( getDebug() ) {
System.out.println("getGroupAttribute returning "+attributeValue);
}
return (attributeValue);
}
}
Once you save and compile that class, you are ready to move the code to your server. However, let me touch on what this does from a higher level. What happens is when WCM is looking for the users categories, it first loads the categories that it has stored in the WMM database, which is what happens when users select the categories for themselves, or when you associate user profile with the user object in the WCM Authoring UI.
What that is doing is adding the library name and “|” in front of the category name. The reason for this is that in v6, we have to know what library we will be pulling the categories from. In order for this to work, just add one line to the /WPHOME/wcm/shared/app/wcm/attributemapping.properties file. You will learn about this file a little later, just note that it does not exist in this location by default, you have to copy it from /WPHOME/wcm/samples/DynamicMapper/config.
Without quotes. My file has:
Now that you have created the code, you have to export the code from your project so that it can be picked up by WCM. In eclipse, I exported as a .jar file:
After the .jar file is exported, then you will place the .jar file in /WPHOME/wcm/shared/app directory. Also, create a /wcm directory within the same /WPHOME/wcm/shared/app. In the /wcm directory, copy the /WPHOME/wcm/samples/DynamicMapper/config/attributemapping.properties file to /WPHOME/wcm/shared/app/wcm directory.
After you place the .jar file, and the .properties file, restart the WCM server.
Once you have placed your code out there, now we have to ensure that we have categories in the WCM library that we have configured in the /WPHOME/wcm/samples/DynamicMapper/config/attributemapping.properties file that match the categories that we are adding to the user session.
For this example, I have included a sample .jar file that contains all of the necessary code listed above, as well as a sample attributemapping.properties file. Place the attributemapping.properties file in /WPHOME/wcm/shared/app/wcm folder, which you will create, and the CKDMSample.jar file in /WPHOME/wcm/shared/app, and restart the server.