Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > Authors stefankopf

A while ago, Alfresco decided to replace the Ghostscript engine in our products. It has been used as a rasteriser to transform PDF files to PNG images within Alfresco Content Services (ACS). The main cause was due to Ghostscript’s change to an AGPL license, which caused some concerns among our customers and limited us in the way how we could distribute ACS.

 

Alfresco Engineering was tasked to evaluate different options for PDF rendition under a permissive open source license. Unfortunately, we found almost no independent performance and fidelity comparison between the different engines out there — especially no study that is thorough enough to base such a decision on. Since the new engine will be used as the default in our next version of ACS, it would have the potential to cause severe problems for our customers if it fails either due to poor performance or poor fidelity.

 

In the end, we decided to do this study ourselves. With this blog post, we want to share our results with you.

 

Market Overview

After research on Wikipedia and Google, our team came up with this list of PDF rendering engines:

EngineLicenseNotes
GhostscriptAGPLv3 or CommercialFull PostScript interpreter. Can also handle PDF files.
MuPDFAGPLv3 or CommercialPDF, XPS and EPUB rendering engine based on the modern high performance Fitz graphics engine
Adobe PDF Library SDKCommercialOriginal Adobe PDF engine.
Foxit SDKCommercialEngine behind the Foxit PDF reader products.
Fork released under BSD license as Pdfium by Google.
PdfiumBSD styleEngine behind PDF Plug-In in Chrome.
Fork of the Foxit SDK.
PopplerGPLv2 or GPLv3Fork of XPdf
XpdfGPLv2 or CommercialPDF viewer for X-Windows & PDF Rasterizer for all platforms (pdftopng). 
GnuPDFGPLv3
PDFBox 2.0Apache 2.0
SejdaAGPLv3 or Commercial
IcePDFApache 2.0 and Commercial Pro version
Aspose PDFCommercial

(Note: There are multiple PDF Reader products out there, but this is the consolidated list of PDF rendering engines behind these products)

 

The Candidates

Given our constraints on license terms and other factors, we decided to start our deep and thorough investigation with this list of candidates. We included some leading proprietary libraries for reference.

EngineTypeVersion
GhostscriptNative9.21
MuPDFNative1.10a
XpdfNative3.04
PdfiumNative2017-04-10
AsposeJava17.2.0
ICEPdfJava6.2.0
SejdaJava3.0.13
PDFBoxJava2.0.5

The version numbers are the latest released version at the time of our investigation.

 

Performance

Ghostscript has been used until ACS 5.1 to render all PDF files to PNG images for things like thumbnails. Most other file formats, like MS Office files, are converted to PDF first for previewing and then the PDF is converted to PNG. In our analysis, we have been interested in the average overall performance.

Our team randomly picked 3071 PDF documents (17,226 pages) from our internal Alfresco Repository to get a sample set representing a typical ACS repository. We are aware that there are ACS installations out there that mainly contain documents of one specific kind, but we are confident that our sample set represents the majority of ACS repositories.
To compare the performance, we rendered all documents to PNG files at 100 dpi. Each engine was configured to produce results at comparable fidelity (e.g. by activating anti-aliased text and graphics) and we guaranteed the same resources to each engine. For all Java based engines, we kept the JVM running and invoked the rendition for each document in the same JVM process, enabling Hotspot to best optimise the generated native code.
This led to the following total process times:

Rendition times of different PDF engines

(total rendition time; smaller is better)

 

We played with different dpi settings to see how the results would change. We found that the relative difference between engines is affected by the resolution (dpi), but the order in which the candidates ranked stayed the same across different dpi settings (except for close candidates).

 

Our key findings are:

  • Native engines are always faster than Java based engines
  • MuPDF is clearly the fastest engine
  • Pdfium comes in second, but is significantly slower than MuPDF

 

Features  and Fidelity

Because our new transformation engine will be used for a server based rendition of PDF documents to PNG files, all interactive features like form filling, signature validation, video or 3D were out of scope for our investigation.

 

Based on the latest PDF specification, we compiled a list of features that are provided by the PDF drawing model. For each feature, we picked a sample document for testing or created such a document ourselves. The rendition of these sample documents was then visually compared and rated. We used the latest Adobe Acrobat Reader as our reference viewer.

 

