10 Replies Latest reply: Dec 15, 2011 9:52 AM by Vyacheslav Sakhno RSS

CustomeRevisionListener -> how to set current user

henry donnell Newbie

I have my own CustomerRevListener implementing RevisionListener interface.
But I am not using Seam. So the question is how can I inject current user Id into this listener. The problem is this listener is instantiated by Envers and the user is known in my case in a JSF Managed Bean.
So what is the best way to inject the current user id into that listener?

  • 1. Re: CustomeRevisionListener -> how to set current user
    Hernán Chanfreau Master

    Hi! We have the sama situation. We use Spring as a bean container.
    Our solution is:
    - Our listener collects meta-data providers for revision, there can be n providers, and each one adds somi information to the revision.
    - There is one meta-data provider that adds the user info to the revision.
    - The listener calls all the meta-data providers when a new revision is created, so the revision contains the meta-data you need.

    Hernan.


  • 2. Re: CustomeRevisionListener -> how to set current user
    henry donnell Newbie

    Hi Hernan,

    thanks for sharing your ideas.
    Finally I did find a solution to transport current user's ID to the custom RevListener class. I used ThreadLocal for that. That works fine and there is no coupling between the layers which would be a bad practice.
    Another way how to do it would be to use AuditReader's method getCurrentRevision and update the properties via setters. I didn't check this method but it should work well too.

    As I understand, you have a 3rd solution with spring. can you describe it better?
    How do you access your spring beans from the revListener?

  • 3. Re: CustomeRevisionListener -> how to set current user
    Hernán Chanfreau Master

    Hi!

    Our revisionListener is a spring bean itself and has an init method for bean inicialization. During initialization, the listener collects all meta-data providers that are spring beans too and register them.

    When newRevision() is called, the listener delegates to each provider the responsability of adding info to the new revision.

    One issue is to taking care that each meta-data provider must be thread-safe. In other words, you can´t keep instance variables or mark the access as synchronized.

    I wish it helps. Hernan.

  • 4. Re: CustomeRevisionListener -> how to set current user
    Alex Schefer Newbie

    Hernan,

     

    I realize this is an old post, so things may have changed.. but I was wondering if you could clear up some confusion I have on this same issue. How do you get the revisionListener that is managed by spring to be the same instance which is called by the envers code? I don't understand how the meta-data providers come into play.

     

    Many thanks..

     

    Alex

  • 5. Re: CustomeRevisionListener -> how to set current user
    Hernán Chanfreau Master

    Hi!

     

    I'll try to explain withour looking at the code...

     

    My revisionListener es defined as a spring bean and implements ApplicationContextAware interface. It let me to access spring applicationContext inside a initialization method. Also I create an interface that is implementes by all my metadata providers. During initialization I ask the applicationContext for all the beans of this interface as type. So I get all providers there and put them in a static variable in my RevisionListener. This initialization is only a configuration for the real use.

     

    In conclusion, I have two instances of the listener, the first one is for configuration and the other is created by envers (this is the "real" one and uses the info added by the other to run). I could add some of my code tomorrow if your confusion remains...

     

    Hope it helps. Hernán.

  • 6. Re: CustomeRevisionListener -> how to set current user
    Alex Schefer Newbie

    Thanks Hernan, that did help. I didn't end up doing it the way you suggest but it was helpful none the less.

     

    I ended up tagging my entity with @Configurable and using spring to inject a session scoped bean which has the logged in user information.

     

    - Alex

  • 7. Re: CustomeRevisionListener -> how to set current user
    Syed Mahdi Newbie

    Hi All,

     

    Alex! Thats a different idea, i would like to hear more about it.

     

    how did you inject a session scoped bean using spring. how did @configurable help you in that. we used to have a session scoped bean that we were storing our user in but We didn't knew how to get it across the request/web scope like in  service layer or in the listener.

     

    I had the same issue. I used Spring Security framework. There is a SecurityConextHolder which is threadlocal to every user. So i used that to get the principal. The only hard part was to setup spring security, but after that its a two line code to get to the logged in user.

  • 8. Re: CustomeRevisionListener -> how to set current user
    Vyacheslav Sakhno Newbie

    Hi Hernán, can you post the code please?

  • 9. Re: CustomeRevisionListener -> how to set current user
    Hernán Chanfreau Master

    Hi!

     

    the custom revision listener is a spring bean, defined like

     

        <bean id="metadataRevisionListener" class="package.MetadataRevisionListener"

            init-method="initialize">

            ...

        </bean>

     

    MetadataRevisionListener class: (you must undestand ApplicationContextAware from spring api)

     

    public class MetadataRevisionListener implements RevisionListener, ApplicationContextAware {

     

     

      /** spring context */

      private static ApplicationContext applicationContext;

     

      private static List<IMetadataRevisionProvider> providers = new ArrayList<IMetadataRevisionProvider>();

     

     

      /**

       * ask applicationContext for beans implementing IMetadataRevisionProvider.

       */

      public void initialize() {

     

        final Map<String, IMetadataRevisionProvider> beans = this.getApplicationContext().getBeansOfType(

            IMetadataRevisionProvider.class, false, false);

     

        final String className = IMetadataRevisionProvider.class.getSimpleName();

        LOGGER.info("Found " + beans.size() + " " + className);

     

        // adding each bean to the list

        for (String beanName : beans.keySet()) {

          final IMetadataRevisionProvider bean = beans.get(beanName);

          LOGGER.info("Adding " + className + " (" + bean.getClass().getCanonicalName()

              + ") as >metadata revision provider");

     

          providers.add(bean);

        }

      }

     

      /**

       * @param pApplicationContext

       * @throws BeansException

       * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)

       */

      public void setApplicationContext(ApplicationContext pApplicationContext) throws BeansException {

        applicationContext = pApplicationContext;

      }

     

      /**

       * @return spring applicationContext

       */

      private static ApplicationContext getApplicationContext() {

        return applicationContext;

      }

     

      /**

       * Method called when a new revision is created.

       *

       * @param genericRevision

       * @see org.hibernate.envers.RevisionListener#newRevision(java.lang.Object)

       */

      public void newRevision(Object genericRevision) {

        Revision revision = (Revision) genericRevision;

     

        ...

     

        LOGGER.debug("Adding metadata to the new revision.");

     

        for (IMetadataRevisionProvider provider : providers) {

          provider.addMetadataToRevisionEntity(revision);

     

        }

      }

     

     

    Each IMetadataRevisionProvider has to be a spring bean, like:

     

        <bean id="userInfoRevisionProvider"

            class="package.UserInfoRevisionProvider">

        </bean>

     

    and the class:

     

    public class UserInfoRevisionProvider implements IMetadataRevisionProvider {

     

    @Override

      public void addMetadataToRevisionEntity(IRevisionMetadata revision) {

     

        try {

     

          LOGGER.debug("Adding user metadata to revision");

       

        // here you put your own code for adding metadata!

         

          revision.setUser(SecurityManager.getCurrentUser());

     

        } catch (Exception e) {

          LOGGER.error("Error, cant set user on revision", e);

          revision.setUser("");

        }

      }

    }

     

    And the interface for all providers:

     

    public interface IMetadataRevisionProvider {

     

      /**

       * Adds metadata to the revision.

       * @param revisionMetadata

      */

      void addMetadataToRevisionEntity(IRevisionMetadata revisionMetadata);

     

    }

     

     

    Finally, I have my own revision entity with the structure for adding all metadata I need.

     

    Hope it will be helpful!

     

    Regards. Hernán.