public static void readFactHandles(
      MarshallerReaderContext context,
      org.drools.core.marshalling.impl.ProtobufMessages.EntryPoint _ep,
      ObjectStore objectStore,
      List<PropagationContextImpl> pctxs)
      throws IOException, ClassNotFoundException {
    InternalWorkingMemory wm = context.wm;

    SessionEntryPoint entryPoint = context.wm.getEntryPoints().get(_ep.getEntryPointId());
    // load the handles
    for (ProtobufMessages.FactHandle _handle : _ep.getHandleList()) {
      InternalFactHandle handle = readFactHandle(context, entryPoint, _handle);

      context.handles.put(handle.getId(), handle);

      if (!_handle.getIsJustified()) {
        // BeliefSystem handles the Object type
        if (handle.getObject() != null) {
          objectStore.addHandle(handle, handle.getObject());
        }

        // add handle to object type node
        assertHandleIntoOTN(context, wm, handle, pctxs);
      }
    }
  }
  private static ProtobufMessages.NodeMemory writeRIANodeMemory(
      final int nodeId, final Memory memory) {
    // for RIA nodes, we need to store the ID of the created handles
    RIAMemory mem = (RIAMemory) memory;
    if (!mem.memory.isEmpty()) {
      ProtobufMessages.NodeMemory.RIANodeMemory.Builder _ria =
          ProtobufMessages.NodeMemory.RIANodeMemory.newBuilder();

      final org.drools.core.util.Iterator it = mem.memory.iterator();
      // iterates over all propagated handles and assert them to the new sink
      for (ObjectEntry entry = (ObjectEntry) it.next();
          entry != null;
          entry = (ObjectEntry) it.next()) {
        LeftTuple leftTuple = (LeftTuple) entry.getKey();
        InternalFactHandle handle = (InternalFactHandle) entry.getValue();
        FactHandle _handle =
            ProtobufMessages.FactHandle.newBuilder()
                .setId(handle.getId())
                .setRecency(handle.getRecency())
                .build();
        _ria.addContext(
            ProtobufMessages.NodeMemory.RIANodeMemory.RIAContext.newBuilder()
                .setTuple(PersisterHelper.createTuple(leftTuple))
                .setResultHandle(_handle)
                .build());
      }

      return ProtobufMessages.NodeMemory.newBuilder()
          .setNodeId(nodeId)
          .setNodeType(ProtobufMessages.NodeMemory.NodeType.RIA)
          .setRia(_ria.build())
          .build();
    }
    return null;
  }
  private static ProtobufMessages.NodeMemory writeQueryElementNodeMemory(
      final int nodeId, final Memory memory, final InternalWorkingMemory wm) {
    LeftTupleIterator it = LeftTupleIterator.iterator(wm, ((QueryElementNodeMemory) memory).node);

    ProtobufMessages.NodeMemory.QueryElementNodeMemory.Builder _query =
        ProtobufMessages.NodeMemory.QueryElementNodeMemory.newBuilder();
    for (LeftTuple leftTuple = (LeftTuple) it.next();
        leftTuple != null;
        leftTuple = (LeftTuple) it.next()) {
      InternalFactHandle handle = (InternalFactHandle) leftTuple.getObject();
      FactHandle _handle =
          ProtobufMessages.FactHandle.newBuilder()
              .setId(handle.getId())
              .setRecency(handle.getRecency())
              .build();

      ProtobufMessages.NodeMemory.QueryElementNodeMemory.QueryContext.Builder _context =
          ProtobufMessages.NodeMemory.QueryElementNodeMemory.QueryContext.newBuilder()
              .setTuple(PersisterHelper.createTuple(leftTuple))
              .setHandle(_handle);

      LeftTuple childLeftTuple = leftTuple.getFirstChild();
      while (childLeftTuple != null) {
        RightTuple rightParent = childLeftTuple.getRightParent();
        _context.addResult(
            ProtobufMessages.FactHandle.newBuilder()
                .setId(rightParent.getFactHandle().getId())
                .setRecency(rightParent.getFactHandle().getRecency())
                .build());
        while (childLeftTuple != null && childLeftTuple.getRightParent() == rightParent) {
          // skip to the next child that has a different right parent
          childLeftTuple = childLeftTuple.getLeftParentNext();
        }
      }
      _query.addContext(_context.build());
    }

    return _query.getContextCount() > 0
        ? ProtobufMessages.NodeMemory.newBuilder()
            .setNodeId(nodeId)
            .setNodeType(ProtobufMessages.NodeMemory.NodeType.QUERY_ELEMENT)
            .setQueryElement(_query.build())
            .build()
        : null;
  }
  private static ProtobufMessages.NodeMemory writeRIANodeMemory(
      final int nodeId,
      final MarshallerWriteContext context,
      final BaseNode node,
      final NodeMemories memories,
      final Memory memory) {
    RightInputAdapterNode riaNode = (RightInputAdapterNode) node;

    ObjectSink[] sinks = riaNode.getSinkPropagator().getSinks();
    BetaNode betaNode = (BetaNode) sinks[0];

    Memory betaMemory = memories.peekNodeMemory(betaNode.getId());
    if (betaMemory == null) {
      return null;
    }
    BetaMemory bm;
    if (betaNode.getType() == NodeTypeEnums.AccumulateNode) {
      bm = ((AccumulateMemory) betaMemory).getBetaMemory();
    } else {
      bm = (BetaMemory) betaMemory;
    }

    // for RIA nodes, we need to store the ID of the created handles
    bm.getRightTupleMemory().iterator();
    if (bm.getRightTupleMemory().size() > 0) {
      ProtobufMessages.NodeMemory.RIANodeMemory.Builder _ria =
          ProtobufMessages.NodeMemory.RIANodeMemory.newBuilder();
      final org.drools.core.util.Iterator it = bm.getRightTupleMemory().iterator();

      // iterates over all propagated handles and assert them to the new sink
      for (RightTuple entry = (RightTuple) it.next();
          entry != null;
          entry = (RightTuple) it.next()) {
        LeftTuple leftTuple = (LeftTuple) entry.getFactHandle().getObject();
        InternalFactHandle handle = (InternalFactHandle) leftTuple.getObject();
        FactHandle _handle =
            ProtobufMessages.FactHandle.newBuilder()
                .setId(handle.getId())
                .setRecency(handle.getRecency())
                .build();
        _ria.addContext(
            ProtobufMessages.NodeMemory.RIANodeMemory.RIAContext.newBuilder()
                .setTuple(PersisterHelper.createTuple(leftTuple))
                .setResultHandle(_handle)
                .build());
      }

      return ProtobufMessages.NodeMemory.newBuilder()
          .setNodeId(nodeId)
          .setNodeType(ProtobufMessages.NodeMemory.NodeType.RIA)
          .setRia(_ria.build())
          .build();
    }
    return null;
  }
  private static ProtobufMessages.FactHandle writeFactHandle(
      MarshallerWriteContext context,
      ObjectMarshallingStrategyStore objectMarshallingStrategyStore,
      InternalFactHandle handle)
      throws IOException {
    ProtobufMessages.FactHandle.Builder _handle = ProtobufMessages.FactHandle.newBuilder();

    _handle.setType(getHandleType(handle));
    _handle.setId(handle.getId());
    _handle.setRecency(handle.getRecency());

    if (_handle.getType() == ProtobufMessages.FactHandle.HandleType.EVENT) {
      // is event
      EventFactHandle efh = (EventFactHandle) handle;
      _handle.setTimestamp(efh.getStartTimestamp());
      _handle.setDuration(efh.getDuration());
      _handle.setIsExpired(efh.isExpired());
      _handle.setActivationsCount(efh.getActivationsCount());
    }

    if (handle.getEqualityKey() != null
        && handle.getEqualityKey().getStatus() == EqualityKey.JUSTIFIED) {
      _handle.setIsJustified(true);
    } else {
      _handle.setIsJustified(false);
    }

    Object object = handle.getObject();

    if (object != null) {
      ObjectMarshallingStrategy strategy = objectMarshallingStrategyStore.getStrategyObject(object);

      Integer index = context.getStrategyIndex(strategy);
      _handle.setStrategyIndex(index.intValue());
      _handle.setObject(
          ByteString.copyFrom(
              strategy.marshal(context.strategyContext.get(strategy), context, object)));
    }

    return _handle.build();
  }
  private static ProtobufMessages.FactHandle writeFactHandle(
      MarshallerWriteContext context,
      ObjectMarshallingStrategyStore objectMarshallingStrategyStore,
      InternalFactHandle handle)
      throws IOException {
    ProtobufMessages.FactHandle.Builder _handle = ProtobufMessages.FactHandle.newBuilder();

    _handle.setType(getHandleType(handle));
    _handle.setId(handle.getId());
    _handle.setRecency(handle.getRecency());

    if (_handle.getType() == ProtobufMessages.FactHandle.HandleType.EVENT) {
      // is event
      EventFactHandle efh = (EventFactHandle) handle;
      _handle.setTimestamp(efh.getStartTimestamp());
      _handle.setDuration(efh.getDuration());
      _handle.setIsExpired(efh.isExpired());
      _handle.setActivationsCount(efh.getActivationsCount());
    }

    Object object = handle.getObject();

    if (object != null) {
      ObjectMarshallingStrategy strategy = objectMarshallingStrategyStore.getStrategyObject(object);

      String strategyClassName = strategy.getClass().getName();
      Integer index = context.usedStrategies.get(strategyClassName);
      if (index == null) {
        index = Integer.valueOf(context.usedStrategies.size());
        context.usedStrategies.put(strategyClassName, index);
      }
      _handle.setStrategyIndex(index.intValue());
      _handle.setObject(ByteString.copyFrom(strategy.marshal(context, object)));
    }

    return _handle.build();
  }
  @SuppressWarnings("unchecked")
  private static ProtobufMessages.NodeMemory writeFromNodeMemory(
      final int nodeId, final Memory memory) {
    FromMemory fromMemory = (FromMemory) memory;

    if (fromMemory.betaMemory.getLeftTupleMemory().size() > 0) {
      ProtobufMessages.NodeMemory.FromNodeMemory.Builder _from =
          ProtobufMessages.NodeMemory.FromNodeMemory.newBuilder();

      final org.drools.core.util.Iterator tupleIter =
          fromMemory.betaMemory.getLeftTupleMemory().iterator();
      for (LeftTuple leftTuple = (LeftTuple) tupleIter.next();
          leftTuple != null;
          leftTuple = (LeftTuple) tupleIter.next()) {
        Map<Object, RightTuple> matches = (Map<Object, RightTuple>) leftTuple.getObject();
        ProtobufMessages.NodeMemory.FromNodeMemory.FromContext.Builder _context =
            ProtobufMessages.NodeMemory.FromNodeMemory.FromContext.newBuilder()
                .setTuple(PersisterHelper.createTuple(leftTuple));
        for (RightTuple rightTuple : matches.values()) {
          FactHandle _handle =
              ProtobufMessages.FactHandle.newBuilder()
                  .setId(rightTuple.getFactHandle().getId())
                  .setRecency(rightTuple.getFactHandle().getRecency())
                  .build();
          _context.addHandle(_handle);
        }
        _from.addContext(_context.build());
      }

      return ProtobufMessages.NodeMemory.newBuilder()
          .setNodeId(nodeId)
          .setNodeType(ProtobufMessages.NodeMemory.NodeType.FROM)
          .setFrom(_from.build())
          .build();
    }
    return null;
  }
  private static ProtobufMessages.NodeMemory writeAccumulateNodeMemory(
      final int nodeId, final Memory memory) {
    // for accumulate nodes, we need to store the ID of created (result) handles
    AccumulateMemory accmem = (AccumulateMemory) memory;
    if (accmem.betaMemory.getLeftTupleMemory().size() > 0) {
      ProtobufMessages.NodeMemory.AccumulateNodeMemory.Builder _accumulate =
          ProtobufMessages.NodeMemory.AccumulateNodeMemory.newBuilder();

      final org.drools.core.util.Iterator tupleIter =
          accmem.betaMemory.getLeftTupleMemory().iterator();
      for (LeftTuple leftTuple = (LeftTuple) tupleIter.next();
          leftTuple != null;
          leftTuple = (LeftTuple) tupleIter.next()) {
        AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
        if (accctx.result != null) {
          FactHandle _handle =
              ProtobufMessages.FactHandle.newBuilder()
                  .setId(accctx.result.getFactHandle().getId())
                  .setRecency(accctx.result.getFactHandle().getRecency())
                  .build();
          _accumulate.addContext(
              ProtobufMessages.NodeMemory.AccumulateNodeMemory.AccumulateContext.newBuilder()
                  .setTuple(PersisterHelper.createTuple(leftTuple))
                  .setResultHandle(_handle)
                  .build());
        }
      }

      return ProtobufMessages.NodeMemory.newBuilder()
          .setNodeId(nodeId)
          .setNodeType(ProtobufMessages.NodeMemory.NodeType.ACCUMULATE)
          .setAccumulate(_accumulate.build())
          .build();
    }
    return null;
  }
  private static ProtobufMessages.KnowledgeSession serializeSession(MarshallerWriteContext context)
      throws IOException {
    ReteooWorkingMemory wm = (ReteooWorkingMemory) context.wm;
    wm.getAgenda().unstageActivations();

    ProtobufMessages.RuleData.Builder _ruleData = ProtobufMessages.RuleData.newBuilder();

    final boolean multithread = wm.isPartitionManagersActive();
    if (multithread) {
      wm.stopPartitionManagers();
    }

    long time = 0;
    if (context.wm.getTimerService() instanceof PseudoClockScheduler) {
      time = context.clockTime;
    }
    _ruleData.setLastId(wm.getFactHandleFactory().getId());
    _ruleData.setLastRecency(wm.getFactHandleFactory().getRecency());

    InternalFactHandle handle = context.wm.getInitialFactHandle();
    ProtobufMessages.FactHandle _ifh =
        ProtobufMessages.FactHandle.newBuilder()
            .setType(ProtobufMessages.FactHandle.HandleType.INITIAL_FACT)
            .setId(handle.getId())
            .setRecency(handle.getRecency())
            .build();
    _ruleData.setInitialFact(_ifh);

    writeAgenda(context, _ruleData);

    writeNodeMemories(context, _ruleData);

    for (WorkingMemoryEntryPoint wmep : wm.getEntryPoints().values()) {
      org.drools.marshalling.impl.ProtobufMessages.EntryPoint.Builder _epb =
          ProtobufMessages.EntryPoint.newBuilder();
      _epb.setEntryPointId(wmep.getEntryPointId());
      writeFactHandles(context, _epb, ((NamedEntryPoint) wmep).getObjectStore());
      _ruleData.addEntryPoint(_epb.build());
    }

    writeActionQueue(context, _ruleData);

    writeTruthMaintenanceSystem(context, _ruleData);

    ProtobufMessages.KnowledgeSession.Builder _session =
        ProtobufMessages.KnowledgeSession.newBuilder()
            .setMultithread(multithread)
            .setTime(time)
            .setRuleData(_ruleData.build());

    if (processMarshaller != null) {
      Builder _pdata = ProtobufMessages.ProcessData.newBuilder();
      if (context.marshalProcessInstances) {
        context.parameterObject = _pdata;
        processMarshaller.writeProcessInstances(context);
      }

      if (context.marshalWorkItems) {
        context.parameterObject = _pdata;
        processMarshaller.writeWorkItems(context);
      }

      // this now just assigns the writer, it will not write out any timer information
      context.parameterObject = _pdata;
      processMarshaller.writeProcessTimers(context);

      _session.setProcessData(_pdata.build());
    }

    Timers _timers = writeTimers(context.wm.getTimerService().getTimerJobInstances(), context);
    if (_timers != null) {
      _session.setTimers(_timers);
    }

    if (multithread) {
      wm.startPartitionManagers();
    }

    return _session.build();
  }
  private static ProtobufMessages.KnowledgeSession serializeSession(MarshallerWriteContext context)
      throws IOException {
    StatefulKnowledgeSessionImpl wm = (StatefulKnowledgeSessionImpl) context.wm;

    try {
      wm.getLock().lock();
      for (WorkingMemoryEntryPoint ep : wm.getWorkingMemoryEntryPoints().values()) {
        if (ep instanceof NamedEntryPoint) {
          ((NamedEntryPoint) ep).lock();
        }
      }

      ((InternalAgenda) wm.getAgenda()).unstageActivations();

      evaluateRuleActivations(wm);

      ProtobufMessages.RuleData.Builder _ruleData = ProtobufMessages.RuleData.newBuilder();

      long time = 0;
      if (context.wm.getTimerService() instanceof PseudoClockScheduler) {
        time = context.clockTime;
      }
      _ruleData.setLastId(wm.getFactHandleFactory().getId());
      _ruleData.setLastRecency(wm.getFactHandleFactory().getRecency());

      InternalFactHandle handle = context.wm.getInitialFactHandle();
      if (handle != null) {
        // can be null for RETE, if fireAllRules has not yet been called
        ProtobufMessages.FactHandle _ifh =
            ProtobufMessages.FactHandle.newBuilder()
                .setType(ProtobufMessages.FactHandle.HandleType.INITIAL_FACT)
                .setId(handle.getId())
                .setRecency(handle.getRecency())
                .build();
        _ruleData.setInitialFact(_ifh);
      }

      writeAgenda(context, _ruleData);

      writeNodeMemories(context, _ruleData);

      for (EntryPoint wmep : wm.getWorkingMemoryEntryPoints().values()) {
        org.drools.core.marshalling.impl.ProtobufMessages.EntryPoint.Builder _epb =
            ProtobufMessages.EntryPoint.newBuilder();
        _epb.setEntryPointId(wmep.getEntryPointId());

        writeObjectTypeConfiguration(
            context,
            ((InternalWorkingMemoryEntryPoint) wmep).getObjectTypeConfigurationRegistry(),
            _epb);

        writeFactHandles(context, _epb, ((NamedEntryPoint) wmep).getObjectStore());

        writeTruthMaintenanceSystem(context, wmep, _epb);

        _ruleData.addEntryPoint(_epb.build());
      }

      writeActionQueue(context, _ruleData);

      ProtobufMessages.KnowledgeSession.Builder _session =
          ProtobufMessages.KnowledgeSession.newBuilder()
              .setMultithread(false)
              .setTime(time)
              .setRuleData(_ruleData.build());

      if (processMarshaller != null) {
        Builder _pdata = ProtobufMessages.ProcessData.newBuilder();
        if (context.marshalProcessInstances) {
          context.parameterObject = _pdata;
          processMarshaller.writeProcessInstances(context);
        }

        if (context.marshalWorkItems) {
          context.parameterObject = _pdata;
          processMarshaller.writeWorkItems(context);
        }

        // this now just assigns the writer, it will not write out any timer information
        context.parameterObject = _pdata;
        processMarshaller.writeProcessTimers(context);

        _session.setProcessData(_pdata.build());
      }

      Timers _timers =
          writeTimers(
              context.wm.getTimerService().getTimerJobInstances(context.wm.getId()), context);
      if (_timers != null) {
        _session.setTimers(_timers);
      }

      return _session.build();
    } finally {
      for (WorkingMemoryEntryPoint ep : wm.getWorkingMemoryEntryPoints().values()) {
        if (ep instanceof NamedEntryPoint) {
          ((NamedEntryPoint) ep).unlock();
        }
      }
      wm.getLock().unlock();
    }
  }