Text rendition and font support

 

Text  rendition & font support

Ghostscript

MuPDF

Xpdf

Pdfium

Aspose

ICEPdf

Sejda

PDFBox

Type1

4

5

4

5

1

1

4

4

TrueType

4

5

4

5

4

0

5

5

Type1 CID

yes

yes

yes

yes

yes

no

yes

yes

TrueType CID

yes

yes

yes

yes

yes

no

yes

yes

Type3

3

5

6

6

1

0

1

1

AVG

3.67

5

4.67

5.33

2

0

3.33

3.33

 

We awarded 0 to 5 points for each rendition compared to the Acrobat reference. In two situations, we awarded an extra point for visually better results than Acrobat.

(click to enlarge)

 

Images

PDF supports 6 different “filters” (i.e. compression formats) that can be used to store raster graphic data. The decoded raster graphic then needed to be mapped to the pixels of the rendered output graphic. This process has a huge impact on the final visual result.

 

Images

Ghostscript

MuPDF

Xpdf

Pdfium

Aspose

ICEPdf

Sejda

PDFBox

Anti Aliasing

no

yes

yes

yes

no

partial

no

no

CCITTFaxDecode

yes

yes

yes

yes

yes

yes

yes

yes

DCTDecode

yes

yes

yes

yes

yes

yes

yes

yes

LZWDecode

yes

yes

yes

yes

yes

yes

yes

yes

FlatDecode

yes

yes

yes

yes

yes

yes

yes

yes

JPXDecode

yes

yes

yes

yes

no

partial

no

no

JBIG2Decode

yes

yes

yes

yes

yes

yes

partial

partial

SUM Image

3

5

5

5

2

2

1

1

 

Every engine starts with 5 points. We removed 1 point for each missing filter support and we removed 2 points for non-working anti aliasing.

 

Drawing model

We noticed that the atomic drawing operations (MoveTo, LineTo, CurveTo) and shading models are supported almost equally in all candidates. Visually different results on complex drawings are mainly caused by the composition of these atomic building blocks, and not by the basic operations themselves.

This is why we decided to focus on the composition of drawing operations for our comparison.

 

Compositing and Blend Modes

PDF supports 16 different blend modes. These can either be applied to single objects or to multiple objects in a transparency group. Each blend mode affects the image channels individually and thus produces different results in different colour spaces (RGB, CMYK).
We used a set of test and reference PDF files (link these two for RGB and CMYK) and counted the errors made by each engine. We then used the relative number of errors to assign points, with 5 points being awarded for the fewest errors averaged over all test files and 0 points representing lots of errors. Here are the final results:

 

Blend Modes

Ghostscript

MuPDF

Xpdf

Pdfium

Aspose

ICEPdf

Sejda

PDFBox

AVG

3.33

3.33

3

2

1.67

1

2

2

 

Fidelity results

Combining all of the above gives the following results for features and fidelity:

(rendition fidelity; larger is better)

 

Conclusion

MuPDF came out of this investigation as the clear winner, followed by Pdfium second. It also became apparent that there is big gap between native PDF renderers and the group of Java based PDF renderers — considering performance as well as features and fidelity.

We ended up selecting PDFium as the PDF rasterization engine for these reasons:

  • The BSD-style license of Pdfium gives us and our customers the most flexibility
  • Since this engine drives the PDF display in Chrome, we expect a very good and continued support from Google for this library, especially when it comes to finding and fixing vulnerabilities
  • It shows a very good overall performance, although it is not the fastest engine in the test
  • It shows very good rendition fidelity

 

The Alfresco PDF Renderer

Based on the Pdfium library, we started a new project: the alfresco-pdf-renderer. This native command line program is inspired by the test application used within the Pdfium builds. But it does not provide support for JavaScript and offers additional parameters to specify the size of the output image.

Before migrating any AMP projects to ACS 6, I recommend to read the blog post about the Anatomy of an ACS 6 AMP module to understand the basics of an AMP project with ACS6.

This article is about modules for the ACS repository and does not cover Share. Although Share uses the same packaging format, extension modules for Share are fundamentally different and will be covered in new blog posts in the future.

