Saltar al contenido

WebSphere Commerce – Management Center: Nuevo tool

We will start by giving a quick overview of what WCS Management Center is and how it works before we move on to the main subject of this article: Explain by example how a new custom tool can be added to Management Center. All code relevant to the tool can be found on GitHub.

WebSphere Management Center Overview

The IBM Management Center for WebSphere Commerce, or simply Management Center is a collection of tools that support store management, merchandising and marketing tasks for business user. It comes with several tools out-of-the-box:

  • Catalogs tool
  • Promotions tool
  • Marketing tool
  • Assets tool
  • Installments tool
  • Store Management tool
  • Catalog Filter and Pricing tool
  • Workspace Management tool
  • Commerce Composer

See the IBM Knowledge Center there are several articles discussing the technical details of the Management Center framework.

We will not go deep into the weeds here, but rather give a simplified overview of how a tool works and then go through an example to create a new tool.

In essence a Management Center tool is nothing but a collection of XML files. In these XML files (or just a single XML file) we define different objects that our tool will use.

There are several different object types that serve a distinct purposes. Additionally, services can be defined on the objects which can either map to a REST service or a command. The information that will be displayed in the Main Work Area of the Management Center console will be a result from a service call.

The service will return with an XML response, which will be mapped to the appropriate object and subsequently displayed accordingly.

Creating a Custom Tool

We want to create a custom tool that can integrate with a Jenkins server. The tool should be able to display a list of jobs from Jenkins and execute build of jobs.

The idea is to create a custom command that communicates with Jenkins. This command will be executed from Management Center and it will return the response from Jenkins in way that Management Center can understand and display in the Main Work Area of the Management Center console.

Websphere Centro de Gestión

The XML files that define the Management Center tools live in the LOBTools project: LOBTools/WebContent/WEB-INF/src/xml.

The standard out-of-the-box tools are located in the commerce sub folder. We will place the new custom tool in a new sub folder called orienteed/integration.

For simplicity we will keep our whole tool definition in a single XML file. The definition of a tool can be split up in many different XML files, as can be seen by exploring the standard tools.

When the complexity of the tool increases and more objects need to be defined, it is advisable to split the definition as the standard tools do.

Objects definitions

The fundamental building block of a Management Center tool is the BusinessObjectEditor. It supports several of the functionalities of the Management Center console.

Inside of the BusinessObjectEditor element we need several things:

  • ObjectFilterType: Filters the objects that are visible in the Explorer view. We only want objects of type JenkinsTop to be visible in the Explorer, so we filter on them. If we would want other object types as well, we could add them to the filter.
  • TopObjectDefinition: The root object that defines the navigation tree in the Explorer view. The Xml element defines the structure of the tree that we want to create. We only want to have one node of the type JenkinsTop.
  • OrganizationalObjectDefinition: An object that is used by the TopObjectDefinition to create the navigation tree. Here we define the JenkinsTop object that we referenced above. On this object we define a GetChildrenService that will get the list of jobs from Jenkins. To list these objects in the Main Work Area we need to define a NavigationListDefinition that will tell Management Center how to visualize the objects. The list definition depends on objects created outside of the BusinessObjectEditor.
  • PrimaryObjectDefinition: This is the object that keeps track of the data we are working with. An instance of this object will represent a Jenkins job. We do not define the object here, but rather reference a base definition.
<BusinessObjectEditor definitionName="cmc/orienteed/integration/IntegrationTool" explorerFilterTypes="Jenkins" displayName="Integration Tool" showStorePreview="false" showStoreSelection="false" showUtilitiesView="false"> <ObjectTypeFilter displayName="Jenkins" filterType="Jenkins" objectTypes="JenkinsTop"/> <TopObjectDefinition> <Xml name="template"> <object objectType="JenkinsTop"/> </Xml> </TopObjectDefinition> <OrganizationalObjectDefinition displayName="Jenkins" objectType="JenkinsTop"> <GetChildrenService url="/cmc/integration/jenkins/jobList"> <ServiceParam name="storeId"/> </GetChildrenService> <NavigationListDefinition definitionName="cmc/orienteed/integration/JenkinsChildList" displayName="Jenkins Job List" listDefinition="cmc/orienteed/integration/JenkinsListEditor" listTitle="Job List"/> </OrganizationalObjectDefinition> <PrimaryObjectDefinition baseDefinition="cmc/orienteed/integration/JenkinsPrimaryObjectDefinition" creatable="true"/> </BusinessObjectEditor>

