cancel
Showing results for 
Search instead for 
Did you mean: 

Transaction must be active and synchronization is required

mliedtke
Champ in-the-making
Champ in-the-making
Hi,
I'm getting an error when trying to deploy a webscript in liferay. 

I'm using LifeRay 4.4.1 with Alfresco 2.1.1 deployed as a portlet on JBoss 4.2, and when I include any of the out of the box WebScrips portlets I get the following error.


The Web Script /alfresco/168s/ui/myspaces has responded with a status of 500 - Internal Error.

500 Description:   An error inside the HTTP server which prevented it from fulfilling the request.

Message:   Transaction must be active and synchronization is required

Exception:   org.alfresco.error.AlfrescoRuntimeException - Transaction must be active and synchronization is required
   
   org.alfresco.repo.transaction.AlfrescoTransactionSupport.registerSynchronizations(AlfrescoTransactionSupport.java:389)
   org.alfresco.repo.transaction.AlfrescoTransactionSupport.getSynchronization(AlfrescoTransactionSupport.java:374)
   org.alfresco.repo.transaction.AlfrescoTransactionSupport.bindDaoService(AlfrescoTransactionSupport.java:237)
   org.alfresco.repo.transaction.TransactionalDaoInterceptor.invoke(TransactionalDaoInterceptor.java:66)
   org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:176)
   org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:210)
   $Proxy306.getNode(Unknown Source)
   org.alfresco.repo.node.db.DbNodeServiceImpl.exists(DbNodeServiceImpl.java:166)
   sun.reflect.GeneratedMethodAccessor312.invoke(Unknown Source)
   sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   java.lang.reflect.Method.invoke(Method.java:597)
   org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:281)
   org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:187)
   org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:154)
   org.alfresco.repo.transaction.TransactionResourceInterceptor.invoke(TransactionResourceInterceptor.java:129)
   org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:176)
   org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:210)
   $Proxy308.exists(Unknown Source) […]


One curious thing to note though if I access the portlet directly (ie not through the liferay portal container) with the URL "http://server.domain.com:8080/alfresco/168s/ui/myspaces" I can see it properly.  I'm hoping that I just missed something small, but I'm lost

If anyone has any clues on this please help

Thanks,
Mark
14 REPLIES 14

mliedtke
Champ in-the-making
Champ in-the-making
I was able to get it working (Alf 2.1.? com & Liferay 5.0.?) with the updated auth code…provided earlier in the post, but ended up taking a different root. 

I ended up implementing CAS for auth, and it has worked out very well…I used the version posed on the Liferay site (too lazy to check what version), then decided to use an Iframe portlet to access alfresco webscripts.  I really didn't want to use CAS at first, but then decided that it was kind of clunky to work in the full alfresco client in a portlet in liferay.  When I decided to move away from "full" integration, CAS became a good option.  Also CAS ended up being really easy to set up, and it's integration with AD is really good.

I created 2 webscripts.  One is a doc list portlet for displaying the contents of a folder in a "narrow" column portlet.  The second is a my spaces portlet (a modified version of the OOTB alfresco my spaces portlet).  I use them as the primary interaction between, then I provide links to the full alfresco client; since I'm using CAS there is a seamless sign on process.  Also by using the iFrame solution, there is quite a bit less load on the Liferay server/process, allowing interaction with alfresco through the portlets to be super quick.

At this point our solution is still in the proof of concept process, but I have presented it to the CIO of my company and several members of senior leadership, and they're all pretty happy with it, and now we're looking for capital and time to fully implement.

dvignola
Champ in-the-making
Champ in-the-making
First of all, Thanks for your replay.

I don't understand very well if the solution that you decided to follow needs the CAS integration or not. Maybe my english undersanting is not too much accurate…

But I'll investigate about CAS+Alfresco+Liferay (is the cignex one??)  for sure. Thanks

Davide

mliedtke
Champ in-the-making
Champ in-the-making
Not a problem, glad if I can help. 

I actually did not use the Cignex solution…but am looking into what they did, so see if there a pieces we could use.

As far as CAS, you shouldn't need it if your access Alfresco as a strict JSR-168 portlet (but there are issues because issues with Alfreco's auth code).  But by adding in CAS, you do gain quite a bit, by adding the ability to access alfresco directly (not as a portlet), or hit a webscript directly.  Take a look at CAS, it's a pretty good product, and not that hard to get running.

ericc
Champ in-the-making
Champ in-the-making
Sorry to come back from the dead this topic.
I try to have Alfresco 2.9b working with a Liferay 5.1

