Developing repo actions in Alfresco 5.1

Recently I’ve been told about an issue in our software related to a custom action which was modifying a NodeRef created inside the current transaction.

In Alfresco Share, a rule on a folder can be configured to execute an action by selecting two different modes:

  • Run in background
    • The action is executed in a new thread
    • The new thread creates a new transaction for execution, so any other transaction modifying the node (as rendition operations on brand new nodes) should provoke a ConcurrencyFailureException
    • It’s required to use a RetryingTransactionHelper to avoid this kind of conflicts
  • Run in foreground
    • The action is executed within the current thread and within the current transaction
    • No special code is required to perform modifications on the node, as no ConcurrencyFailureException will arise

If this condition could be detected, a code for each situation could be developed. However, I’ve found an issue with Alfresco API and there is no way to know if the action is being executed synchronously or asynchronously.

I’ve created the following pattern to cover both behaviors with the same code.


public class CustomAction extends ActionExecuterAbstractBase {

    @Override
    protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {

        try {
            executeInNewTransaction(actionedUponNodeRef);
        } catch (Throwable throwableNewTransaction) {
            executeImplInternal(actionedUponNodeRef);
        }

    }

    // Avoid ConcurrencyFailureException by using RetryingTransactionHelper
    private void executeInNewTransaction(final NodeRef nodeRef) {
        
        RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable {
                executeImplInternal(nodeRef);
                return null;
            }
        };
        RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
        txnHelper.doInTransaction(callback, false, true);
    }

    private void executeImplInternal(NodeRef actionedUponNodeRef) {
       // Do whatever you need with the current transaction
    }

}

Obviously it’s not the better solution in terms of performance, but it works for both scenarios.

Anuncios

Alfresco – Using plain HTTP for SOLR from Alfresco 5.1

Official Alfresco documentation on this is now available: http://docs.alfresco.com/5.1/tasks/running-without-ssl.html

In Alfresco 5.0 and before, configuring SOLR to run without SSL, involved modifying both SOLR and Alfresco web.xml files. This modifications make harder AMP addon deployments and some other maintenance operations.
From Alfresco 5.1 this feature can be configured without any web.xml modification, thanks to the new X509Filters included in SOLR and Alfresco repo:

Instructions for Alfresco 5.1

  1. Set the solr.secureComms property to none in the alfresco-global.properties file.
  2. Ensure that the solr.port property is set to the correct non-SSL port of the application server in which Solr is running.
  3. In the <solrRootDir>/archive-SpacesStore/conf/solrcore.properties file and <solrRootDir>/workspace-SpacesStore/conf/solrcore.properties file, do the following:
    1. Set the property alfresco.secureComms property to none.
    2. Ensure that the alfresco.port property is set to the correct non-SSL port of the application server in which your repository is running.

Caution

Use this configuration only when your application server is behind a web server (ELB, Apache, Nginx or whatever) and cut any request outside from localhost to api/solr.

Alfresco documentation for 5.1 release will be available in the future. Please refer this resource before following this instructions.

Alfresco: extending Aikau pages using Aikau services

Alfresco official documentation, which lives in http://docs.alfresco.com , has been expanded during the last year and nowadays, it contains detailed information on many technical topics.

One of these topics is Alfresco Share extension, including different extension techniques such as Surf Pages, Aikau Surf Pages, Aikau widgets, Web Scripts, Spring Surf extensions and Dashlets.

Recently, I’ve been working on an addon in order to allow Site Manager to set any page as Site Home Page. Besides other well known extension techniques, I’ve used the new Aikau framework to develop some part of my code.

Below my lessons learned are exposed.

Goal

alfresco-developing-aikau

Configuring Aikau services for a module

In order to get our Aikau service available in Share Header extension, it’s required to declare it at module extension section site-home-page-extension.xml

<extension>
    <modules>
        <module>
            <id>Select home page for Site</id>
            <version>1.0</version>
            <auto-deploy>true</auto-deploy>
            <!-- Declare Aikau service -->  
            <configurations>
                <config evaluator="string-compare" condition="WebFramework" replace="false">
                    <web-framework>
                        <dojo-pages>
                            <packages>
                                <package name="sitehomepage" location="js/keensoft/sitehomepage"/>
                            </packages>
                        </dojo-pages>
                    </web-framework>
                </config>
            </configurations>
            <!-- Share header extension -->
            <customizations>
                <customization>
                    <targetPackageRoot>org.alfresco.share.header</targetPackageRoot>
                    <sourcePackageRoot>es.keensoft.share.header</sourcePackageRoot>
                </customization>
            </customizations>
        </module>
    </modules>