In the last two points in the above list, we have referenced other object definitions that are defined outside of the BusinessObjectEditor:

  • PrimaryObjectDefinition: This is the base definition of the primary object that we want to define. We define the two services that we need and the parameters that we need to pass to them. The services will allow us to refresh an object and to build the corresponding Jenkins job. The RefreshService is executed when the refresh button is clicked (while the object is selected). The CustomService is executed when the custom Jenkins build button is clicked. Both of these buttons are in the toolbar of the Management Center. To display the custom Jenkins build icon, we define an Image element that points to the location of the image.
  • ChildListEditor & ObjectGrid: These two objects are used to define the list view that we create for displaying the list of Jenkins jobs in the Main Work Area. The ChildListEditor defines the type of object the list expects, meanwhile the ObjectGrid object defines the structure of the actual list (such as columns and which data to display).
<PrimaryObjectDefinition definitionName="cmc/orienteed/integration/JenkinsPrimaryObjectDefinition" objectType="Jenkins" idProperty="integrationId" displayNameProperty="jenkinsName" propertiesDefinition="cmc/orienteed/integration/JenkinsProperties" initializeObjectStoreId="false"> <RefreshService url="/cmc/integration/jenkins/refresh"> <ServiceParam name="jobName" propertyName="jobName"/> <ServiceParam name="storeId"/> </RefreshService> <CustomService url="/cmc/integration/jenkins/build" displayName="Build job" toolbarIcon="jenkinsBuild"> <ServiceParam name="jobName" propertyName="jobName"/> <ServiceParam name="storeId"/> </CustomService> </PrimaryObjectDefinition> <Image name="jenkinsBuild" src="/images/orienteed/integration/resources/jenkins_build.png"/> <ChildListEditor definitionName="cmc/orienteed/integration/JenkinsListEditor" listDefinition="cmc/orienteed/integration/JenkinsGrid" objectTypes="Jenkins"/> <ObjectGrid definitionName="cmc/orienteed/integration/JenkinsGrid"> <GridText editable="false" name="color" propertyName="color" text="Color" width="80"/> <GridText editable="false" name="jobName" propertyName="jobName" text="Job Name" width="350"/> <GridText editable="false" name="lastBuild" propertyName="lastBuild" text="Last Build" width="350"/> <GridText editable="false" name="lastDuration" propertyName="lastDuration" text="Last Duration" width="350"/> <GridText editable="false" name="lastResult" propertyName="lastResult" text="Last Result" width="350"/> </ObjectGrid>

The last object that we define for the tool is a properties object for the primary object. We have already referenced this object in the PrimaryObjectDefinition.

  • ObjectProperties: This object defines the properties object that we link to our primary object. It defines which properties the Jenkins jobs object will have.
<ObjectProperties definitionName="cmc/orienteed/integration/JenkinsProperties"> <PropertyPane> <PropertyGroup groupTitle="General Job Properties" open="true"> <PropertyViewLongText promptText="Color" propertyName="color" required="true"/> <PropertyViewLongText promptText="Job Name" propertyName="jobName" required="true"/> <PropertyViewLongText promptText="URL" propertyName="integrationId" required="true"/> </PropertyGroup> <PropertyGroup groupTitle="Last Build Information" open="false"> <PropertyViewLongText promptText="Last Build" propertyName="lastBuild" required="false"/> <PropertyViewLongText promptText="Last Duration" propertyName="lastDuration" required="false"/> <PropertyViewLongText promptText="Last Result" propertyName="lastResult" required="false"/> </PropertyGroup> <PropertyGroup groupTitle="Last Build Console Log"> <PropertyViewLongText promptText="Last Console Log" propertyName="lastConsoleLog" required="false"/> </PropertyGroup> </PropertyPane> </ObjectProperties>

Service definitions

In order to make the tool functional we need to define the services that we have declared in the above XML definition. We have defined three services, one on the OrganizationalObjectDefinition and two on the PrimaryObjectDefinition. Respectively, those are:

  • GetChildrenService: This service gets the list of jobs from the Jenkins server by executing a custom command. It is triggered when the Jenkins node is selected in the Explorer view, or when the list is refreshed. The custom command will query the Jenkins server for a list of jobs. The response will be parsed by Management Center and it will create the corresponding primary objects.
  • RefreshService: This service is run when a Jenkins object is selected and the refresh button is clicked. It works basically in the same way as the GetChildrenService, except it only requests information about the specific job that is selected.
  • CustomService: This service is triggered by the click of a custom toolbar button. It sends a request to the Jenkins server to build the selected job. The Jenkins server does not respond with any information that Management Center needs to parse, only a status on whether or not the build request was successfully created.