I followed this tutorial : http://www.optaros.com/blogs/running-alfresco-web-scripts-liferay-portlets (with some change because we are on Liferay 5.1).
All works fine (I have my portlet available on menu) but when I add it to liferay I have the "Transaction must be active and synchronization is required" error mentioned in this topic.
I don't need CAS authentification but the JSR-168 mechanism so the code put by mliedtke interest me much but I don't know how to make it work.

I copy/paste the code, adapt the package name, add the spring jar used but I always have non resolved import for :
import org.alfresco.web.scripts.WebScriptContext;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.alfresco.web.scripts.portlet.WebScriptPortletAuthenticator;

And I don't found in Alfresco's source code this file…
So complety lost in this bug and any help will be welcome.

Thanks.

here's the class code i created…It's kind of messy, and requires adding spring-portlet.jar


/*
* Bassed off of org.alfresco.web.scripts.portlet.WebClientPortletAuthenticator
*
* Sets and gets the Authenticated user directly off the session rather then the
* application scope of the session.
*
*
*/
package com.tca.authentication;

import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.scripts.WebScriptContext;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

// TCA Added
import javax.portlet.PortletContext;

import org.alfresco.web.scripts.portlet.WebScriptPortletAuthenticator;
import org.alfresco.web.scripts.portlet.WebScriptPortletRequest;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.repository.NodeService;

import org.springframework.context.ApplicationContext;
import org.springframework.web.portlet.context.PortletApplicationContextUtils;



/**
* Portlet authenticator which synchronizes with the Alfresco Web Client authentication
*
* @author davidc
*/
public class WebClientPortletAuthenticator implements WebScriptPortletAuthenticator
{
    // Logger
    private static final Log logger = LogFactory.getLog(WebClientPortletAuthenticator.class);

    // dependencies
    private AuthenticationService authenticationService;
    private WebScriptContext scriptContext;
     
    /**
     * @param authenticationService
     */
    public void setAuthenticationService(AuthenticationService authenticationService)
    {
        this.authenticationService = authenticationService;
    }
   
    /**
     * @param scriptContext
     */
    public void setScriptContext(WebScriptContext scriptContext)
    {
        this.scriptContext = scriptContext;
    }
   
    /* (non-Javadoc)
     * @see org.alfresco.web.scripts.portlet.WebScriptPortletAuthenticator#authenticate(org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication, boolean, javax.portlet.RenderRequest, javax.portlet.RenderResponse)
     */
    public boolean authenticate(RequiredAuthentication required, boolean isGuest, RenderRequest req, RenderResponse res)
    {
       logger.debug("Starting authenticate - RequiredAuthentication: " + required + " - isGuest: " + isGuest);
       logger.debug("Using authentication service: " +authenticationService);
       
       
       PortletSession session = req.getPortletSession();
       
        // first look for the username key in the session - we add this by hand for some portals
        // when the WebScriptPortletRequest is created
        String portalUser = (String)req.getPortletSession().getAttribute(WebScriptPortletRequest.ALFPORTLETUSERNAME);
        logger.debug("User Name from request: "+portalUser);
       
        if (portalUser == null)
        {
            portalUser = req.getRemoteUser();
            logger.debug("User Name from session: "+portalUser);
        }
       
        if (logger.isDebugEnabled())
        {  
            logger.debug("JSR-168 Remote user: " + portalUser);
        }
       
        if (isGuest || portalUser == null)
        {
            if (logger.isDebugEnabled())
                logger.debug("Authenticating as Guest");
           
            // authenticate as guest
            AuthenticationUtil.setCurrentUser(AuthenticationUtil.getGuestUserName());

            if (logger.isDebugEnabled())
                logger.debug("Setting Web Client authentication context for guest");
           
            createWebClientUser(session);
            removeSessionInvalidated(session);
        }
        else
        {
            if (logger.isDebugEnabled())
                logger.debug("Authenticating as user " + portalUser);
           
            AuthenticationUtil.setCurrentUser(portalUser);

            // determine if Web Client context needs to be updated
            User user = getWebClientUser(session);
            if (user == null || !portalUser.equals(user.getUserName()))
            {
                if (logger.isDebugEnabled())
                    logger.debug("Setting Web Client authentication context for user: " + portalUser);
               
                createWebClientUser(session);
                removeSessionInvalidated(session);
            }
        }
       
        return true;
    }

    /**
     * Helper.  Remove Web Client session invalidated flag
     *
     * @param session
     */
    private void removeSessionInvalidated(PortletSession session)
    {
       if (logger.isDebugEnabled())
        {  
            logger.debug("Invalidating session:  " + AuthenticationHelper.SESSION_INVALIDATED);
        }
        //session.removeAttribute(AuthenticationHelper.SESSION_INVALIDATED, PortletSession.APPLICATION_SCOPE);
        session.removeAttribute(AuthenticationHelper.SESSION_INVALIDATED);
    }
   
