Search code examples
alfresco

What does RetryingTransactionHandler work within alfresco


Alfresco advise the programmer once you need to make a transaction into the repository to use RetryingTransactionHandler to achieve the transaction.

Can anyone give me a clarification bout that ?

specifically i'm talking about code like this

RetryingTransactionCallback<String> callback = new RetryingTransactionCallback<String>(){
 public String execute() throws Throwable {
     // doProcess must be invoked within user context.
 AuthenticationUtil.runAs(new RunAsWork<String>(){
 public String doWork()throws Exception{
 try {      
     if(getOperationType().equals(OperationTypes.CREATE_ORGANIZATION_OPERATION)){
 RetryingTransactionHelper txnHelper = 
     Repository.getRetryingTransactionHelper(FacesContext.getCurrentInstance());
     txnHelper.doInTransaction(doProcessActionCallbackOperations.CreateOrganizationCallback, false, true);
                        }
                    } catch(Throwable e){

                    }
                    return "";
                }
            }, AuthenticationUtil.getSystemUserName());
            return "";
        }
    };  
    try {
        RetryingTransactionHelper txnHelper = Repository.getRetryingTransactionHelper(FacesContext.getCurrentInstance());
        txnHelper.doInTransaction(callback, false, true);
    } catch (Throwable e) {
        if (e instanceof ReportedException == false) {
            Utils.addErrorMessage(formatErrorMessage(e), e);
        }
        ReportedException.throwIfNecessary(e);
    }

Also, as you are notice i use AuthenticationUtils.runAs().

So can you help me to understand different keys in this code like RetryingTransaction & AuthenticationUtils ?


Solution

  • The RetryingTransactionHelper ensures the callback you provide is executed within a transaction. Moreover, as the name suggests, if the transaction fails for some reasons (e.g. an Exception is thrown in the callback code that's not properly caught, or you try to change the same content in the repository concurrently from two different threads), the same transaction will be properly rolled back and re-executed ("retried") a configurable amount of times (default: 20). This is handier than manually handling transactions more often than not.

    In the code you posted, you are nesting the call to doProcessActionCallbackOperations.CreateOrganizationCallback in two transactions (because of the last parameter of doInTransaction being always true, i.e. requiresNew), the outer being created before callback gets executed, the inner the one created within the callback itself. It seems to me that you could get rid of the inner transaction, but that's up to you. Transactions can be nested, even though failures in an inner transaction will most likely invalidate the whole stack of transactions.

    The AuthenticationUtils.runAs facility allows you to execute pieces of logic using an authority in general different from the current user. Internally, user credentials are stored as ThreadLocal parameters. In you case, the AuthenticationUtil.runAs takes care of temporarily change such ThreadLocal to allow the inner callback to always execute with the highest permission level (i.e. system) regardless of which user is executing the code. There are also situations where there's no logged in user currently set, e.g. within scheduled jobs. Then the AuthenticationUtils.runAs allows you to access the content into the repository, usually specifying system or admin users.