Tuesday, January 5, 2016

IBM WCM 8.0 - virtual-portal-scoped actions

A challenge we came across when building our REST service for using WCM as a document library was: how do I target a virtual portal? WCM content libraries are created in a virtual portal. This means that, while you're working on the same VP as your content library, you can happily access WCM content via the Java API (see my example code in an earlier article). The problem comes in when you want to access content in a library on one VP, while your client code sits on another VP or are completely run outside of the Portal (as is the case in a REST client).

To make it possible to access content across virtual portals, IBM released a fixpack (CF13) that includes something called a VirtualPortalScopedAction and some additions to the Repository. This API comes standard with Portal 8.5, but for Portal 8.0/WCM 8.0 you need to upgrade to the right fixpack.

Diving in to a sample

I've created a Gist with some sample code, which is listed here.

The code uses the WCM Query API to fetch Category items (from the WCM API) on another virtual portal and library, using a hypothetical portlet. The main players in the code are:
  • VirtualPortalScopedAction interface to implement. In the run() method you can access the WCM API as if you are running in the correct virtual portal.
  • Repository.generateVPContextFromContextPath which is a CF13+ method to generate/get a context for a virtual portal different than the one you're currently on.
  • Repository.executeInVP(VirtualPortalContext, VirtualPortalScopedAction) executes your implementation of VirtualPortalScopedAction on the required virtual portal.

package example.wcm.vpscopedaction;
import com.ibm.workplace.wcm.api.Category;
import com.ibm.workplace.wcm.api.Repository;
import com.ibm.workplace.wcm.api.VirtualPortalContext;
import com.ibm.workplace.wcm.api.exceptions.WCMException;
/**
* Hypothetical Portlet that runs the query against a VP to fetch Categories.
* This can be a servlet or REST service as well.
*/
public class CategoriesViewerPortlet extends javax.portlet.GenericPortlet {
@Override
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
try {
// Get the Repository and generate VP Context from our VP name.
Repository repository = WCM_API.getRepository();
VirtualPortalContext vpContext = repository.generateVPContextFromContextPath("virtualPortalName");
// execute our action on that VP
FindCategoriesAction findCategoriesAction = new FindCategoriesAction();
repository.executeInVP(vpContext, findCategoriesAction);
// extract the info
List<Category> categories = findCategoriesAction.getCategories();
request.setAttribute("categories", categories); // or a String[] by accessing Category.getName() or whatever.
} catch (VirtualPortalNotFoundException e) {
// log this out somewhere or handle it
} catch (WCMException e) {
// log this out somewhere or handle it
}
getPortletContext().getRequestDispatcher("some/jsp/path.jsp").include(request, response);
}
}
package example.wcm.vpscopedaction;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.ibm.workplace.wcm.api.DocumentLibrary;
import com.ibm.workplace.wcm.api.Repository;
import com.ibm.workplace.wcm.api.VirtualPortalContext;
import com.ibm.workplace.wcm.api.VirtualPortalScopedAction;
import com.ibm.workplace.wcm.api.WCM_API;
import com.ibm.workplace.wcm.api.Workspace;
import com.ibm.workplace.wcm.api.exceptions.QueryServiceException;
import com.ibm.workplace.wcm.api.exceptions.WCMException;
import com.ibm.workplace.wcm.api.Category;
import com.ibm.workplace.wcm.api.DocumentLibrary;
import com.ibm.workplace.wcm.api.Workspace;
import com.ibm.workplace.wcm.api.exceptions.QueryServiceException;
import com.ibm.workplace.wcm.api.query.ResultIterator;
import com.ibm.workplace.wcm.api.query.Selectors;
import com.ibm.workplace.wcm.api.query.SortDirection;
import com.ibm.workplace.wcm.api.query.Sorts;
/**
* Virtual Portal Scoped Action to demonstrate how to access WCM items on a different Virtual Portal than the one
* the user is currently in.
*/
public class FindCategoriesAction implements VirtualPortalScopedAction {
private static final String LIBRARY_NAME = "WCM Test library";
private List<Category> categories = new ArrayList<Category>();
@Override
public void run() throws WCMException {
// Create a Repository and an anonymous workspace (can also use an authenticated workspace, but we'll keep it simple for now)
Repository repository = WCM_API.getRepository();
Workspace workspace = repository.getAnonymousWorkspace();
workspace.login();
DocumentLibrary library = workspace.getDocumentLibrary(LIBRARY_NAME);
/* Constuct the query. For the example, we're fetching all Category items in our library, sorted ascending.
*/
com.ibm.workplace.wcm.api.query.Query query = workspace.getQueryService().createQuery();
query.addSelector(Selectors.typeIn(Category.class));
query.addSelector(Selectors.libraryEquals(library));
query.addSort(Sorts.byName(SortDirection.ASCENDING));
query.returnObjects();
/* Make the categories available to client code.
* This is needed as the run() method does not return a value.
*/
ResultIterator resultIterator = workspace.getQueryService().execute(query);
categories.clear();
while (resultIterator.hasNext()) {
categories.add((Category) resultIterator.next());
}
repository.endWorkspace();
}
public List<Category> getCategories() {
return categories;
}
}