How to execute Groovy scripts from the shell

Sometimes it can be useful for system administrators without Liferay knowledge (or without a Liferay administrator user) to be able to execute Liferay Groovy (or other supported scripting language) scripts.

To accomplish this we can combine both Blade CLI and the Gogo shell scripting language.

  • Blade CLIoffers some nice possibilities in order to manage your Liferay server letting you install, start, stop, inspect a Liferay server, and, for our current purposes, even executing Goog shell commands. You can get more information here: Managing Your Liferay Server with Blade CLI
  • Gogo shell is a unix bash like shell that offers a simple scripting language that supports some programmatic features. You can find additional information here: The Command Shell.

 

Requirements

After Liferay 7.1 (LPS-82849) the telnet access to the Gogo console was disabled as a security precaution in order to prevent potential XXE or SSRF security vulnerabilities. But it can be enabled if you're using the developer mode (via portal-developer.properties) or by setting the following property in portal-ext.properties:

module.framework.properties.osgi.console=localhost:11311

 

How-To

The following steps can be followed in order to execute a Groovy script from the command line:

  1. Save the following gosh script as, for example, executeScript.gosh

    service = { $.context service ([($.context servicereferences $1 $2)] get 0) };
    
    scriptFile = $1;
    fileUtil = service "com.liferay.portal.kernel.util.File" null;
    script = $fileUtil read $scriptFile;
    editServerCommand = service "com.liferay.portal.kernel.portlet.bridges.mvc.MVCActionCommand" "(mvc.command.name=/server_admin/edit_server)"
    editServerCommandClass = $editServerCommand class
    
    currentThread = (($editServerCommandClass forname "java.lang.Thread") method "currentThread") invoke null;
    currentThreadClassLoader = $currentThread contextclassloader;
    
    $currentThread setcontextclassloader ($editServerCommandClass classloader);
    
    if { $2 } { scriptingLanguage = $2 } { scriptingLanguage = "groovy" }
    
    groovyExecutor = service "com.liferay.portal.kernel.scripting.ScriptingExecutor" "(scripting.language=$scriptingLanguage)";
    $groovyExecutor eval null null null $script;
    
    $currentThread setcontextclassloader $currentThreadClassLoader;
    

     

  2. Create the script you want to execute, for example script.groovy:

    // ### Groovy Sample ###
    import com.liferay.journal.service.JournalArticleLocalServiceUtil;
    
    number = JournalArticleLocalServiceUtil.getCompanyArticlesCount(0L, 0);
    
    System.out.println("Number of Articles: " + number);
    

     

  3. Execute the gosh script: blade sh sh executeScript.gosh script.groovy groovybeing:
    • script.groovy: The script to be executed (it's programming language must be indicated in the next parameter).
    • groovy: The scripting language. Default: groovy. Can be every scripting language provided as a component, see GroovyExecutor.java

 

This script, mainly provided by Minhchau Dang in LPS-106390, what does is to use as context classloader the same classloader as the one provided by the Liferay Server Admin Web module. This module uses the DynamicImport-Package directive in order to be able to know how to load every class contained in every exported package. That makes the script able to use also classes loaded from modules.

WARNING: Take into account that Liferay, sometimes, initializes several values when receiving an HTTP request (like ThreadLocals) that will not be initialized when executing your script, and aren't part of the API method parameters, so you'll have to do it manually.