6 Replies Latest reply on Nov 15, 2010 11:27 AM by obastard

    Putting extra data in revinfo

    florin13

      We need to put a ton of extra data in the revinfo table.  Looks like this is easy, all we have to do is implement a RevisionEntity and associate a RevisionListener to update it with our extra data.  The problem is we need to store the IP address of the user among other information that is only available in a controller (we're using Spring 3.0 and webmvc) and the RevisionListener is a singleton that doesn't have access to this data.  How should we get the user's IP address (among other info)?  One idea is to use ThreadLocal to store this info.  Are there any issues with this?  Will it in fact be the same thread that calls our RevisionListener that the Controller runs on?  Is there a better way to implement our needed behavior?

        • 1. Re: Putting extra data in revinfo
          adamw

          It will be the same thread. If there's no way to statically lookup the current component I don't have any other ideas.

           

          I'd really like to provide some more integration with DI frameworks but I simply don't know how - either Envers must somehow obtain the instance of the listener, or the listener an instance of the components - in both cases a static lookup is needed.

           

          Adam

          • 2. Re: Putting extra data in revinfo
            hernanbolido

            Hi!

             

            Maybe this thread can help you.

             

            Regards. Hernán.

            • 3. Re: Putting extra data in revinfo
              obastard

              ThreadLocal is your answer.

               

              Here's what I did. I made a class called "ModelOperation.java" that holds the who, what, why, with what being an enum. Making a new one of these objects push itself into a ThreadLocal while remembering any previous value. Calling .pop on the model operation puts the previous value back (which will be null). In other words there's a stack of pending ModelOperations that hold the data:

               

               

              ModelOperation mo = new ModelOperation(what, who, why);

               

                  // do stuff

               

                mo.pop().

               

               

              If there isn't a current ModelOperation object, I throw in the Listener, which forces all the code that commits to the database to have an audit trail setup. So you need to do auditing early in development, not later.

               

              Pierce

               

              P.S.

               

              There's one issue with Envers that's kind of a pain, which is that it's kind of hard to set all that up with EJBs, which was why I was searching to see if someone had a better solution. I've ended up having to refactor things to wrap all EJB calls that require a transaction with the above code by having a wrapper EJB that calls the other EJB. Of course, its not too bad, because I need that for optimistic locking failure checks anyways, but it seems to be kind of a hole in the whole EJB spec that you can wrap an invocation, but you can't wrap the transaction commit.

               

              Anyone have a better way of solving that problem? That is, wrapping the execution of the EJB method including the transaction stuff instead of inside the transaction stuff?

              /**
              * Class to hold the current operation being performed on the model for Auditing purposes.
              *
              * @author  pierce
              */
              public class ModelOperation
              {
                  //~ Static fields/initializers -----------------------------------------------------------------
                  /** DOCUMENT ME! */
                  private static final ThreadLocal<ModelOperation> currentOperation =
                      new ThreadLocal<ModelOperation>();
                  //~ Instance fields ----------------------------------------------------------------------------
                  /** DOCUMENT ME! */
                  public ModelOperationType operationType;
                  /** DOCUMENT ME! */
                  public String who;
                  /** DOCUMENT ME! */
                  public String why;
                  /** DOCUMENT ME! */
                  public ModelOperation previous;
                  //~ Constructors -------------------------------------------------------------------------------
                  /**
                   * Creates a new ModelOperation object.
                   *
                   * @param  operationType  DOCUMENT ME!
                   * @param  who            DOCUMENT ME!
                   * @param  why            DOCUMENT ME!
                   */
                  public ModelOperation(ModelOperationType operationType, String who, String why)
                  {
                      super();
                      this.operationType = operationType;
                      this.who           = who;
                      this.why           = why;
                      push();
                  }
                  //~ Methods ------------------------------------------------------------------------------------
                  /**
                   * DOCUMENT ME!
                   */
                  public void push()
                  {
                      previous = currentOperation.get();
                      currentOperation.set(this);
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public ModelOperation pop()
                  {
                      currentOperation.set(previous);
                      previous = null;
                      return currentOperation.get();
                  }
                  /**
                   * DOCUMENT ME!
                   */
                  public static void popAll()
                  {
                      while (currentOperation.get() != null)
                      {
                          currentOperation.get().pop();
                      }
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public static ModelOperation current()
                  {
                      return currentOperation.get();
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public ModelOperationType getOperationType()
                  {
                      return this.operationType;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public String getWho()
                  {
                      return who;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  who  DOCUMENT ME!
                   */
                  public void setWho(String who)
                  {
                      this.who = who;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public String getWhy()
                  {
                      return why;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  why  DOCUMENT ME!
                   */
                  public void setWhy(String why)
                  {
                      this.why = why;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  operationType  DOCUMENT ME!
                   */
                  public void setOperationType(ModelOperationType operationType)
                  {
                      this.operationType = operationType;
                  }
              } // end class ModelOperation
              /**
              * Class to hold the current operation being performed on the model for Auditing purposes.
              *
              * @author  pierce
              */
              public class ModelOperation
              {
                  //~ Static fields/initializers -----------------------------------------------------------------
                  /** DOCUMENT ME! */
                  private static final ThreadLocal<ModelOperation> currentOperation =
                      new ThreadLocal<ModelOperation>();
                  //~ Instance fields ----------------------------------------------------------------------------
                  /** DOCUMENT ME! */
                  public ModelOperationType operationType;
                  /** DOCUMENT ME! */
                  public String who;
                  /** DOCUMENT ME! */
                  public String why;
                  /** DOCUMENT ME! */
                  public ModelOperation previous;
                  //~ Constructors -------------------------------------------------------------------------------
                  /**
                   * Creates a new ModelOperation object.
                   *
                   * @param  operationType  DOCUMENT ME!
                   * @param  who            DOCUMENT ME!
                   * @param  why            DOCUMENT ME!
                   */
                  public ModelOperation(ModelOperationType operationType, String who, String why)
                  {
                      super();
                      this.operationType = operationType;
                      this.who           = who;
                      this.why           = why;
                      push();
                  }
                  //~ Methods ------------------------------------------------------------------------------------
                  /**
                   * DOCUMENT ME!
                   */
                  public void push()
                  {
                      previous = currentOperation.get();
                      currentOperation.set(this);
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public ModelOperation pop()
                  {
                      currentOperation.set(previous);
                      previous = null;
                      return currentOperation.get();
                  }
                  /**
                   * DOCUMENT ME!
                   */
                  public static void popAll()
                  {
                      while (currentOperation.get() != null)
                      {
                          currentOperation.get().pop();
                      }
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public static ModelOperation current()
                  {
                      return currentOperation.get();
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public ModelOperationType getOperationType()
                  {
                      return this.operationType;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public String getWho()
                  {
                      return who;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  who  DOCUMENT ME!
                   */
                  public void setWho(String who)
                  {
                      this.who = who;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public String getWhy()
                  {
                      return why;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  why  DOCUMENT ME!
                   */
                  public void setWhy(String why)
                  {
                      this.why = why;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  operationType  DOCUMENT ME!
                   */
                  public void setOperationType(ModelOperationType operationType)
                  {
                      this.operationType = operationType;
                  }
              } // end class ModelOperation
              /**
              * Class to hold the current operation being performed on the model for Auditing purposes.
              *
              * @author  pierce
              */
              public class ModelOperation
              {
                  //~ Static fields/initializers -----------------------------------------------------------------
                  /** DOCUMENT ME! */
                  private static final ThreadLocal<ModelOperation> currentOperation =
                      new ThreadLocal<ModelOperation>();
                  //~ Instance fields ----------------------------------------------------------------------------
                  /** DOCUMENT ME! */
                  public ModelOperationType operationType;
                  /** DOCUMENT ME! */
                  public String who;
                  /** DOCUMENT ME! */
                  public String why;
                  /** DOCUMENT ME! */
                  public ModelOperation previous;
                  //~ Constructors -------------------------------------------------------------------------------
                  /**
                   * Creates a new ModelOperation object.
                   *
                   * @param  operationType  DOCUMENT ME!
                   * @param  who            DOCUMENT ME!
                   * @param  why            DOCUMENT ME!
                   */
                  public ModelOperation(ModelOperationType operationType, String who, String why)
                  {
                      super();
                      this.operationType = operationType;
                      this.who           = who;
                      this.why           = why;
                      push();
                  }
                  //~ Methods ------------------------------------------------------------------------------------
                  /**
                   * DOCUMENT ME!
                   */
                  public void push()
                  {
                      previous = currentOperation.get();
                      currentOperation.set(this);
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public ModelOperation pop()
                  {
                      currentOperation.set(previous);
                      previous = null;
                      return currentOperation.get();
                  }
                  /**
                   * DOCUMENT ME!
                   */
                  public static void popAll()
                  {
                      while (currentOperation.get() != null)
                      {
                          currentOperation.get().pop();
                      }
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public static ModelOperation current()
                  {
                      return currentOperation.get();
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public ModelOperationType getOperationType()
                  {
                      return this.operationType;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public String getWho()
                  {
                      return who;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  who  DOCUMENT ME!
                   */
                  public void setWho(String who)
                  {
                      this.who = who;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @return  DOCUMENT ME!
                   */
                  public String getWhy()
                  {
                      return why;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  why  DOCUMENT ME!
                   */
                  public void setWhy(String why)
                  {
                      this.why = why;
                  }
                  /**
                   * DOCUMENT ME!
                   *
                   * @param  operationType  DOCUMENT ME!
                   */
                  public void setOperationType(ModelOperationType operationType)
                  {
                      this.operationType = operationType;
                  }
              } // end class ModelOperation

              • 4. Re: Putting extra data in revinfo
                florin13

                Thanks guys, in the end I went with ThreadLocal. Seems like a simple enough solution for my needs.

                • 5. Re: Putting extra data in revinfo
                  adamw

                  Did you try registering a transaction synchronisation?

                   

                  Adam

                  • 6. Re: Putting extra data in revinfo
                    obastard

                    Didn't know such a thing existed. Googling. Ah, I see. I can register a class to get called after the transaction completes. Cool. Thanks for the tip. It's part of JTA not EJB.

                     

                    Though currently, I've replaced all my Transaction.REQUIRED stuff with an interceptor solution instead, so that I could also implement retrying optimistic lock failures.