    /**
     * Helper.  Create Web Client session user
     *
     * @param session
     */
    private void createWebClientUser(PortletSession session)
    {
       try{
          String loginUser = authenticationService.getCurrentUserName();
          String loginTicket = authenticationService.getCurrentTicket();
          if (logger.isDebugEnabled())
           {  
               logger.debug("Starting createWebClientUser for user: " + loginUser + " and ticket " + loginTicket);
           }
                 
          PortletContext portletContext = session.getPortletContext();
          if (logger.isDebugEnabled())
             logger.debug("##portletContext " + portletContext);
          
          ApplicationContext appContext = PortletApplicationContextUtils.getRequiredWebApplicationContext(portletContext);
          if (logger.isDebugEnabled())
                logger.debug("##appContext " + appContext);
          
          PersonService personService = (PersonService)appContext.getBean("personService");
          if (logger.isDebugEnabled())
             logger.debug("##personService " + personService);
          
          NodeService nodeService = (NodeService)appContext.getBean("nodeService");
          if (logger.isDebugEnabled())
             logger.debug("##nodeService " + nodeService);
                    
           NodeRef personRef = personService.getPerson(loginUser);
          
          if (logger.isDebugEnabled())
           {  
               logger.debug("##Successfully retrieved person reference: " + personRef);
           }
          
          User user = new User(loginUser, authenticationService.getCurrentTicket(), personRef);
          
          if (logger.isDebugEnabled())
           {  
               logger.debug("##Successfully created the user: " + user);
               logger.debug("##Setting on session: " + session);
           }
          
          //session.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user, PortletSession.APPLICATION_SCOPE);
          session.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);
          
          if (logger.isDebugEnabled())
           {  
               logger.debug("Successfully set the user on the session ###");
           }
       }
       catch(Exception e)
       {

          logger.error("Error in createWebClientUser: " + e.toString());
          e.printStackTrace();

       }
    }
   
    /**
     * Helper.  Get Web Client session user
     *
     * @param session
     * @return
     */
    private User getWebClientUser(PortletSession session)
    {
       if (logger.isDebugEnabled())
        {  
            logger.debug("Getting the user from the session:  " + AuthenticationHelper.AUTHENTICATION_USER);
        }
       /* Removed and get globally at the session
        * return (User)session.getAttribute(AuthenticationHelper.AUTHENTICATION_USER, PortletSession.APPLICATION_SCOPE);
        */
        //return (User)session.getAttribute(AuthenticationHelper.AUTHENTICATION_USER, PortletSession.APPLICATION_SCOPE);
        return (User)session.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
    }
   
}

mliedtke
Champ in-the-making
Champ in-the-making
It's been quite some time since I've looked at that solution I posted since I decided to implement CAS.  Based on what you posted
I always have non resolved import for :
import org.alfresco.web.scripts.WebScriptContext;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.alfresco.web.scripts.portlet.WebScriptPortletAuthenticator;

One thing to note is I started with the class org.alfresco.web.scripts.portlet.WebClientPortletAuthenticator from 2.0.1, and since 2.9 is really a 3.0 beta, I'm guessing that there are some significant auth changes.  You may want to grab the WebClientPortletAuthenticator  from the 2.9 code base, and apply my changes to that.  The main piece of code that you may want to look at is where it gets the user and sets it on the session.
I think that this is the main section of code that you may want to look at.

          PortletContext portletContext = session.getPortletContext();
          if (logger.isDebugEnabled())
             logger.debug("##portletContext " + portletContext);
         
          ApplicationContext appContext = PortletApplicationContextUtils.getRequiredWebApplicationContext(portletContext);
          if (logger.isDebugEnabled())
                logger.debug("##appContext " + appContext);
         
          PersonService personService = (PersonService)appContext.getBean("personService");
          if (logger.isDebugEnabled())
             logger.debug("##personService " + personService);
         
          NodeService nodeService = (NodeService)appContext.getBean("nodeService");
          if (logger.isDebugEnabled())
             logger.debug("##nodeService " + nodeService);
                   
           NodeRef personRef = personService.getPerson(loginUser);
         
          if (logger.isDebugEnabled())
           {  
               logger.debug("##Successfully retrieved person reference: " + personRef);
           }
         
          User user = new User(loginUser, authenticationService.getCurrentTicket(), personRef);
         
          if (logger.isDebugEnabled())
           {  
               logger.debug("##Successfully created the user: " + user);
               logger.debug("##Setting on session: " + session);
           }
         
          //session.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user, PortletSession.APPLICATION_SCOPE);
          session.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);

Good Luck!