public static void writeWorkItem(MarshallerWriteContext context, WorkItem workItem)
      throws IOException {
    ObjectOutputStream stream = context.stream;
    stream.writeLong(workItem.getId());
    stream.writeLong(workItem.getProcessInstanceId());
    stream.writeUTF(workItem.getName());
    stream.writeInt(workItem.getState());

    // Work Item Parameters
    Map<String, Object> parameters = workItem.getParameters();
    Collection<Object> notNullValues = new ArrayList<Object>();
    for (Object value : parameters.values()) {
      if (value != null) {
        notNullValues.add(value);
      }
    }

    stream.writeInt(notNullValues.size());
    for (String key : parameters.keySet()) {
      Object object = parameters.get(key);
      if (object != null) {
        stream.writeUTF(key);

        ObjectMarshallingStrategy strategy =
            context.objectMarshallingStrategyStore.getStrategyObject(object);
        String strategyClassName = strategy.getClass().getName();
        stream.writeInt(-2); // backwards compatibility
        stream.writeUTF(strategyClassName);
        if (strategy.accept(object)) {
          strategy.write(stream, object);
        }
      }
    }
  }
  private static void writeFactHandle(
      MarshallerWriteContext context,
      ObjectOutputStream stream,
      ObjectMarshallingStrategyStore objectMarshallingStrategyStore,
      int type,
      InternalFactHandle handle)
      throws IOException {
    stream.writeInt(type);
    stream.writeInt(handle.getId());
    stream.writeLong(handle.getRecency());

    if (type == 2) {
      // is event
      EventFactHandle efh = (EventFactHandle) handle;
      stream.writeLong(efh.getStartTimestamp());
      stream.writeLong(efh.getDuration());
      stream.writeBoolean(efh.isExpired());
      stream.writeLong(efh.getActivationsCount());
    }

    // context.out.println( "Object : int:" + handle.getId() + " long:" + handle.getRecency() );
    // context.out.println( handle.getObject() );

    Object object = handle.getObject();

    // Old versions wrote -1 and tested >= 0 to see if there was a strategy available
    // Now, we write -2 to indicate that we write the strategy class name to the stream
    stream.writeInt(-2);
    if (object != null) {
      ObjectMarshallingStrategy strategy = objectMarshallingStrategyStore.getStrategyObject(object);

      String strategyClassName = strategy.getClass().getName();
      stream.writeUTF(strategyClassName);

      strategy.write(stream, object);
    } else {
      stream.writeUTF("");
    }

    if (handle.getEntryPoint() instanceof InternalWorkingMemoryEntryPoint) {
      String entryPoint =
          ((InternalWorkingMemoryEntryPoint) handle.getEntryPoint())
              .getEntryPoint()
              .getEntryPointId();
      if (entryPoint != null && !entryPoint.equals("")) {
        stream.writeBoolean(true);
        stream.writeUTF(entryPoint);
      } else {
        stream.writeBoolean(false);
      }
    } else {
      stream.writeBoolean(false);
    }
  }
  public static WorkItem readWorkItem(MarshallerReaderContext context) throws IOException {
    ObjectInputStream stream = context.stream;

    WorkItemImpl workItem = new WorkItemImpl();
    workItem.setId(stream.readLong());
    workItem.setProcessInstanceId(stream.readLong());
    workItem.setName(stream.readUTF());
    workItem.setState(stream.readInt());

    // WorkItem Paramaters
    int nbVariables = stream.readInt();
    if (nbVariables > 0) {

      for (int i = 0; i < nbVariables; i++) {
        String name = stream.readUTF();
        try {
          int index = stream.readInt();
          ObjectMarshallingStrategy strategy = null;
          // Old way of retrieving strategy objects
          if (index >= 0) {
            strategy = context.resolverStrategyFactory.getStrategy(index);
            if (strategy == null) {
              throw new IllegalStateException("No strategy of with index " + index + " available.");
            }
          }
          // New way
          else if (index == -2) {
            String strategyClassName = stream.readUTF();
            strategy = context.resolverStrategyFactory.getStrategyObject(strategyClassName);
            if (strategy == null) {
              throw new IllegalStateException(
                  "No strategy of type " + strategyClassName + " available.");
            }
          }

          Object value = strategy.read(stream);
          workItem.setParameter(name, value);
        } catch (ClassNotFoundException e) {
          throw new IllegalArgumentException("Could not reload variable " + name);
        }
      }
    }

    return workItem;
  }
  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();
  }
  public static InternalFactHandle readFactHandle(MarshallerReaderContext context)
      throws IOException, ClassNotFoundException {
    int type = context.stream.readInt();
    int id = context.stream.readInt();
    long recency = context.stream.readLong();

    long startTimeStamp = 0;
    long duration = 0;
    boolean expired = false;
    long activationsCount = 0;
    if (type == 2) {
      startTimeStamp = context.stream.readLong();
      duration = context.stream.readLong();
      expired = context.stream.readBoolean();
      activationsCount = context.stream.readLong();
    }

    int strategyIndex = context.stream.readInt();
    Object object = null;
    ObjectMarshallingStrategy strategy = null;
    // This is the old way of de/serializing strategy objects
    if (strategyIndex >= 0) {
      strategy = context.resolverStrategyFactory.getStrategy(strategyIndex);
    }
    // This is the new way
    else if (strategyIndex == -2) {
      String strategyClassName = context.stream.readUTF();
      if (!StringUtils.isEmpty(strategyClassName)) {
        strategy = context.resolverStrategyFactory.getStrategyObject(strategyClassName);
        if (strategy == null) {
          throw new IllegalStateException(
              "No strategy of type " + strategyClassName + " available.");
        }
      }
    }

    // If either way retrieves a strategy, use it
    if (strategy != null) {
      object = strategy.read(context.stream);
    }

    WorkingMemoryEntryPoint entryPoint = null;
    if (context.readBoolean()) {
      String entryPointId = context.readUTF();
      if (entryPointId != null && !entryPointId.equals("")) {
        entryPoint = context.wm.getEntryPoints().get(entryPointId);
      }
    }
    InternalFactHandle handle = null;
    switch (type) {
      case 0:
        {
          handle = new DefaultFactHandle(id, object, recency, entryPoint);
          break;
        }
      case 1:
        {
          handle = new QueryElementFactHandle(object, id, recency);
          break;
        }
      case 2:
        {
          handle = new EventFactHandle(id, object, recency, startTimeStamp, duration, entryPoint);
          ((EventFactHandle) handle).setExpired(expired);
          ((EventFactHandle) handle).setActivationsCount(activationsCount);
          break;
        }
      default:
        {
          throw new IllegalStateException(
              "Unable to marshal FactHandle, as type does not exist:" + type);
        }
    }

    return handle;
  }