docs: public ProcessInstance readProcessInstance(MarshallerReaderContext context) throws IOException {
          InternalKnowledgeBase ruleBase = context.kBase;
          InternalWorkingMemory wm = context.wm;
          
          JBPMMessages.ProcessInstance _instance = (org.jbpm.marshalling.impl.JBPMMessages.ProcessInstance) context.parameterObject;
          if( _instance == null ) {
              // try to parse from the stream
              ExtensionRegistry registry = PersisterHelper.buildRegistry( context, null ); 
              Header _header;
              try {
  
                  _header = PersisterHelper.readFromStreamWithHeaderPreloaded(context, registry);
              } catch ( ClassNotFoundException e ) {
                  // Java 5 does not accept [new IOException(String, Throwable)]
                  IOException ioe =  new IOException( "Error deserializing process instance." );
                  ioe.initCause(e);
                  throw ioe;
              }
              _instance = JBPMMessages.ProcessInstance.parseFrom( _header.getPayload(), registry );
          }
  
          WorkflowProcessInstanceImpl processInstance = createProcessInstance();
          processInstance.setId( _instance.getId() );
          String processId = _instance.getProcessId();
          processInstance.setProcessId( processId );
          String processXml = _instance.getProcessXml();
          Process process = null;
          if (processXml != null && processXml.trim().length() > 0) {
          	processInstance.setProcessXml( processXml );
          	process = processInstance.getProcess();
          } else {
              process = ruleBase.getProcess( processId );
              if (process == null) {
              	throw new RuntimeException("Could not find process " + processId + " when restoring process instance " + processInstance.getId());
              }
              processInstance.setProcess( process );
          }
          processInstance.setDescription(_instance.getDescription());
          processInstance.setState( _instance.getState() );
          processInstance.setParentProcessInstanceId(_instance.getParentProcessInstanceId());
          processInstance.setSignalCompletion(_instance.getSignalCompletion());
          processInstance.setDeploymentId(_instance.getDeploymentId());
          processInstance.setCorrelationKey(_instance.getCorrelationKey());
          processInstance.internalSetSlaCompliance(_instance.getSlaCompliance());
          if (_instance.getSlaDueDate() > 0) {
              processInstance.internalSetSlaDueDate(new Date(_instance.getSlaDueDate()));
          }
          processInstance.internalSetSlaTimerId(_instance.getSlaTimerId());
          
          long nodeInstanceCounter = _instance.getNodeInstanceCounter();
          processInstance.setKnowledgeRuntime( wm.getKnowledgeRuntime() );
          processInstance.internalSetNodeInstanceCounter( nodeInstanceCounter );
          for( String completedNodeId : _instance.getCompletedNodeIdsList() ) { 
              processInstance.addCompletedNodeId(completedNodeId);
          }
  
          if ( _instance.getSwimlaneContextCount() > 0 ) {
              Context swimlaneContext = ((org.jbpm.process.core.Process) process).getDefaultContext( SwimlaneContext.SWIMLANE_SCOPE );
              SwimlaneContextInstance swimlaneContextInstance = (SwimlaneContextInstance) processInstance.getContextInstance( swimlaneContext );
              for ( JBPMMessages.ProcessInstance.SwimlaneContextInstance _swimlane : _instance.getSwimlaneContextList() ) {
                  swimlaneContextInstance.setActorId( _swimlane.getSwimlane(), _swimlane.getActorId() );
              }
          }
  
          for ( JBPMMessages.ProcessInstance.NodeInstance _node : _instance.getNodeInstanceList() ) {
              context.parameterObject = _node;
              readNodeInstance( context, 
                                processInstance, 
                                processInstance );
          }
  
          for ( JBPMMessages.ProcessInstance.ExclusiveGroupInstance _excl : _instance.getExclusiveGroupList() ) {
              ExclusiveGroupInstance exclusiveGroupInstance = new ExclusiveGroupInstance();
              processInstance.addContextInstance( ExclusiveGroup.EXCLUSIVE_GROUP, exclusiveGroupInstance );
              for ( Long nodeInstanceId : _excl.getGroupNodeInstanceIdList() ) {
                  NodeInstance nodeInstance = ((org.jbpm.workflow.instance.NodeInstanceContainer)processInstance).getNodeInstance( nodeInstanceId, true );
                  if ( nodeInstance == null ) {
                      throw new IllegalArgumentException( "Could not find node instance when deserializing exclusive group instance: " + nodeInstanceId );
                  }
                  exclusiveGroupInstance.addNodeInstance( nodeInstance );
              }
          }
  
          if ( _instance.getVariableCount() > 0 ) {
              Context variableScope = ((org.jbpm.process.core.Process) process)
                      .getDefaultContext( VariableScope.VARIABLE_SCOPE );
              VariableScopeInstance variableScopeInstance = (VariableScopeInstance) processInstance
                      .getContextInstance( variableScope );
              for ( JBPMMessages.Variable _variable : _instance.getVariableList() ) {
                  try {
                      Object _value = ProtobufProcessMarshaller.unmarshallVariableValue( context, _variable );
                      variableScopeInstance.internalSetVariable( _variable.getName(), 
                                                                 _value );
                  } catch ( ClassNotFoundException e ) {
                      throw new IllegalArgumentException( "Could not reload variable " + _variable.getName() );
                  }
              }
          }
          
          if ( _instance.getIterationLevelsCount() > 0 ) {
              
              for ( JBPMMessages.IterationLevel _level : _instance.getIterationLevelsList()) {
                  processInstance.getIterationLevels().put(_level.getId(), _level.getLevel());
              }
          }        
      	processInstance.reconnect();
          return processInstance;
      }