Sometimes you need to use a 3rd party Java library when you develop Alfresco extensions. Meaning a library that is not available as part of the standard Alfresco installation. So you cannot just specify a provided dependency in the Maven POM and expect the library to be available in webapps/[alfresco|share]/WEB-INF/lib during runtime, you need to supply the 3rd party JAR file as part of the installation. This can be achieved by using what’s called an AMP file for the distribution of the extension. An AMP file can contain other JAR files and they are then installed automatically into the webapps/[alfresco|share]/WEB-INF/lib directory when you install the extension.
In this article we will use a 3rd party library called Jsoup that can be used to parse and create HTML. It’s not by default part of an Alfresco installation, so we need to supply this library via an AMP. To make sure the 3rd party library can be called and works we will create a Repository Web Script with a Java controller that uses the Jsoup library.
Alfresco SDK 3.0 can be used to build extensions for Alfresco version 4.2, 5.1, and 5.2. We are going to work with the default settings, which assumes version 5.2.
Source code for this article can be found here: https://github.com/gravitonian/repo-ext-3rd-party-lib
We need an Alfresco SDK 3.0 project to work with so let’s generate a Repository extension project. This is a project that can be used to develop extensions that should be applied to the alfresco.war file, such as a Repository Web Script.
Here is how to generate this SDK project:
$ mvn archetype:generate -Dfilter=org.alfresco:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom<<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> org.alfresco.maven.archetype:activiti-jar-archetype (Sample project with full support for lifecycle and rapid development of Activiti JARs)
2: remote -> org.alfresco.maven.archetype:alfresco-allinone-archetype (Sample multi-module project for All-in-One development on the Alfresco plaftorm. Includes modules for Platform/Repository JAR and Share JAR)
3: remote -> org.alfresco.maven.archetype:alfresco-amp-archetype (Sample project with full support for lifecycle and rapid development of Repository AMPs (Alfresco Module Packages))
4: remote -> org.alfresco.maven.archetype:alfresco-platform-jar-archetype (Sample project with full support for lifecycle and rapid development of Platform/Repository JARs and AMPs (Alfresco Module Packages))
5: remote -> org.alfresco.maven.archetype:alfresco-share-jar-archetype (Share project with full support for lifecycle and rapid development of JARs and AMPs (Alfresco Module
Packages))
6: remote -> org.alfresco.maven.archetype:share-amp-archetype (Share project with full support for lifecycle and rapid development of AMPs (Alfresco Module
Packages))
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 4
Choose org.alfresco.maven.archetype:alfresco-platform-jar-archetype version:
1: 3.0.0
2: 3.0.1
Choose a number: 2:
Define value for property 'groupId': org.alfresco.training
Define value for property 'artifactId':repo-ext-3rd-party-lib
[INFO] Using property: version = 1.0-SNAPSHOT
Define value for property 'package' org.alfresco.training: :
Confirm properties configuration:
groupId: org.alfresco.training
artifactId: aio-3rd-party-lib
version: 1.0-SNAPSHOT
package: org.alfresco.training
Y: :
From the Maven archetype list choose alfresco-platform-jar-archetype, then select SDK version 3.0.1, and finally specify the group and artifact IDs for the project.
This generates an SDK project with the following files:
$ cd repo-ext-3rd-party-lib/
repo-ext-3rd-party-lib mbergljung$ tree
.
├── README.md
├── debug.bat
├── debug.sh
├── pom.xml
├── run.bat
├── run.sh
└── src
├── main
│ ├── assembly
│ │ ├── amp.xml
│ │ ├── file-mapping.properties
│ │ └── web
│ │ └── README.md
│ ├── java
│ │ └── org
│ │ └── alfresco
│ │ └── training
│ │ └── platformsample
│ │ ├── Demo.java
│ │ ├── DemoComponent.java
│ │ └── HelloWorldWebScript.java
│ └── resources
│ ├── META-INF
│ │ └── resources
│ │ └── test.html
│ └── alfresco
│ ├── extension
│ │ └── templates
│ │ └── webscripts
│ │ └── alfresco
│ │ └── tutorials
│ │ ├── helloworld.get.desc.xml
│ │ ├── helloworld.get.html.ftl
│ │ └── helloworld.get.js
│ └── module
│ └── repo-ext-3rd-party-lib
│ ├── alfresco-global.properties
│ ├── context
│ │ ├── bootstrap-context.xml
│ │ ├── service-context.xml
│ │ └── webscript-context.xml
│ ├── messages
│ │ └── content-model.properties
│ ├── model
│ │ ├── content-model.xml
│ │ └── workflow-model.xml
│ ├── module-context.xml
│ ├── module.properties
│ └── workflow
│ └── sample-process.bpmn20.xml
└── test
├── java
│ └── org
│ └── alfresco
│ └── training
├── license
│ └── README.md
├── properties
│ └── local
│ ├── alfresco-global-enterprise.properties
│ ├── alfresco-global-h2.properties
│ ├── alfresco-global-mysql.properties
│ └── alfresco-global-postgresql.properties
└── resources
├── alfresco
│ └── extension
│ ├── dev-log4j.properties
│ └── disable-webscript-caching-context.xml
├── platform-hotswap-agent.properties
└── tomcat
└── context-solr.xml
Note that the Repository extension project comes with a Hello World Web Script that has a Java Controller. So we don’t need to create a new Web Script for our exercise, we can just extend the Hello World Web Script.
Before we can start using a 3rd party library we need to add a dependency to it in the Maven POM. Open up the repo-ext-3rd-party-lib/pom.xml project file and add a dependency to the JSoup library as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.alfresco.training</groupId>
<artifactId>repo-ext-3rd-party-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<name>repo-ext-3rd-party-lib Platform Jar Module - SDK 3</name>
<description>Platform JAR Module (to be included in the alfresco.war) - SDK 3</description>
<packaging>jar</packaging>
...
<dependencies>
<dependency>
<!-- jsoup HTML parser library @ https://jsoup.org/ -->
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
...
If your IDE auto imports new Maven dependencies, then we should now be ready to use this library in our Web Script Java Controller.
Now, let’s use the Jsoup library in the Web Script’s Java Controller. Open up the repo-ext-3rd-party-lib/src/main/java/org/alfresco/training/platformsample/HelloWorldWebScript.java file and make changes so it looks like this:
package org.alfresco.training.platformsample;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class HelloWorldWebScript extends DeclarativeWebScript {
private static Log logger = LogFactory.getLog(HelloWorldWebScript.class);
protected Map<String, Object> executeImpl(
WebScriptRequest req, Status status, Cache cache) {
Map<String, Object> model = new HashMap<String, Object>();
Connection.Response html = null;
try {
html = Jsoup.connect("http://www.example.com/").execute();
} catch (IOException e) {
e.printStackTrace();
}
model.put("fromJava", "HelloFromJava(" + html.body() + ")");
logger.debug("Your 'Hello World' Web Script was called!");
return model;
}
}
We can now test this out and see if it works by using the SDK 3.0 Runner, which is built into the Alfresco Maven Plugin. We can kick off Tomcat with the Repository extension applied to the alfresco.war as follows:
repo-ext-3rd-party-lib mbergljung$ mvn clean install alfresco:run
Now we can access the Web Script on the http://localhost:8080/alfresco/s/sample/helloworld URL, we should see the following response:
So that worked nicely.
If you look in the repo-ext-3rd-party-lib/target directory you will see the repo-ext-3rd-party-lib-1.0-SNAPSHOT.jar file, which contains the extension code (i.e. the Hello World Web Script files). But it does not contain the Jsoup library, so it will not work to just drop this file into an Alfresco 5.2 installation. We need to provide an AMP with this extension JAR and the Jsoup JAR.
Enable AMP file generation by uncommenting the maven-assembly-plugin in the repo-ext-3rd-party-lib/pom.xml project file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.alfresco.training</groupId>
<artifactId>repo-ext-3rd-party-lib</artifactId>
<version>1.0-SNAPSHOT</version>
<name>repo-ext-3rd-party-lib Platform Jar Module - SDK 3</name>
<description>Platform JAR Module (to be included in the alfresco.war) - SDK 3</description>
<packaging>jar</packaging>
...
<build>
<plugins>
...
<!--
Build an AMP if 3rd party libs are needed by the extensions
JARs are the default artifact produced in your modules, if you want to build an amp for each module
you have to enable this plugin and inspect the src/main/assembly.xml file if you want to customize
the layout of your AMP. The end result is that Maven will produce both a JAR file and an AMP with your
module.
-->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>build-amp-file</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptor>src/main/assembly/amp.xml</descriptor>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.alfresco.maven.plugin</groupId>
<artifactId>alfresco-maven-plugin</artifactId>
<version>${alfresco.sdk.version}</version>
</dependency>
</dependencies>
</plugin>
...
Now execute the build again to create also an AMP:
repo-ext-3rd-party-lib mbergljung$mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building repo-ext-3rd-party-lib Platform Jar Module - SDK 3 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ repo-ext-3rd-party-lib---
[INFO] Deleting /Users/mbergljung/IDEAProjects/sdk-test/repo-ext-3rd-party-lib/target
[INFO]
[INFO] --- maven-resources-plugin:3.0.1:resources (default-resources) @ repo-ext-3rd-party-lib---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 14 resources
[INFO]
[INFO] --- jrebel-maven-plugin:1.1.6:generate (generate-rebel-xml) @ repo-ext-3rd-party-lib---
[INFO] Processing org.alfresco.training:repo-ext-3rd-party-lib with packaging jar
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ repo-ext-3rd-party-lib---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 3 source files to /Users/mbergljung/IDEAProjects/sdk-test/repo-ext-3rd-party-lib/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.0.1:testResources (default-testResources) @ repo-ext-3rd-party-lib---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ repo-ext-3rd-party-lib---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ repo-ext-3rd-party-lib---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ repo-ext-3rd-party-lib---
[INFO] Building jar: /Users/mbergljung/IDEAProjects/sdk-test/repo-ext-3rd-party-lib/target/repo-ext-3rd-party-lib-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-assembly-plugin:2.6:single (build-amp-file) @ repo-ext-3rd-party-lib---
[INFO] Building amp: /Users/mbergljung/IDEAProjects/sdk-test/repo-ext-3rd-party-lib/target/repo-ext-3rd-party-lib-1.0-SNAPSHOT.amp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.121 s
[INFO] Finished at: 2018-12-19T11:44:33Z
[INFO] Final Memory: 50M/529M
[INFO] ------------------------------------------------------------------------
We can see in the console logs that the target/repo-ext-3rd-party-lib-1.0-SNAPSHOT.amp
AMP file was created. Looking inside the AMP (it’s just a ZIP) we can see it contains the following files:
repo-ext-3rd-party-lib-1.0-SNAPSHOT mbergljung$ tree
.
├── file-mapping.properties
├── lib
│ ├── jsoup-1.11.3.jar
│ └── repo-ext-3rd-party-lib-1.0-SNAPSHOT.jar
├── module.properties
└── web
So we should be ready to distribute this AMP file and use it to install the extension into any Alfresco 5.2 server.
We now got a tested Repository Extension that uses a third party library. Time to deploy it to a real Alfresco server. But before we do that, what would happen if we just dropped the repo-ext-3rd-party-lib-1.0-SNAPSHOT.jar into the <ALFRESCO_INSTALL_DIR>/tomcat/webapps/alfresco/WEB-INF/lib directory and restarted. We would see an error like follows when calling the Web Script:
Caused by: java.lang.NoClassDefFoundError: org/jsoup/Jsoup
at org.alfresco.training.platformsample.HelloWorldWebScript.executeImpl(HelloWorldWebScript.java:47)
at org.springframework.extensions.webscripts.DeclarativeWebScript.execute(DeclarativeWebScript.java:64)
... 36 more
Caused by: java.lang.ClassNotFoundException: org.jsoup.Jsoup
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
... 38 more
This is because the extension JAR does not contain the 3rd party library that is needed, so we really need to install the AMP to get everything needed for the extension. I got a 5.2 installation locally and I can use the Alfresco Module Management tool to install the AMP as follows (note that I have stopped the server and I have copied the AMP to the tomcat/webapps directory):
acs5211 mbergljung$ ./alfresco.sh stop
Using CATALINA_BASE: /Applications/acs5211/tomcat
Using CATALINA_HOME: /Applications/acs5211/tomcat
Using CATALINA_TMPDIR: /Applications/acs5211/tomcat/temp
Using JRE_HOME: /Applications/acs5211/java
Using CLASSPATH: /Applications/acs5211/tomcat/bin/bootstrap.jar:/Applications/acs5211/tomcat/bin/tomcat-juli.jar
Using CATALINA_PID: /Applications/acs5211/tomcat/temp/catalina.pid
Tomcat stopped.
/Applications/acs5211/tomcat/scripts/ctl.sh : tomcat stopped
waiting for server to shut down.... done
server stopped
/Applications/acs5211/postgresql/scripts/ctl.sh : postgresql stopped
acs5211 mbergljung$ cd tomcat/webapps/
webapps mbergljung$ cp ~/IDEAProjects/sdk-test/repo-ext-3rd-party-lib/target/repo-ext-3rd-party-lib-1.0-SNAPSHOT.amp .
webapps mbergljung$rm -rf alfresco
webapps mbergljung$ ls -l
total 694472
drwxr-xr-x 9 mbergljung admin 288 4 Jul 13:58 ROOT
-rw-r--r-- 1 mbergljung admin 274599 19 Sep 2017 ROOT.war
drwxr-xr-x 5 mbergljung admin 160 4 Jul 13:58 _vti_bin
-rw-rw-r-- 1 mbergljung admin 763917 6 Apr 2017 _vti_bin.war
-rw-r--r-- 1 mbergljung admin 168007175 26 Feb 2018 alfresco.war
drwxr-xr-x 7 mbergljung admin 224 26 Feb 2018 host-manager
drwxr-xr-x 8 mbergljung admin 256 26 Feb 2018 manager
-rw-r--r--@ 1 mbergljung admin 383139 19 Dec 13:08 repo-ext-3rd-party-lib-1.0-SNAPSHOT.amp
drwxr-xr-x 18 mbergljung admin 576 4 Jul 13:58 share
-rw-r--r-- 1 mbergljung admin 73494142 26 Feb 2018 share.war
drwxr-xr-x 10 mbergljung admin 320 4 Jul 13:58 solr4
-rw-r--r-- 1 mbergljung admin 100394429 19 Sep 2017 solr4.war
webapps mbergljung$ java -jar ../../bin/alfresco-mmt.jar install repo-ext-3rd-party-lib-1.0-SNAPSHOT.amp alfresco.war
webapps mbergljung$ ls -l
total 1023360
drwxr-xr-x 9 mbergljung admin 288 4 Jul 13:58 ROOT
-rw-r--r-- 1 mbergljung admin 274599 19 Sep 2017 ROOT.war
drwxr-xr-x 5 mbergljung admin 160 4 Jul 13:58 _vti_bin
-rw-rw-r-- 1 mbergljung admin 763917 6 Apr 2017 _vti_bin.war
-rw-r--r-- 1 mbergljung admin 168389519 19 Dec 13:10 alfresco.war
-rw-r--r-- 1 mbergljung admin 168007175 26 Feb 2018 alfresco.war-1545225046395.bak
drwxr-xr-x 7 mbergljung admin 224 26 Feb 2018 host-manager
drwxr-xr-x 8 mbergljung admin 256 26 Feb 2018 manager
-rw-r--r--@ 1 mbergljung admin 383139 19 Dec 13:08 repo-ext-3rd-party-lib-1.0-SNAPSHOT.amp
drwxr-xr-x 18 mbergljung admin 576 4 Jul 13:58 share
-rw-r--r-- 1 mbergljung admin 73494142 26 Feb 2018 share.war
drwxr-xr-x 10 mbergljung admin 320 4 Jul 13:58 solr4
-rw-r--r-- 1 mbergljung admin 100394429 19 Sep 2017 solr4.war
Note also that I removed the exploded /alfresco WAR directory, so we are sure the newly generated WAR will be deployed instead.
Now start it all up again:
webapps mbergljung$ cd ..
tomcat mbergljung$ cd ..
acs5211 mbergljung$ ./alfresco.sh start
waiting for server to start.... done
server started
/Applications/acs5211/postgresql/scripts/ctl.sh : postgresql started at port 5432
Using CATALINA_BASE: /Applications/acs5211/tomcat
Using CATALINA_HOME: /Applications/acs5211/tomcat
Using CATALINA_TMPDIR: /Applications/acs5211/tomcat/temp
Using JRE_HOME: /Applications/acs5211/java
Using CLASSPATH: /Applications/acs5211/tomcat/bin/bootstrap.jar:/Applications/acs5211/tomcat/bin/tomcat-juli.jar
Using CATALINA_PID: /Applications/acs5211/tomcat/temp/catalina.pid
Tomcat started.
/Applications/acs5211/tomcat/scripts/ctl.sh : tomcat started
acs5211 mbergljung$ tail -f tomcat/logs/catalina.out
...
You should now be able to hit the Web Script URL again with a successful response: http://localhost:8080/alfresco/s/sample/helloworld