</extension>

Developing an Aikau service

The dojo based definition is included in a new file, called SiteHomePageService.js, stored at js/keensoft/sitehomepage, which is the identifier used on the location tag in previous Aikau service configuration.

In my case, URL page selected by the user must be sent to a Repo Web Script, so I have to use XHR module.

define(["dojo/_base/declare",
        "alfresco/core/Core",
        "alfresco/core/CoreXhr",
        "dojo/request/xhr",
        "dojo/json",
        "dojo/_base/lang",
        "dojo/dom",
        "dojo/dom-attr",
        "dojo/dom-class",
        "service/constants/Default"],
        function(declare, AlfCore, AlfXhr, xhr, JSON, lang, dom, domAttr, domClass, AlfConstants) {

   return declare([AlfCore, AlfXhr], {
     
      TOPIC_SET_CURRENT_PAGE_AS_SITE_HOME: "ALF_SET_CURRENT_PAGE_AS_SITE_HOME",

      //...
     
      
      onSetCurrentPageAsHome:  function share_services_SiteHomePageService__onSetCurrentPageAsHome(publishPayload) {
         if (publishPayload && publishPayload.servletContext) {
             
            // Get current page
            var currentPage = window.location.href;
            currentPage = currentPage.replace(window.location.origin + publishPayload.servletContext, "/page");
            
            // Send current page to custom Repo Web Script
            this.serviceXhr({
               url: AlfConstants.PROXY_URI + "keensoft/site/home/" + currentPage,
               method: "POST",
               data: {},
               successCallback: this.onSetHomePageSucess,
               callbackScope: this
            });            
            
           // Publish event
           this.alfPublish(this.setSiteHomePageTopic, { homePage: currentPage });
           
         }
      },
      
      //...

  });
});

Extending an Aikau page

Once the extension is declared and the Aikau service is available, Aikau page extension can be performed. As my new link (“Use current page”) is at Share Header, I have to extend share-header.get.js Alfresco page.

// Find dropdown menu in Share Site header
var siteConfig = widgetUtils.findObject(model.jsonModel, "id", "HEADER_SITE_CONFIGURATION_DROPDOWN");

if (siteConfig != null) {
    
    // Repo Web Script invocation to determine if current user is Site Manager
    var userIsSiteManager = false, obj = null;
    json = remote.call("/api/sites/" + page.url.templateArgs.site + "/memberships/" + encodeURIComponent(user.name));
    if (json.status == 200) {
       obj = JSON.parse(json);
    }
    if (obj) {
       userIsSiteManager = obj.role == "SiteManager";
    }

    if (userIsSiteManager) {
        
        // Include our new Aikau Service
        model.jsonModel.services.push("sitehomepage/SiteHomePageService");
        
        // Include our new Aikau Component
        siteConfig.config.widgets.push({
            
            id : "HEADER_SITE_MENU_HOME_PAGE_GROUP",
            name : "alfresco/menus/AlfMenuGroup",
            config :
            {
                // Separator
                label: "Home page",
                widgets:
                [
                    {
                        // Link to set current page as Site Home Page
                        id : "HEADER_SITE_MENU_HOME_PAGE",
                        name : "alfresco/menus/AlfMenuItem",
                        config : {
                            id : "HEADER_SITE_MENU_SET_CURRENT_PAGE_AS_HOME",
                            label : "Use current page",
                            title: "Use current page",
                            iconAltText: "Use current page",
                            iconClass: "alf-edit-icon",
                            // The topic to be published, our Aikau service is also subscribed to it
                            publishTopic: "ALF_SET_CURRENT_PAGE_AS_SITE_HOME",
                            // Topic payload, current page
                            publishPayload: {
                               servletContext: page.url.servletContext
                            }
                        }
                    }
                ]
            }
        
        });
        
    }
    
}

Conclusions

Aikau may appear a hard framework to learn; but improved Alfresco documentation, code samples extracted from Alfresco source code, the extended and detailed Aikau learning material and the fact that every day more and more Alfresco Share source code is based in Aikau should be enough to make the transition to this technology softer for Alfresco developers.

Remember that all this code is available as an Alfresco SDK 2.1 project at https://github.com/angelborroy/alfresco-site-home-page