docs: @SuppressWarnings({ "rawtypes", "unchecked" })
      @FFDCIgnore(Exception.class) //manually logged
      private boolean handlePropertyHits(BackedSession session) {
          final boolean trace = TraceComponent.isAnyTracingEnabled();
  
          Thread t = Thread.currentThread();
  
          String id = session.getId();
  
          try {
              Set<String> propsToWrite = null;
  
              // we are not synchronized here - were not in old code either
              Hashtable tht = null;
              if (_smc.writeAllProperties()) {
                  Map<?, ?> ht = session.getSwappableData();
                  propsToWrite = (Set<String>) ht.keySet();
                  if (trace && tc.isDebugEnabled()) {
                      Tr.debug(this, tc, "doing app changes for ALL mSwappable Data", ht);
                  }
              } else {
                  if (session.appDataChanges != null) {
                      if (appDataTablesPerThread) {
                          if ((tht = (Hashtable) session.appDataChanges.get(t)) != null) {
                              if (trace && tc.isDebugEnabled()) {
                                  Tr.debug(this, tc, "doing app changes for " + id + " on thread " + t);
                              }
                              propsToWrite = tht.keySet();
                          }
                      } else { // appDataTablesPerSession
                          propsToWrite = session.appDataChanges.keySet();
                          if (trace && tc.isDebugEnabled()) {
                              Tr.debug(this, tc, "doing app changes for TimeBasedWrite");
                          }
                      }
                  }
              }
  
              if (propsToWrite != null) {
                  for (String propid : propsToWrite) {
                      long startTime = System.nanoTime();
  
                      if (id.equals(propid)) {
                          throw new IllegalArgumentException(propid); // internal error, should never occur
                      }
  
                      String key = createSessionAttributeKey(id, propid);
                      Object value = session.getSwappableData().get(propid);
  
                      if (value == null) {
                          // Value removed by another thread
                          if (trace && tc.isDebugEnabled())
                              Tr.debug(this, tc, "ignoring " + propid + " because it is no longer found");
                      } else {
                          byte[] objbuf = serialize(value);
  
                          if (trace && tc.isDebugEnabled()) {
                              @SuppressWarnings("static-access")
                              boolean hideValues = _smc.isHideSessionValues();
                              if (hideValues)
                                  tcInvoke(tcSessionAttrCache, "put", key, "byte[" + objbuf.length + "]");
                              else
                                  tcInvoke(tcSessionAttrCache, "put", key, objbuf, value);
                          }
  
                          sessionAttributeCache.put(key, objbuf);
  
                          if (trace && tc.isDebugEnabled())
                              tcReturn(tcSessionAttrCache, "put");
  
                          SessionStatistics pmiStats = _iStore.getSessionStatistics();
                          if (pmiStats != null) {
                              pmiStats.writeTimes(objbuf.length, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
                          }
                      }
                  }
  
                  if (trace && tc.isDebugEnabled())
                      Tr.debug(this, tc, propsToWrite.size() + " property writes are done");
  
                  if (appDataTablesPerThread) {
                      if (session.appDataChanges != null)
                          session.appDataChanges.remove(t);
                      if (trace && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "remove thread from appDataChanges for thread", t);
                      }
                  } else { //appDataTablesPerSession
                      if (session.appDataChanges != null)
                          session.appDataChanges.clear();
                      if (trace && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "clearing appDataChanges");
                      }
                  }
              }
  
              // see if any properties were REMOVED.
              // if so, process them
  
              Set<String> propsToRemove = null;
  
              if (session.appDataRemovals != null) {
                  if (!appDataTablesPerThread) { // appDataTablesPerSession
                      propsToRemove = session.appDataRemovals.keySet();
                      if (trace && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "doing app removals for " + id + " on ALL threads");
                      }
                  } else { //appDataTablesPerThread
                      if ((tht = (Hashtable) session.appDataRemovals.get(t)) != null) {
                          if (trace && tc.isDebugEnabled()) {
                              Tr.debug(this, tc, "doing app removals for " + id + " on thread ", t);
                          }
                          propsToRemove = tht.keySet();
                      }
                  }
  
                  if (propsToRemove != null && !propsToRemove.isEmpty()) {
                      for (String propid : propsToRemove) {
                          if (trace && tc.isDebugEnabled()) {
                              Tr.debug(this, tc, "deleting prop " + propid + " for session " + id);
                          }
                          String key = createSessionAttributeKey(id, propid);
  
                          if (trace && tc.isDebugEnabled())
                              tcInvoke(tcSessionAttrCache, "remove", key);
  
                          boolean removed = sessionAttributeCache.remove(key);
  
                          if (trace && tc.isDebugEnabled())
                              tcReturn(tcSessionAttrCache, "remove", removed);
                      }
                  }
  
                  if (!appDataTablesPerThread) { // appDataTablesPerSession
                      if (session.appDataRemovals != null)
                          session.appDataRemovals.clear();
                      if (trace && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "clearing appDataRemovals");
                      }
                  } else { //appDataTablesPerThread
                      if (session.appDataRemovals != null)
                          session.appDataRemovals.remove(t);
                      if (trace && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "remove thread from appDataRemovals", t);
                      }
                  }
              }
  
              // Update the session's main cache entry per the identified changes
              if (propsToWrite != null || propsToRemove != null) {
                  ArrayList<?> oldValue, newValue;
                  long backoff = 20; // allows first two attempts without delay, then a delay of 160-319ms, then a delay of 320-639 ms, ...
                  for (boolean replaced = false; !replaced; ) {
                      if (backoff > 500 || (backoff *= 2) > 100)
                          try {
                              TimeUnit.MILLISECONDS.sleep(backoff + (long) Math.random() * backoff);
                          } catch (InterruptedException x) {
                              FFDCFilter.processException(x, getClass().getName(), "324", new Object[] { id, backoff, propsToWrite, propsToRemove });
                              throw x;
                          }
                      if (trace && tc.isDebugEnabled())
                          tcInvoke(tcSessionMetaCache, "get", id);
  
                      oldValue = sessionMetaCache.get(id);
  
                      if (trace && tc.isDebugEnabled())
                          tcReturn(tcSessionMetaCache, "get", oldValue);
                      if (oldValue == null) 
                          break; // no need to delete corresponding entries from attributes cache. The code that deleted the session meta info will do so.
                      SessionInfo sessionInfo = new SessionInfo(oldValue).clone();
                      if (propsToWrite != null)
                          sessionInfo.addSessionPropertyIds(propsToWrite);
                      if (propsToRemove != null)
                          sessionInfo.removeSessionPropertyIds(propsToRemove);
                      newValue = sessionInfo.getArrayList();
  
                      if (trace && tc.isDebugEnabled())
                          tcInvoke(tcSessionMetaCache, "replace", id, oldValue, newValue);
  
                      replaced = sessionMetaCache.replace(id, oldValue, newValue);
  
                      if (trace && tc.isDebugEnabled())
                          tcReturn(tcSessionMetaCache, "replace", replaced);
                  }
              }
          } catch (Exception ex) {
              FFDCFilter.processException(ex, "com.ibm.ws.session.store.cache.CacheHashMap.handlePropertyHits", "656", this, new Object[] { session });
              Tr.error(tc, "PROP_HIT_ERROR", ex);
              throw new RuntimeException(Tr.formatMessage(tc, "INTERNAL_SERVER_ERROR"));
          }
  
          return true;
      }