Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > 2018 > April
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.

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.

sakshik

Disable Download on a File

Posted by sakshik Apr 4, 2018

Problem: You don't want to disable download for all the files. You want to have a control over disabling download action on particular files in alfresco.

Solution: Use aspects. We will conditionalize disabling download action on an aspect and then apply that aspect to the file for which we want to disable download.

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

 

Here are the steps:

First: Disable the top right corner Download button

Step 1) Stop alfresco server

Step 2) Create an XML file called hide-download-pdf.xml under

<ALF_HOME>\tomcat\shared\classes\alfresco\web-extension\site-data\extension:

<extension>
   <modules>
      <module>
         <id>Hide Download Buttons</id>
         <auto-deploy>true</auto-deploy>
         <customizations>
            <customization>
               <targetPackageRoot>org.alfresco</targetPackageRoot>
               <sourcePackageRoot>com.someco</sourcePackageRoot>
            </customization>
         </customizations>
      </module>
   </modules>

</extension>

Step 3) Create a file node-header.get.js with the following under 

<ALF_HOME>\tomcat\shared\classes\alfresco\web-extension/site-webscripts/com/someco/components/node-details

<import resource="classpath:/alfresco/templates/org/alfresco/import/alfresco-util.js">
var nodeDetails = AlfrescoUtil.getNodeDetails(model.nodeRef, model.site, null, model.libraryRoot);
var count = nodeDetails.item.node.properties["cm:autoVersion"];

if (count != undefined)
{
model.showDownload = "false";
}

Step 4) Restart alfresco server.

Go to a file in Alfresco -> Click Manage Aspects from the details options -> add cm:versionable aspect -> Hit Apply changes. You will see that the Download button will disappear

 

Second: Disable the Download action from browse options:

 

Step 1) Stop alfresco server

Step 2) To create a custom evaluator for an aspect, create an XML file custom-action-context.xml under
<ALF HOME>/tomcat/shared/classes/alfresco/web-extension folder.

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="custom.evaluator.doclib.hasversionable" class="org.alfresco.web.evaluator.HasAspectEvaluator">
<property name="aspects">
<list>
<value>cm:versionable</value>
</list>
</property>
</bean>
</beans>

 

 

Step 3) Add the following to share-config-custom.xml under

<config evaluator="string-compare" condition="DocLibActions">
<actions>
<action id="document-download" type="link" label="actions.document.download">
<param name="href">{downloadUrl}</param>
<evaluator negate=true>custom.evaluator.doclib.hasversionable</evaluator >
</action>
</actions>

Step 4) Restart alfresco server.

Go to a file in Alfresco -> Click Manage Aspects from the details options -> add cm:versionable aspect -> Hit Apply changes. You will see that the Download action from browse menu disappeared

Filter Blog

By date: By tag: