Uploaded image for project: 'MyFaces Trinidad'
  1. MyFaces Trinidad
  2. TRINIDAD-1856

Extend Trinidad HA testing support to check whether modified Session and App Attributes have been dirtied

    XMLWordPrintableJSON

Details

    • New Feature
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 1.0.12-core, 2.0.0-beta-1
    • None
    • Archetype
    • None
    • All

    Description

      A common developer error when supporting high availability is to modify a Serializable Session or ServletContext(Application) attribute and forget to dirty the relevant attribute by resetting it in the Session or ServletContext. This is even easier to forget when the contents of the attribute are mutable. For example, a Session attribute that contains a Map. In this case, whenever the map is modified, the relevant Session attribute would need to be modified as well. We propose that we extend the current Trinidad high availability checking to handle this case as well.

      Trinidad currently supports the following system property for checking various high availability features:

      org.apache.myfaces.trinidad.CHECK_STATE_SERIALIZATION

      With the following case insensitive comma-separated options:PROPERTY, COMPONENT, TREE, SESSION, APPLICATION, NONE, ALL

      To support checking for mutated beans, we will add the following additional option:BEANS

      If the Beans property is specified and Session attribute serialization is being checked (either by the presence of ALL or SESSION), the Session attributes will be checked for mutation without dirtying. If Application attribute serialization is being checked (either by the presence of ALL or APPLICATION), the Application attributes will be checked for mutation without dirtying.

      New apis:

      on org.apache.myfaces.trinidad.bean.util.StateUtils:
      /**

      • Returns <code>true</code> if the attributes of the session and application Maps should be
      • checked for cases where the attribute was mutated but not dirtied for failover. If
      • <code>checkSessionSerialization</code> returns <code>true</code>, the contents of the
      • Session should be checked. If <code>checkApplicationSerialization</code> returns
      • <code>true</code>, the Serializable content of the Application should be checked.
      • @return true if the contents of scopes should be checked for mutation without dirtying.
      • @see #checkApplicationSerialization
      • @see #checkSessionSerialization
        */
        public static boolean checkManagedBeanMutation(ExternalContext extContext) { return _CHECK_MANAGED_BEAN_MUTATATION; }

      In order to catch mutations to non-HTTP servlet environments we need need to wrap the Session and Application Maps. (for HttpRequests, we wrpa the underlying ServletContext and Session objects in order to catch direct mutations). To do so, the following apis are added to

      org.apache.myfaces.trinidad.util.CollectionUtils

      /**

      • Interface for trapping mutations to a Map.
      • @param <K> the type of the keys of the Map that MapMutationHooks are associated with
      • @param <V> the type of the values of the Map that MapMutationHooks are associated with
      • @see #newMutationHookedMap
        */
        public interface MapMutationHooks<K, V> { /** * Called when the associated Map of the MapMutationHooks is written to * @param key key of entry that has changed * @param value value of entry that has changed */ public void writeNotify(K key, V value); /** * Called when an entry is removed from the associated Map of the MapMutationHooks * @param key key of entry that has been removed */ public void removeNotify(Object key); }

      /**

      • Creates a new Map that informs the MapMutationHooks of any direct mutations. Mutations to
      • the underlying Map will not be caught.
      • If the base map is Serializable, the returned Map will be Serializable
      • @param <K> type of the keys of the Map
      • @param <V> type of the values of the Map
      • @param map Underlying map to trap mutations of
      • @param hooks MapMutationHooks to inform of mutations to the returned Map
      • @return a new Map that traps the mutations to the underlying Map
      • @throws NullPointerException if map or hooks are null
        */
        public static <K,V> Map<K, V> newMutationHookedMap(Map<K, V> map, MapMutationHooks<K, V> hooks)

      How it works:

      We want to check that attributes that existed in the scope to be checked at the beginning of the request are either identical at the end of the request, or were dirtied. To do so, we hook the TrinidadImpl and at the beginning of the request, to check a scope like the Session:

      1) Iterate the attributes of the Session
      2) Get the value
      3) If the value is known to be immutable, do nothing
      4) If it is mutable and Serializable, serialize it and save the bytes

      We then wrap all of the means of mutating the scope in question and trap the mutations. When a mutation occurs, we remove our serialized entry

      At the end of the request, for each remaining entry, we fetch the current value from the scope, serialize it and compare the bytes. If the bytes are different we have a problem and we log a severe error (since it is too late to throw an exception)

      Debugging problems found by this mechanism is unpleasant, so to make things easier, we do the following:

      Deserialize both the old and new bytes back to objects for dumping into the message
      We call .equals() on the objects. If the objects implement a equals(), this can make finding the difference much easier.

      In general, good toString() and equals() implementations are quite handy. A general purpose object tree diffing tool would be extremely helpful, but outside the scope of this work

      Attachments

        1. jira1856Trunk.diff
          92 kB
          Blake Sullivan

        Activity

          People

            btsulliv Blake Sullivan
            btsulliv Blake Sullivan
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:

              Time Tracking

                Estimated:
                Original Estimate - 192h
                192h
                Remaining:
                Remaining Estimate - 192h
                192h
                Logged:
                Time Spent - Not Specified
                Not Specified