Alfresco List Installed Modules in Explorer Template

UPDATE: I realized that if Alfresco is started with alf_start.bat then this works fine. However if you start it as a service then ALF_HOME and CATALINA_HOME environment variables are not available and it fails. So I’ve updated getCommonArgs() to use hard-coded paths as a work around for now.

Did you ever want to see which modules (AMPs) are currently installed but don’t want to have to log into the server to call the MMT (module management tool) for a list? Or you don’t have access to the MMT on the server. Or maybe you just want to see module version or install dates.

I decided to make this information available in the Alfresco Explorer to make my life easier.

Since the MMT already has a list command I decided just to call it with Java’s ProcessBuilder and parse the output.

Originally I planned to do this as a JavaScript extension that would return a JSON object with the module details. But then I thought this would work nicely in a presentation template that I could tack onto my Alfresco home space or wherever I wanted. So I ended up creating a template extension for Freemarker. The ModuleInfo class is shown below and extends BaseTemplateProcessorExtension.

If you aren’t familiar with extending Freemarker in Alfresco or using custom templates, see the Alfresco Template Guide.

Here is the command we need to run to have the MMT list installed Alfresco modules:

%JAVA_HOME%/bin/java -jar %ALF_HOME%bin/alfresco-mmt.jar list %CATALINA_HOME%/webapps/alfresco.war

And for Share modules we just change the war file it points to:

%JAVA_HOME%/bin/java -jar %ALF_HOME%bin/alfresco-mmt.jar list %CATALINA_HOME%/webapps/share.war

I don’t use ProcessBuilder often at all, but I figured since it has JAVA_HOME, ALF_HOME and CATALINA_HOME in its environment map that it should substitute the values as it’s making the call. Apparently not. Or I wasn’t calling it correctly. In any case I didn’t want to spend much time on that so I “manually” substituted the environment variable values before making the call. This is in the getCommonArgs() method (the full source is included below).

I split the Alfresco module list from the Share module list, having one call for each.

I put the module properties into a map using the names from the MMT output as the keys and I added the Name. So the map includes these properties:

  • Name
  • Title
  • Version
  • Install Date
  • Desription (this isn’t my spelling mistake! The MMT outputs it this way)

Once the template extension is deployed, we just need to make a presentation template that calls it. In the repository in Company Home/Data Dictionary/PresentationTemplates I created ModuleInfo.ftl.

It just calls the Java class to get the module details and throws it into a plain table. Here’s how one of the calls looks:

<#assign alfMods = moduleInfo.getAlfrescoModules()>

Finally, I chose a repository space and set it to use my new custom view.

Here’s what it looks like:

Here is the spring bean for the template extension:

<bean id="moduleInfoTemplate" parent="baseTemplateImplementation">
    <property name="extensionName">
        <value>moduleInfo</value>
    </property>
</bean>

ModuleInfo.java:

package ca.abstractive.ecm.alfresco.template;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.template.BaseTemplateProcessorExtension;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Extends Alfresco's FreeMarker API with functions for retrieving
 * information about installed modules.
 * 
 * Refer to this class in FreeMarker templates using the alias "moduleInfo".
 *
 * @author Tim.Frith
 */
public class ModuleInfo extends BaseTemplateProcessorExtension {

    // Commons logger
    protected static final Log logger = LogFactory.getLog(ModuleInfo.class);

    /* ------------------------------------------------------------ */
    /* Public methods - available to FreeMarker */
    /* ------------------------------------------------------------ */

    /**
     * Returns a list of modules currently installed in the Alfresco war.
     *
     * @return a List of module property maps (Name, Title, Version, Install Date, Desription)
     * @throws AlfrescoRuntimeException
     */
    public List<Map<String,String>> getAlfrescoModules() throws AlfrescoRuntimeException {
    	ProcessBuilder processBuilder = new ProcessBuilder();

	String[] args = this.getCommonArgs(processBuilder);
	args[4] += "alfresco.war";

    	return this.getInstalledModules(processBuilder, args);
    }

    /**
     * Returns a list of modules currently installed in the Share war.
     *
     * @return a List of module property maps (Name, Title, Version, Install Date, Desription)
     * @throws AlfrescoRuntimeException
     */
    public List<Map<String,String>> getShareModules() throws AlfrescoRuntimeException {
    	ProcessBuilder processBuilder = new ProcessBuilder();

	String[] args = this.getCommonArgs(processBuilder);
	args[4] += "share.war";

    	return this.getInstalledModules(processBuilder, args);
    }

    /**
     * Gets detailed information about installed modules.
     *
     * @param processBuilder the process builder to be used to execute the command
     * @param args an array containing the command to execute and its args
     * @return a List with a Map for each module
     * @throws AlfrescoRuntimeException if errors retrieving module info
     */
    public List<Map<String,String>> getInstalledModules(ProcessBuilder processBuilder, String[] args) throws AlfrescoRuntimeException {

    	List<Map<String,String>> results = new ArrayList<Map<String,String>>();

    	try {
            processBuilder.command(args);
            Process process = processBuilder.start();

	    InputStream is = process.getInputStream();

	    String output = IOUtils.toString(is);
	    String[] lines = output.split("\n");

	    Map<String,String> workingMap = null;
	    for (String line : lines) {

	        if (line.startsWith("Module")) {
		    if (workingMap != null) {
	  	        Map<String,String> moduleMap = new HashMap<String,String>();
  		        moduleMap.putAll(workingMap);
		        results.add(moduleMap);
 		    }
		    workingMap = new HashMap<String,String>();
		    workingMap.put("Name", this.getModuleName(line));

	        } else {

	    	    String[] prop = this.getModuleProperty(line);
		    workingMap.put(prop[0], prop[1]);
	        }
	    }

	} catch (IOException ioe) {
	    logger.error("Error retrieving module details", ioe);
	    throw new AlfrescoRuntimeException("Error retrieving module details");
	}

	return results;
    }