This article describes how to migrate Alfresco SDK 2 based projects to plain maven projects, essentially by moving off the Alfresco SDK track. This requires more knowledge about the ACS build process and is not as convenient as using the Alfresco SDK. In Alfresco engineering however, we need to build AMPs before we release a new version of ACS and before the updated Alfresco SDK becomes available. As a third party developer, it might be a lot easier for you to wait for the next version of our official Alfresco SDK.

 

Step 1: Remove parent reference to SDK artifact

With the Alfresco SDK2, it has been required that your projects import a basic project definition by setting a specific pom as parent:

    <parent>
        <groupId>org.alfresco.maven</groupId>
        <artifactId>alfresco-sdk-parent</artifactId>
        <version>2.1.1</version>
    </parent>

This needs to be removed. You can either replace this with your own parent in a multi module project or just have no parent at all.

 

Step 2: Remove dependency management

The SDK2 managed dependencies for you by delegating dependency management to a pom file that got produced during our build:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>${alfresco.groupId}</groupId>
                <artifactId>alfresco-platform-distribution</artifactId>
                <version>${alfresco.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

This artifact does no longer exist in ACS6. You need to remove this section from your pom file as well.

 

Step 3: Remove H2 artifacts

The H2 database has never been supported officially and got deprecated even for test and development use in 5.0.
With the removal of Hibernate, we fundamentally changed the management of SQL dialects and the required files are no longer available for ACS6.
Remove this dependency:

    <dependency>
        <groupId>${alfresco.groupId}</groupId>
        <artifactId>alfresco-repository</artifactId>
        <version>${alfresco.version}</version>
        <classifier>h2scripts</classifier>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>*</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

 

Step 4: Cleanup properties

The SDK2 defined a set of properties that are used in the imported parent pom and various other locations. It is a good idea to at least comment out these properties now and remove them when we are finished migrating this project.
These properties are (among others):

    <properties>
        <alfresco.groupId/>
        <alfresco.version/>
        <alfresco.repo.artifactId/>
        <alfresco.share.artifactId/>
        <alfresco.version/>
        <alfresco.data.location/>
        <app.log.root.level/>
        <env/>
        <alfresco.db.*/>
        <app.amp.*/>
        <maven.tomcat.port/>
        <h2.version/>
    </properties>

This will also help you to identify any pieces that are left over from the SDK2 project setup.

 

Step 5: Configure alfresco-maven-plugin

Now that we no longer import the SDK2 parent pom, the build will fail as it is missing the important configuration required to build AMPs.
If you try to run it like this, it will immediately complain about the unknown packaging type "amp" and that maven does not know what to do with your project.
So you need to add in the alfresco-maven-plugin:

    <properties>
        <app.amp.output.folder>${project.build.directory}/amp</app.amp.output.folder>
    </properties>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/amp</directory>
                <targetPath>${app.amp.output.folder}</targetPath>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.alfresco.maven.plugin</groupId>
                <artifactId>alfresco-maven-plugin</artifactId>
                <version>2.1.1</version>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    <build>

Since the alfresco-maven-plugin only packages an exploded AMP from the /target folder, we also need to make sure that the content of src/main/amp is copied over and that the app.amp.output.folder is properly set for the plugin.

 

Step 6: Fix dependencies

Now, maven knows how to build your project, but it will start to complain about a lot of missing dependencies and you will most likely see some compile errors. At least, you need to add these dependencies and define the location of our
public maven repository:

    <dependencies>
        <dependency>
            <groupId>org.alfresco</groupId>
            <artifactId>alfresco-repository</artifactId>
            <version>6.37</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.alfresco</groupId>
            <artifactId>alfresco-remote-api</artifactId>
            <version>6.23</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>alfresco-public</id>
            <url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
        </repository>
    </repositories>

From ACS 6 onwards, each artifact is on its own lifecycle. You can look up the version numbers of each artifact either in the release notes or simply take a look in your ACS 6 deployment.
It is important to define each ACS artifact as provided so that it is only used during the compilation of your Java classes, but it is not packaged into the AMP file.

 

You should try to tackle one problem after another. Try to run just the first phase of the build lifecycle:

$ mvn validate

This will tell you if maven is able to read and understand the project object model and if it has all plugins to build it.

 

Next, go a tiny step further and try to compile your code:

$ mvn compile

This will probably give you some errors. It is possible that you are just missing a dependency. If so, try to identify the artifact that contains this class and add it to your project.

Otherwise:

 

Step 7: Refactor your code where required

In ACS6, we have updated almost all dependencies to the most recent feasible version. Some noticeable updates that might affect your custom code are (among others):

  • Spring
  • Quartz
  • POI
  • Jackson
  • multiple commons-* libraries

In addition, we removed some deprecated code like

  • Hibernate

You should see the release notes for a detailed list. If your custom code is using these libraries, it is possible that a class or method got renamed or removed. You need to refactor your code accordingly and sometimes change bean definitions in your custom context files.

Our ACS 6 Migration Guide wiki page on GitHub collects useful information about updating Java code to work with the ACS6 repository.

 

Step 8: Testing part 1: Test code

The Alfresco SDK2 used the H2 database to provide a full ACS repository for unit testing. With ACS6, we now recommend to use unit testing only for small isolated tests. All larger tests that require a ACS repository should be treated as an integration test. See it as an integration of your custom code into the ACS repository.
This means that you need to rename all such *Test classes to *IT. The surefire and failsafe plugins are configured by default to look for test classes with these naming conventions during the test and the integration-test phases and execute them automatically.
If you followed the SDK2 guidelines and used the boilerplate code provided by archetype generator, then your test classes most likely look like this:

@RunWith(RemoteTestRunner.class)
@Remote(runnerClass=SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:alfresco/application-context.xml")
public class DemoComponentTest {
   
    private static final String ADMIN_USER_NAME = "admin";

    static Logger log = Logger.getLogger(DemoComponentTest.class);

    @Autowired
    protected DemoComponent demoComponent;
   
    @Autowired
    @Qualifier("NodeService")
    protected NodeService nodeService;
   
    @Test
    public void testWiring() {
        assertNotNull(demoComponent);
    }
   
    @Test
    public void testGetCompanyHome() {
        AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER_NAME);
        NodeRef companyHome = demoComponent.getCompanyHome();
        assertNotNull(companyHome);
        String companyHomeName = (String) nodeService.getProperty(companyHome, ContentModel.PROP_NAME);
        assertNotNull(companyHomeName);
        assertEquals("Company Home", companyHomeName);
    }
   
}

You can either keep the RemoteTestRunner and simply add it as a dependency:

    <dependency>
        <groupId>com.tradeshift</groupId>
        <artifactId>junit-remote</artifactId>
        <version>3</version>
        <type>jar</type>
        <exclusions>
            <exclusion>
                <artifactId>servlet-api</artifactId>
                <groupId>javax.servlet</groupId>
            </exclusion>
        </exclusions>
        <scope>test</scope>
    </dependency>

Or you can choose to remove the RemoteTestRunner and change the jUnit test  configuration to this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:alfresco/application-context.xml")
public class DemoComponentTest {

After this step, when you run maven until the test phase

$ mvn test

then it should only execute tests that do not require a Spring Context (i.e. don't require the ACS6 repository).

 

Step 9: Testing part 2: Test environment

Trying to run the integration tests will fail with an error message either complaining that it cannot find the JDBC database driver or some other configuration related problem. The tests can start a ACS6 repository now, but it is yet completely unconfigured.
At first, we need to add this dependency for tests:

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.4.1212</version>
        <scope>test</scope>
    </dependency>

Next, we need to add this configuration for the failsafe build plugin:

    <build>
        <plugins>
            ...
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.17</version>
                <configuration>
                    <systemPropertyVariables>
                        <db.name>alfresco-test</db.name>
                        <db.driver>org.postgresql.Driver</db.driver>
                        <db.url>jdbc:postgresql://localhost:${database.port}/alfresco-test</db.url>
                        <dir.root>${project.build.directory}/alf-data-test</dir.root>
                    </systemPropertyVariables>
                </configuration>
                <executions>
                    <execution>
                        <id>integration-test</id>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    <build>

This configures the ACS repository to store the file content in the folder

    /target/alf-data-test

and use a PostgreSQL database on localhost. To provide this database, we recommend to use docker as we can start a clean DB for every test run automatically. Add this fabric8 docker-maven-plugin to your project:

    <build>
        <plugins>
            ...
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${maven.fabric8.version}</version>
                <configuration>
                    <images>
                        <image>
                            <alias>test-database</alias>
                            <name>postgres:9.4.12</name>
                            <run>
                                <ports>
                                    <port>database.port:5432</port>
                                </ports>
                                <env>
                                    <POSTGRES_PASSWORD>alfresco</POSTGRES_PASSWORD>
                                    <POSTGRES_USER>alfresco</POSTGRES_USER>
                                    <POSTGRES_DB>alfresco-test</POSTGRES_DB>
                                </env>
                                <cmd>
                                    <shell>-c max_connections=300</shell>
                                </cmd>
                                <wait>
                                    <log>database system is ready to accept connections</log>
                                    <time>20000</time>
                                </wait>
                            </run>
                        </image>
                    </images>
                </configuration>
                <executions>
                    <execution>
                        <id>start</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>stop</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    <build>

This pulls the image postgres in version 9.4.12 from DockerHub and starts a new, clean container from this image. After running the integration tests, it stops and removes this image again.
This requires that you have Docker installed and set up properly on your machine.

 

Now you can run maven past the integration tests with

$ mvn verify

 

Conclusion

These steps are just a guideline how to convert existing ACS extension projects to ACS6. They are based on lessons learned from converting our own AMP projects here at Alfresco.
However, all custom AMP projects are different and you might hit completely different problems than we did.
So I want to encourage you to leave comments under this blog post and share your experience. This will help others who experience the same problems as you did.

At first, I want to highlight that an AMP module is not the recommended way to extend or develop against ACS 6. Instead, you should build a (micro-)service that sits next to the repository and use the v1 REST API to talk to the repository.

However, there are certain requirements that cannot yet be met by an extension outside the ACS repository. And, of course, you simply might want to port existing extension projects to ACS 6.

There is also the official "Alfresco SDK" that can help you to jump start extension projects. But this SDK is not updated until after the release of a new ACS version. So we in engineering need to build AMPs independent of the SDK. In this post, I want to share our experiences in the hope that it contains some useful background information.


Basic project structure

We provide plugins for maven to build AMP files. The smallest possible AMP project has a pom.xml file that looks like this:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.acs-module</groupId>
    <artifactId>my-awesome-module</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>amp</packaging>
    <properties>
        <app.amp.output.folder>${project.build.directory}/amp</app.amp.output.folder>
    </properties>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/amp</directory>
                <targetPath>${app.amp.output.folder}</targetPath>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.alfresco.maven.plugin</groupId>
                <artifactId>alfresco-maven-plugin</artifactId>
                <version>2.2.0</version>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    <build>
</project>

This defines amp as the packaging type of your project and brings in our alfresco-maven-plugin which provides this packaging. Our plugin is available through maven central so that you do not need to define an additional plugin repository.


Required files

Each AMP project should consist at least of these files:

/my-awesome-module
  |
  +-- pom.xml
  |
  +-- src
       |
       +-- main
            |
            +-- amp
            |    |
            |    +-- module.properties
            |
            +-- resources
                 |
                 +-- alfresco
                      |
                      +-- module
                           |
                           +-- my-awesome-module
                                |
                                +-- module-context.xml

The module.properties file defines this module and helps our module manager to do its job. It basically looks like this:

module.id=my-awesome-module
module.title=My awesome module
module.description=This is an awesome module
module.version=1.0
module.repo.version.min=6.0

The second file is the entry point to the Spring context that makes up the ACS server. You can define new beans in there that will be instantiated during startup and that can be wired up with other components of ACS.


Dependencies

If your project contains Java code, as almost all AMP projects do, then you need to define certain artifacts from the ACS 6 repository as dependencies:

<project>
    ...
    <packaging>amp</packaging>
    <dependencies>
        <dependency>
            <groupId>org.alfresco</groupId>
            <artifactId>alfresco-repository</artifactId>
            <version>6.37</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.alfresco</groupId>
            <artifactId>alfresco-remote-api</artifactId>
            <version>6.23</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>alfresco-public</id>
            <url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
        </repository>
    </repositories>
    <build>
        ...
    <build>
</project>

From ACS 6 onwards, each artifact is on its own lifecycle. You can look up the version numbers of each artifact either in the release notes or simply take a look in your ACS 6 deployment.
It is important to define each artifact as provided so that it is only used during the compilation of your Java classes, but it is not packaged into the AMP file.
If your customization depends on enterprise specific bits, then you need to include the artifacts alfresco-enterprise-repository and alfresco-enterprise-remote-api as well.

Since our artifacts are not always available through maven central, you should add our artifact repository to your build as shown above.


Testing

During the test phase in maven, you should only run isolated jUnit tests that do not require a full ACS repository.
Tests that require a repository, e.g. to create or modify nodes, should be run as integration tests.
The default configuration of the "surefire" (test) and "failsafe" (integration-test) plugins define wildcards for test cases. All classes in src/main/test with a name like *Test are run during the test phase and all classes with a name like *IT are run during the integration-test phase.
Your integration tests then might look like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( { "classpath:alfresco/application-context.xml" } )
@TestExecutionListeners( listeners = { DependencyInjectionTestExecutionListener.class,
                                       MyAwesomeModuleIT.class} )
public class MyAwesomeModuleIT extends AbstractTestExecutionListener
{

    @Autowired
    @Qualifier("NodeService")
    protected NodeService nodeService;

    @Autowired
    @Qualifier("FileFolderService")
    protected FileFolderService fileFolderService;

    @Before
    public void initializeTest()
    {
    }

    @After
    public void cleanupTest()
    {
    }

    @Test
    public void testAwesomeFeature() throws Exception
    {
    }

}

This definition of a jUnit test executes the tests in a Spring environment.The @ContextConfiguration of the spring jUnit runner then loads the main ACS context file which starts up the entire ACS 6 repository.

These jUnit tests require additional dependencies in your pom.xml file:

<project>
    ...
    <dependencies>
        ...
        <!-- test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4.1212</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.0.6.RELEASE</version>
            <type>jar</type>
            <scope>test</scope>
        </dependency>
    </dependencies>
    ...
</project>

And we need to configure the maven-failsafe-plugin plugin to set the required ACS configuration properties as system properties:

<project>
    ...
    <build>
        <plugins>
            ...
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.17</version>
                <configuration>
                    <systemPropertyVariables>
                        <db.name>acs-test</db.name>
                        <db.driver>org.postgresql.Driver</db.driver>
                        <db.url>jdbc:postgresql://localhost:${database.port}/acs-test</db.url>
                        <dir.root>${project.build.directory}/alf-data-test</dir.root>
                    </systemPropertyVariables>
                </configuration>
                <executions>
                    <execution>
                        <id>integration-test</id>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    <build>
</project>

The configuration above uses a PostgreSQL database on localhost for testing. We suggest to use docker to provide this temporary database instance during the integration tests:

<project>
    ...
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.25.0</version>
                <configuration>
                    <images>
                        <image>
                            <alias>test-database</alias>
                            <name>postgres:9.4.12</name>
                            <run>
                                <ports>
                                    <port>database.port:5432</port>
                                </ports>
                                <env>
                                    <POSTGRES_PASSWORD>alfresco</POSTGRES_PASSWORD>
                                    <POSTGRES_USER>alfresco</POSTGRES_USER>
                                    <POSTGRES_DB>acs-test</POSTGRES_DB>
                                </env>
                                <cmd>
                                    <shell>-c max_connections=300</shell>
                                </cmd>
                                <wait>
                                    <log>database system is ready to accept connections</log>
                                    <time>20000</time>
                                </wait>
                            </run>
                        </image>
                    </images>
                </configuration>
                <executions>
                    <execution>
                        <id>start</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>stop</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    <build>
</project>

This pulls the image postgres in version 9.4.12 from DockerHub and starts a new, clean container from this image. After running the integration tests, it stops and removes this image again.
This requires that you have Docker installed and set up properly on your machine.

Filter Blog

By date: By tag: