stefankopf

Migrating Alfresco SDK2 projects to ACS 6

Blog Post created by stefankopf Employee on Apr 24, 2018

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.

Outcomes