    /* ------------------------------------------------------------ */
    /* Utility methods - unavailable to FreeMarker */
    /* ------------------------------------------------------------ */

    /**
     * Builds the list of arguments that are common to both Alfresco and Share calls.
     *
     * @param processBuilder the process builder to be used to execute the command
     * @return command and args WITHOUT the war file specified
     */
    protected String[] getCommonArgs(ProcessBuilder processBuilder) {
	String[] args = new String[5];
	args[0] = processBuilder.environment().get("JAVA_HOME") + "/bin/java";
	args[1] = "-jar";
	args[2] = "C:/Alfresco/bin/alfresco-mmt.jar";
	args[3] = "list";
	args[4] = "C:/Alfresco/tomcat/webapps/";

	return args;
    }

    /**
     * Extracts the module name from a line of alfresco_mmt.jar output.
     *
     * @param line a line of output
     * @return the module name
     */
    protected String getModuleName(String line) {
    	// Sample:
    	// Module 'org_alfresco_module_dod5015' installed in 'C:/ALFRES~1/tomcat/webapps/alfresco.war'

    	int startIdx = line.indexOf("'") + 1;
    	int endIdx = line.indexOf("'", startIdx + 1);

    	return line.substring(startIdx, endIdx).trim();
    }

    /**
     * Extracts the property name and value from a line of alfresco_mmt.jar output.
     *
     * @param line a line of output
     * @return an array where [0] = property name, [1] = propery value
     */
    protected String[] getModuleProperty(String line) {
    	// Sample:
    	//   -    Title:        Alfresco DOD 5015 Record Management Extension

    	String[] pair = new String[2];
    	pair[0] = line.substring("   -    ".length(), line.indexOf(":"));

    	int startIdx = line.indexOf(":") + 1;
    	pair[1] = line.substring(startIdx).trim();

    	return pair;
    }
}

ModuleInfo.ftl:

<h3>Installed Alfresco Modules:</h3>

<#assign alfMods = moduleInfo.getAlfrescoModules()>

<#if alfMods?size == 0>
    There are curently no Alfresco modules installed.
<#else>
    <table cellpadding="2">
	<tr>
   	    <th>Name</th>
	    <th>Title</th>
	    <th>Version</th>
	    <th>Install Date</th>
	    <th>Description</th>
	</tr>
	<#list alfMods as module>
	    <tr>
		<td>${module.Name}</td>
		<td>${module.Title}</td>
		<td>${module.Version}</td>
		<#-- convert install date to date so then we can format it back into the string we want -->
		<td>${module['Install Date']?datetime("EEE MMM dd hh:mm:ss zzz yyyy")?string.medium_short}</td>
		<td>${module.Desription}</td>	<#-- the MMT spells it wrong, so we must too -->
	    </tr>
	</#list>
    </table>
</#if>

<h3>Installed Share Modules:</h3>

<#assign shareMods = moduleInfo.getShareModules()>

<#if shareMods?size == 0>
    There are curently no Share modules installed.
<#else>
    <table cellpadding="2">
	<tr>
	    <th>Name</th>
	    <th>Title</th>
	    <th>Version</th>
	    <th>Install Date</th>
	    <th>Description</th>
	</tr>
	<#list shareMods as module>
	    <tr>
		<td>${module.Name}</td>
		<td>${module.Title}</td>
		<td>${module.Version}</td>
		<#-- convert install date to date so then we can format it back into the string we want -->
		<td>${module['Install Date']?datetime("EEE MMM dd hh:mm:ss zzz yyyy")?string.medium_short}</td>
		<td>${module.Desription}</td>	<#-- the MMT spells it wrong, so we must too -->
	    </tr>
	</#list>
    </table>
</#if>

Suggested improvements:

  • change the Java to use regular expressions to extract the details from the MMT output
  • make the presentation template pretty, mine is very plain
This entry was posted in Alfresco, ECM by tim.frith. Bookmark the permalink.
tim.frith

About tim.frith

Alfresco Certified Engineer

Senior ECM Consultant
Tim holds a diploma in Computer Systems Technology from Camosun College as well as a Bachelor of Commerce Degree with a Finance specialization from the University of Alberta. He has over 10 years of experience developing web applications with Java as well as extensive database development experience with Oracle, IBM DB2, MS SQL Server, and MySQL.

In recent years he has focused on Enterprise Content Management, with specific training and experience in Alfresco's content management suite.

And finally, Tim is a strong believer that the Edmonton Oilers will soon reclaim their rightful place at the top of the hockey world. Yes, Stanley will once again come to town.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>