public static void writeAgenda(MarshallerWriteContext context) throws IOException {
    InternalWorkingMemory wm = context.wm;
    DefaultAgenda agenda = (DefaultAgenda) wm.getAgenda();

    context.writeInt(agenda.getDormantActivations());
    context.writeInt(agenda.getActiveActivations());

    Map<String, ActivationGroup> activationGroups = agenda.getActivationGroupsMap();

    AgendaGroup[] agendaGroups =
        (AgendaGroup[])
            agenda
                .getAgendaGroupsMap()
                .values()
                .toArray(new AgendaGroup[agenda.getAgendaGroupsMap().size()]);
    Arrays.sort(agendaGroups, AgendaGroupSorter.instance);

    for (AgendaGroup group : agendaGroups) {
      context.writeShort(PersisterEnums.AGENDA_GROUP);
      context.writeUTF(group.getName());
      context.writeBoolean(group.isActive());
    }
    context.writeShort(PersisterEnums.END);

    LinkedList<AgendaGroup> focusStack = agenda.getStackList();
    for (Iterator<AgendaGroup> it = focusStack.iterator(); it.hasNext(); ) {
      AgendaGroup group = it.next();
      context.writeShort(PersisterEnums.AGENDA_GROUP);
      context.writeUTF(group.getName());
    }
    context.writeShort(PersisterEnums.END);

    RuleFlowGroupImpl[] ruleFlowGroups =
        (RuleFlowGroupImpl[])
            agenda
                .getRuleFlowGroupsMap()
                .values()
                .toArray(new RuleFlowGroupImpl[agenda.getRuleFlowGroupsMap().size()]);
    Arrays.sort(ruleFlowGroups, RuleFlowGroupSorter.instance);

    for (RuleFlowGroupImpl group : ruleFlowGroups) {
      context.writeShort(PersisterEnums.RULE_FLOW_GROUP);
      // group.write( context );
      context.writeUTF(group.getName());
      context.writeBoolean(group.isActive());
      context.writeBoolean(group.isAutoDeactivate());
      Map<Long, String> nodeInstances = group.getNodeInstances();
      context.writeInt(nodeInstances.size());
      for (Map.Entry<Long, String> entry : nodeInstances.entrySet()) {
        context.writeLong(entry.getKey());
        context.writeUTF(entry.getValue());
      }
    }
    context.writeShort(PersisterEnums.END);
  }
  public static void readAgenda(MarshallerReaderContext context, DefaultAgenda agenda)
      throws IOException {
    ObjectInputStream stream = context.stream;

    agenda.setDormantActivations(stream.readInt());
    agenda.setActiveActivations(stream.readInt());

    while (stream.readShort() == PersisterEnums.AGENDA_GROUP) {
      BinaryHeapQueueAgendaGroup group =
          new BinaryHeapQueueAgendaGroup(stream.readUTF(), context.ruleBase);
      group.setActive(stream.readBoolean());
      agenda.getAgendaGroupsMap().put(group.getName(), group);
    }

    while (stream.readShort() == PersisterEnums.AGENDA_GROUP) {
      String agendaGroupName = stream.readUTF();
      agenda.getStackList().add(agenda.getAgendaGroup(agendaGroupName));
    }

    while (stream.readShort() == PersisterEnums.RULE_FLOW_GROUP) {
      String rfgName = stream.readUTF();
      boolean active = stream.readBoolean();
      boolean autoDeactivate = stream.readBoolean();
      RuleFlowGroupImpl rfg = new RuleFlowGroupImpl(rfgName, active, autoDeactivate);
      agenda.getRuleFlowGroupsMap().put(rfgName, rfg);
      int nbNodeInstances = stream.readInt();
      for (int i = 0; i < nbNodeInstances; i++) {
        Long processInstanceId = stream.readLong();
        String nodeInstanceId = stream.readUTF();
        rfg.addNodeInstance(processInstanceId, nodeInstanceId);
      }
    }
  }
  private static void writeAgenda(
      MarshallerWriteContext context, ProtobufMessages.RuleData.Builder _ksb) throws IOException {
    InternalWorkingMemory wm = context.wm;
    DefaultAgenda agenda = (DefaultAgenda) wm.getAgenda();

    org.drools.marshalling.impl.ProtobufMessages.Agenda.Builder _ab =
        ProtobufMessages.Agenda.newBuilder();

    AgendaGroup[] agendaGroups =
        (AgendaGroup[])
            agenda
                .getAgendaGroupsMap()
                .values()
                .toArray(new AgendaGroup[agenda.getAgendaGroupsMap().size()]);
    Arrays.sort(agendaGroups, AgendaGroupSorter.instance);
    for (AgendaGroup group : agendaGroups) {
      org.drools.marshalling.impl.ProtobufMessages.Agenda.AgendaGroup.Builder _agb =
          ProtobufMessages.Agenda.AgendaGroup.newBuilder();
      _agb.setName(group.getName());
      _agb.setIsActive(group.isActive());
      _ab.addAgendaGroup(_agb.build());
    }

    org.drools.marshalling.impl.ProtobufMessages.Agenda.FocusStack.Builder _fsb =
        ProtobufMessages.Agenda.FocusStack.newBuilder();
    LinkedList<AgendaGroup> focusStack = agenda.getStackList();
    for (Iterator<AgendaGroup> it = focusStack.iterator(); it.hasNext(); ) {
      AgendaGroup group = it.next();
      _fsb.addGroupName(group.getName());
    }
    _ab.setFocusStack(_fsb.build());

    RuleFlowGroupImpl[] ruleFlowGroups =
        (RuleFlowGroupImpl[])
            agenda
                .getRuleFlowGroupsMap()
                .values()
                .toArray(new RuleFlowGroupImpl[agenda.getRuleFlowGroupsMap().size()]);
    Arrays.sort(ruleFlowGroups, RuleFlowGroupSorter.instance);
    for (RuleFlowGroupImpl group : ruleFlowGroups) {
      org.drools.marshalling.impl.ProtobufMessages.Agenda.RuleFlowGroup.Builder _rfgb =
          ProtobufMessages.Agenda.RuleFlowGroup.newBuilder();
      _rfgb.setName(group.getName());
      _rfgb.setIsActive(group.isActive());
      _rfgb.setIsAutoDeactivate(group.isAutoDeactivate());

      Map<Long, String> nodeInstances = group.getNodeInstances();
      for (Map.Entry<Long, String> entry : nodeInstances.entrySet()) {
        org.drools.marshalling.impl.ProtobufMessages.Agenda.RuleFlowGroup.NodeInstance.Builder
            _nib = ProtobufMessages.Agenda.RuleFlowGroup.NodeInstance.newBuilder();
        _nib.setProcessInstanceId(entry.getKey());
        _nib.setNodeInstanceId(entry.getValue());
        _rfgb.addNodeInstance(_nib.build());
      }
      _ab.addRuleFlowGroup(_rfgb.build());
    }

    // serialize all dormant activations
    ActivationIterator it = ActivationIterator.iterator(wm);
    List<org.drools.spi.Activation> dormant = new ArrayList<org.drools.spi.Activation>();
    for (org.drools.spi.Activation item = (org.drools.spi.Activation) it.next();
        item != null;
        item = (org.drools.spi.Activation) it.next()) {
      if (!item.isActive()) {
        dormant.add(item);
      }
    }
    Collections.sort(dormant, ActivationsSorter.INSTANCE);
    for (org.drools.spi.Activation activation : dormant) {
      _ab.addActivation(writeActivation(context, (AgendaItem) activation));
    }

    _ksb.setAgenda(_ab.build());
  }
  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;
  }