public static ReteooStatefulSession readSession(
      ProtobufMessages.KnowledgeSession _session,
      ReteooStatefulSession session,
      DefaultAgenda agenda,
      MarshallerReaderContext context)
      throws IOException, ClassNotFoundException {
    GlobalResolver globalResolver = (GlobalResolver) context.env.get(EnvironmentName.GLOBALS);
    if (globalResolver != null) {
      session.setGlobalResolver(globalResolver);
    }

    if (session.getTimerService() instanceof PseudoClockScheduler) {
      PseudoClockScheduler clock = (PseudoClockScheduler) session.getTimerService();
      clock.advanceTime(_session.getTime(), TimeUnit.MILLISECONDS);
    }

    // RuleFlowGroups need to reference the session
    for (RuleFlowGroup group : agenda.getRuleFlowGroupsMap().values()) {
      ((RuleFlowGroupImpl) group).setWorkingMemory(session);
    }

    context.wm = session;

    // need to read node memories before reading the fact handles
    // because this data is required during fact propagation
    readNodeMemories(context, _session.getRuleData());

    List<PropagationContextImpl> pctxs = new ArrayList<PropagationContextImpl>();

    readInitialFactHandle(context, _session.getRuleData(), pctxs);

    for (ProtobufMessages.EntryPoint _ep : _session.getRuleData().getEntryPointList()) {
      SessionEntryPoint wmep = context.wm.getEntryPoints().get(_ep.getEntryPointId());
      readFactHandles(context, _ep, ((NamedEntryPoint) wmep).getObjectStore(), pctxs);
      readTruthMaintenanceSystem(context, wmep, _ep, pctxs);
    }

    cleanReaderContexts(pctxs);

    readActionQueue(context, _session.getRuleData());

    if (processMarshaller != null) {
      if (_session.hasProcessData()) {
        context.parameterObject = _session.getProcessData();
        processMarshaller.readProcessInstances(context);

        context.parameterObject = _session.getProcessData();
        processMarshaller.readWorkItems(context);

        // This actually does ALL timers, due to backwards compatability issues
        // It will read in old JBPM binaries, but always write to the new binary format.
        context.parameterObject = _session.getProcessData();
        processMarshaller.readProcessTimers(context);
      }
    } else {
      if (_session.hasProcessData()) {
        throw new IllegalStateException(
            "No process marshaller, unable to unmarshall process data.");
      }
    }

    if (_session.hasTimers()) {
      for (ProtobufMessages.Timers.Timer _timer : _session.getTimers().getTimerList()) {
        readTimer(context, _timer);
      }
    }

    // remove the activations filter
    agenda.setActivationsFilter(null);

    return session;
  }
  public static ReteooStatefulSession readSession(
      ReteooStatefulSession session,
      DefaultAgenda agenda,
      long time,
      boolean multithread,
      MarshallerReaderContext context)
      throws IOException, ClassNotFoundException {
    if (session.getTimerService() instanceof PseudoClockScheduler) {
      PseudoClockScheduler clock = (PseudoClockScheduler) session.getTimerService();
      clock.advanceTime(time, TimeUnit.MILLISECONDS);
    }

    // RuleFlowGroups need to reference the session
    for (RuleFlowGroup group : agenda.getRuleFlowGroupsMap().values()) {
      ((RuleFlowGroupImpl) group).setWorkingMemory(session);
    }

    context.wm = session;

    context.handles.put(
        context.wm.getInitialFactHandle().getId(), context.wm.getInitialFactHandle());

    if (context.stream.readBoolean()) {
      InternalFactHandle initialFactHandle = context.wm.getInitialFactHandle();
      int sinkId = context.stream.readInt();
      ObjectTypeNode initialFactNode = (ObjectTypeNode) context.sinks.get(sinkId);
      if (initialFactNode == null) {
        // ------ START RANT ------
        // The following code is as bad as it looks, but since I was so far
        // unable to convince Mark that creating OTNs on demand is really bad,
        // I have to continue doing it :)
        EntryPointNode defaultEPNode =
            context.ruleBase.getRete().getEntryPointNode(EntryPoint.DEFAULT);
        BuildContext buildContext =
            new BuildContext(
                context.ruleBase, context.ruleBase.getReteooBuilder().getIdGenerator());
        buildContext.setPartitionId(RuleBasePartitionId.MAIN_PARTITION);
        buildContext.setObjectTypeNodeMemoryEnabled(true);
        initialFactNode =
            new ObjectTypeNode(
                sinkId, defaultEPNode, ClassObjectType.InitialFact_ObjectType, buildContext);
        // isn't contention something everybody loves?
        context.ruleBase.lock();
        try {
          // Yeah, I know, because one session is being deserialized, we go and lock all of them...
          initialFactNode.attach(buildContext);
        } finally {
          context.ruleBase.unlock();
        }
        // ------- END RANT -----
      }
      ObjectHashSet initialFactMemory = (ObjectHashSet) context.wm.getNodeMemory(initialFactNode);

      initialFactMemory.add(initialFactHandle);
      readRightTuples(initialFactHandle, context);
    }
    while (context.readShort() == PersisterEnums.ENTRY_POINT) {
      String entryPointId = context.stream.readUTF();
      WorkingMemoryEntryPoint wmep = context.wm.getEntryPoints().get(entryPointId);
      readFactHandles(context, ((NamedEntryPoint) wmep).getObjectStore());
    }
    InternalFactHandle handle = context.wm.getInitialFactHandle();
    while (context.stream.readShort() == PersisterEnums.LEFT_TUPLE) {
      LeftTupleSink sink = (LeftTupleSink) context.sinks.get(context.stream.readInt());
      LeftTuple leftTuple = sink.createLeftTuple(handle, sink, true);
      readLeftTuple(leftTuple, context);
    }

    readPropagationContexts(context);

    readActivations(context);

    readActionQueue(context);

    readTruthMaintenanceSystem(context);

    if (processMarshaller != null) {
      processMarshaller.readProcessInstances(context);
    } else {
      short type = context.stream.readShort();
      if (PersisterEnums.END != type) {
        throw new IllegalStateException(
            "No process marshaller, unable to unmarshall type: " + type);
      }
    }

    if (processMarshaller != null) {
      processMarshaller.readWorkItems(context);
    } else {
      short type = context.stream.readShort();
      if (PersisterEnums.END != type) {
        throw new IllegalStateException(
            "No process marshaller, unable to unmarshall type: " + type);
      }
    }

    if (processMarshaller != null) {
      // This actually does ALL timers, due to backwards compatability issues
      // It will read in old JBPM binaries, but always write to the new binary format.
      processMarshaller.readProcessTimers(context);
    } else {
      short type = context.stream.readShort();
      if (PersisterEnums.END != type) {
        throw new IllegalStateException(
            "No process marshaller, unable to unmarshall type: " + type);
      }
    }

    // no legacy jBPM timers, so handle locally
    while (context.readShort() == PersisterEnums.DEFAULT_TIMER) {
      InputMarshaller.readTimer(context);
    }

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

    return session;
  }