Management Center uses the Spring framework to manage service calls. We have to add beans to /LOBTools/WebContent/WEB-INF/spring-extension.xml configuration file for the services we want to define. We need two beans, since the GetChildrenService and RefreshService will use the same bean. bean, ya que el GetChildrenService y el RefreshService usarán el mismo bean.

<bean id="/integration/jenkins/jobList" class="com.ibm.commerce.foundation.internal.client.lobtools.controllers.ControllerCommandController"> <property name="commandInterface" value="com.orienteed.commerce.integration.commands.IntegrationJenkinsCmd" /> <property name="defaultParameters"> <props> <prop key="function">joblist</prop> </props> </property> <property name="contextParameters"> <props> <prop key="storeId">storeId</prop> </props> </property> <property name="successView" value="/jsp/orienteed/integration/JenkinsJobList.jsp" /> </bean>
  • Note that when declaring the services (in the tool definition), we need to reference the bean that it will invoke and the id of the bean needs to be prepended by ‘/cmc’.
  • Especificamos los parámetros que se pasarán al comando. Podemos pasar tanto parámetros estáticos que se definen en el XML, como dinámicos que se especifican en la declaración del servicio. Utilizamos la propiedad defaultParameters para pasar los parámetros estáticos y los contextParameters de los dinámicos.
  • We specify the parameters that will be passed on to the command. We can both pass static parameters that are defined the XML, or dynamic ones that are specified in the declaration of the service. We use the defaultParameters property to pass static parameters and the contextParameters from dynamic ones.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://commerce.ibm.com/foundation" prefix="wcf"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix = "x" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<c:set var="responseMap" value="${requestScope['com.ibm.commerce.responseMap']}"/>
<x:parse xml="${responseMap.jenkins}" var="parsedResponse"/>
<x:set var="jobList" select="$parsedResponse/hudson/job"/>
<objects>
	<x:forEach select="$jobList" var="job">
		<object objectType="Jenkins">
			<integrationId readonly="true"><x:out select="$job/url"/></integrationId>
			<jobName readonly="true"><x:out select="$job/name"/></jobName>
			<color readonly="true"><x:out select="$job/color"/></color>
			<lastBuild readonly="true"><x:out select="$job/lastBuild/timestamp"/> - #<x:out select="$job/lastBuild/number"/></lastBuild>
			<lastDuration readonly="true"><x:out select="$job/lastBuild/duration"/></lastDuration>
			<lastResult readonly="true"><x:out select="$job/lastBuild/result"/></lastResult>
		</object>
	</x:forEach>
</objects>

La última pieza que falta antes de que nuestra nueva herramienta personalizada sea completamente funcional, es implementar el comando personalizado que usarán los frijoles. Crearemos un solo comando para que ambos frijoles usen y distingan la funcionalidad pasando diferentes parámetros al comando. No entraremos en detalles aquí sobre cómo crear un comando personalizado en WebSphere Commerce, pero observamos que todo el código relacionado con esta herramienta se puede encontrar en GitHub.

Conclusion

We have gone through the process of adding a brand new custom tool to Management Center and explained what is needed to get it up and running. We will conclude this article with a few tips and tricks that may help with developing customizations to Management Center:

  • Run the test server in debug mode, so certain code changes can be hot swapped without a restart.
  • Changes to XML configuration files can be realized by closing Management Center and re-opening it after clearing caches in the browser. It is not needed to re-publish.
  • Start simple! Get a simple tool (as the one implemented here) to work and try to understand the structure of the tool. Then move onto adding complexity to the tool. The fully fledged standard tools are very complex and it is hard to get an overview of how they work, since the definition of each tool is split up into many different XML files.
  • When opening Management Center, append &logger.display=true to the URL to add a logging tool to Management Center. Open the logging tool and turn on the needed logs. For server side logging consider adding com.ibm.commerce.lobtools.*=all: com.ibm.commerce.foundation.*=all to the trace. Keep in mind that this may slow Management Center down considerably.
  • Puede ser útil editar archivos XML restringidos mientras se depura un problema en el desarrollo. Sólo recuerda revertir cualquier cambio en los archivos XML restringidos. Por ejemplo, en commerce/shell/foundation/restricted/Service.xml, nos pareció útil editar la función handleResponse y añadir un registro adicional para ver exactamente qué XML estaba recibiendo.
  • Last but not least, dig into the IBM Knowledge Center and get a fundamental understanding of the architecture of the Management Center and how a tool is structured.

Useful links