Monday, October 7, 2013

How to access the Alfresco repository from Liferay Portal

A few years ago I was responsible for some Partner Certfication projects for the database system vendor Ingres. Two of the partners were Alfresco and Liferay. So we investigated how to combine them best. The answer seemed to be a portlet which accesses Alfresco, by just using Alfresco as the Content Repository. A few years later I am now investigating it again from the point of view of a Consultant. So I evaluated some well documented ways to integrate the DMS Alfresco into the Portal System Liferay. The result is not that bad, but not really what I wanted.

There are basically two approaches. The first one is to embed Alfresco's UI (Alfresco Share) as a Dashlet. This has challenges and raises questions. For instance: What's about permissions within the Content Repository? If you know Alfresco, then you also know that it comes with 2 tiers aka 2 web applications. The application Share is repsobsible to show you the UI, whereby the application Alfresco is used to provide you the repository layer. The interesting part of this story is that it is just possible to deploy the applications seperated. So Alfresco Share is just a WebApp which performs REST to the service layer in order to show some data. Share's endpoint configuration allows you to configure which Alfresco Repository should be used. Exactly this, let's name it feauture, is helpful to integrate Share with Liferay. Basically, the whole Share applications gets deployed to Liferay. Share is then configured to access the repository layer which is hosted somewhere else. Alfresco Share is build on top of the Spring SURF framework. Which means that Web Scripts are used in order to show something (like Dashlets ... do not mix it up with Portlets). So as far as I understood it, several of those UI Web Scripts are made available as Portlets by having a Portlet descriptor within the Share application. This seems to be OK and also has advantages. Another more Portal System like idea would be to have a standard portlet (JSR 168, or maybe one which is a little bit more Liferay specific) which then performs the CMIS call to the underlying repository. The last metioned approach has some disadvantages regarding document focused customizations (So documents can have types with several properties and so on. So the portlet needs to be configured which properties of which document type should be presented to the end user. I guess this can become a kind of complex, and so Alfresco decided to just provide Share itself - which can be easily customized - to realize the portlets.). But to use Share as a Portlet causes another problem. Share requires that the user is authenticated and has a specific role (Site Manager, Site Contributor, ...) within the Alfresco repository in order to access documents. The way how this integration solved it, is to extend the Alfresco configuration by adding an extra external Authentication Subsystem. The Share application which is deployed into Liferay was configured to use this external and Cookie based authentication. When a Liferay user logs in the first time, a new Alfresco user with the same name is created within Alfresco, whereby the authentication itself happens in Liferay and not in Alfresco. I think this sufficient for demonstration or evaluation purposes, but not really an Enterprise scenario. In such a scenario you would have a standalone Alfresco repository which uses an LDAP or AD for authentication purposes. Every LDAP user would be already in Alfresco. So an interesting question would be if this external authentication integrates well with other authentication mechanisms. So the idea is that Liferay has exactly the same users synchronized as Alfresco, and so it is not required to recreate the user in Alfresco. This way, the Alfresco user could be also a kind of preconfigured regarding his/her site memberships in Alfresco. So here are the steps how to embed Alfresco Share into Liferay by accessing the Alfresco remote repository (https://www.liferay.com/de/web/navin.agarwal11/blog/-/blogs/integration-with-alfresco-4-x-and-liferay-6-1):


  • We assume that you installed Alfresco and Liferay not in the same Tomcat Application Container. So they have both different port settings. I installed at first Alfresco by choosing port 8081 and then Liferay by keeping port 8080.
  • In ${LIFERAY_HOME}/tomcat/conf/catalina.properties configure an additional classpath location ${LIFERAY_HOME}/tomcat/shared/classes in order to have a place where you can add Alfresco's overriding configuration files (The XML and property files are used to override the default configuration.). To do so add the following line: shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
  • In Alfresco's global configuration file ${ALF_HOME}/tomcat/shared/classes/alfresco-global.properties extend the authentication chain by adding the external authentication configuration: 
authentication.chain=alfrescoNtlm1:alfrescoNtlm,external1:external
external.authentication.proxyUserName=
  • Copy the share.war file to ${LIFERAY_HOME}/deploy
  • Copy the folder '${ALF_HOME}/tomcat/shared/classes/alfresco/web-extension' and all it's contents to '${LIFERAY_HOME}/tomcat/shared/classes/alfresco'
  • Modify the file  '${LIFERAY_HOME}/tomcat/shared/classes/alfresco/share-config-custom.xml' in order to configure the endpoints.
<alfresco-config>
   <!-- Repository Library config section -->
   <config evaluator="string-compare" condition="RepositoryLibrary" replace="true">
      <!--
         Whether the link to the Repository Library appears in the header component or not.
      -->
      <visible>true</visible>
   </config>
   <config evaluator="string-compare" condition="Remote">
      <remote>
         <endpoint>
            <id>alfresco-noauth</id>
            <name>Alfresco - unauthenticated access</name>
            <description>Access to Alfresco Repository WebScripts that do not require authentication</description>
            <connector-id>alfresco</connector-id>
            <endpoint-url>http://localhost:8081/alfresco/s</endpoint-url>
            <identity>none</identity>
         </endpoint>
        <!-- Define a new connector -->
        <connector>
                <id>alfrescoCookie</id>
                <name>Alfresco Connector</name>
                <description>Connects to an Alfresco instance using cookie-based authentication</description>
                <class>org.springframework.extensions.webscripts.connector.AlfrescoConnector</class>
        </connector>
        <!-- Use the Cookie based authentication by default -->
         <endpoint>
            <id>alfresco</id>
            <name>Alfresco - user access</name>
            <description>Access to Alfresco Repository WebScripts that require user authentication</description>
            <connector-id>alfrescoCookie</connector-id>
            <endpoint-url>http://localhost:8081/alfresco/wcs</endpoint-url>
            <identity>user</identity>
            <external-auth>true</external-auth>
         </endpoint>
<endpoint>
            <id>alfresco-feed</id>
            <name>Alfresco Feed</name>
            <description>Alfresco Feed - supports basic HTTP authentication via the EndPointProxyServlet</description>
            <connector-id>http</connector-id>
            <endpoint-url>http://localhost:8081/alfresco/s</endpoint-url>
            <basic-auth>true</basic-auth>
            <identity>user</identity>
         </endpoint>
         <endpoint>
            <id>activiti-admin</id>
            <name>Activiti Admin UI - user access</name>
            <description>Access to Activiti Admin UI, that requires user authentication</description>
            <connector-id>activiti-admin-connector</connector-id>
            <endpoint-url>http://localhost:8081/alfresco/activiti-admin</endpoint-url>
            <identity>user</identity>
         </endpoint>
      </remote>
   </config>
</alfresco-config>

  • Restart Alfresco
  • Restart Liferay
  • Open Liferay, create a new Page and add a new Application for it. You can see that there is a new Application category which is named Alfresco. These are your Portlets.
  • BTW: You can find the portlet descriptor (portlet.xml) within Share at ${SHARE}/WEB-INF
The following YouTube video explains the result and the usage best: http://www.youtube.com/watch?v=On7SfssX8TI

If you know Liferay, then you may think 'Hey, Liferay already has DMS functionality. Why should I use another Content Repository?'. The answer is a statement like 'The right tool for the right job.'. Liferay is a Portal System. It is intended to provide you an unified view to your company's (or organization's) Tools, Documents and Web Content. Alfresco was orignally more a pure Document Management System. The purpose was to provide you easy access to your content/document together with structured information about it. (and other cool features like document oriented workflows, document behaviours, content rules, ...) So even if there was a convergence of the two systems, I would still say: 'Use Liferay for Portals and Intranets and use Alfresco to manage Documents (their Properties, their Life Cycles and the Processes of them)'. So it makes just sense to combine Liferay and Alfresco. But the question 'Why should I not use the Liferay Document Library?' is still valid in the sense of 'Why should Alfresco not be integrated into the Liferay Document Library?'. So the answer is 'It should!' and the following part of this article will show how. There is a very cool feature in Liferay which allows you to just add new CMIS repositories (You can find some details about CMIS in my blog, but it basically means a more or less standardized protocol to access Content Repositories). The way how it should work is described here: http://www.liferay.com/de/web/alexander.chow/blog/-/blogs/7670631 . Unfortunatly, it was impossible for me to test this feature because Liferay just refused to add my Repo with an error message like 'Please check your repository configuration.'. Maybe it works better in a later version. However, I got it running by adding my CMIS configuration to Liferay's external configuration file. Here are the steps:

  • In '${LIFERAY_HOME}/tomcat/webapps/ROOT/WEB-INF/classes/portal-ext.properties' add the following lines:

## CMIS
#
dl.store.impl=com.liferay.portlet.documentlibrary.store.CMISStore
dl.store.cmis.credentials.username=admin
dl.store.cmis.credentials.password=admin
dl.store.cmis.system.root.dir=Liferay Home
#dl.store.cmis.repository.url=http://localhost:8081/alfresco/cmisatom
dl.store.cmis.repository.url=http://localhost:8081/alfresco/service/cmis
session.store.password=true
company.security.auth=screenName

  • Important is that the screenName is used to authenticate. Alfresco uses normally not the email address as the user name. So the screen name should match the user id.
  • The CMIS AtomPub service is available via '/alfresco/service/cmis'. With Alfresco 4.x an additional JSON CMIS binding was introduced, and so there is now also the 'alfresco/cmisatom' service.
  • Log in to Liferay, open the Control Panel, choose the Global settings and add a new root folder which is named 'Liferay Home', then upload a test document.
  • Open Alfresco Share and navigate to the repository view. You can find a 'Company Home/Liferay Home' folder. 
The bad part of the story is that Liferay seems to use the CMIS repository the same way as a file system. It creates some cryptical folder names (maybe internal id-s) and places files with the extension 'bin' inside them. So the documents are not human readable in Alfresco. As far as I understood, this was not really the intend of CMIS (Here a short JavaDoc excerpt of the Java Client OpenCMIS: http://chemistry.apache.org/java/0.9.0/maven/apidocs/org/apache/chemistry/opencmis/client/api/Folder.html ).

So what are the options? There is still the option to develop an own Portlet. It could use CMIS or Alfresco's REST API. Another option is to develop an Alfresco Hook which goes beyond Liferay's CMIS Hook. A Hook (in Liferay) is an extension which uses an existing extension point. As far as I understood it is possible to replace the whole Document Library provider by using an own implementation.



What do you think? Any success stories about Alfresco & Liferay integrations?

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete