protected void initKsession(
      Integer sessionId,
      KnowledgeBase kbase,
      KnowledgeSessionConfiguration conf,
      PersistenceContext persistenceContext) {
    if (!doRollback && this.ksession != null) {
      return;
      // nothing to initialise
    }

    this.doRollback = false;
    try {
      this.sessionInfo = persistenceContext.findSessionInfo(sessionId);
    } catch (Exception e) {
      throw new RuntimeException("Could not find session data for id " + sessionId, e);
    }

    if (sessionInfo == null) {
      throw new RuntimeException("Could not find session data for id " + sessionId);
    }

    if (this.marshallingHelper == null) {
      // this should only happen when this class is first constructed
      this.marshallingHelper = new SessionMarshallingHelper(kbase, conf, env);
      MarshallingConfigurationImpl config =
          (MarshallingConfigurationImpl)
              ((DefaultMarshaller) this.marshallingHelper.getMarshaller())
                  .getMarshallingConfiguration();
      config.setMarshallProcessInstances(false);
      config.setMarshallWorkItems(false);
    }

    this.sessionInfo.setJPASessionMashallingHelper(this.marshallingHelper);

    // if this.ksession is null, it'll create a new one, else it'll use the existing one
    this.ksession = this.marshallingHelper.loadSnapshot(this.sessionInfo.getData(), this.ksession);

    // update the session id to be the same as the session info id
    ((InternalKnowledgeRuntime) ksession).setId(this.sessionInfo.getId());

    ((InternalKnowledgeRuntime) this.ksession)
        .setEndOperationListener(new EndOperationListenerImpl(this.sessionInfo));

    if (this.kContext == null) {
      // this should only happen when this class is first constructed
      this.kContext = new FixedKnowledgeCommandContext(null, null, null, this.ksession, null);
    }

    this.commandService = new DefaultCommandService(kContext);

    ((AcceptsTimerJobFactoryManager) ((InternalKnowledgeRuntime) ksession).getTimerService())
        .getTimerJobFactoryManager()
        .setCommandService(this);
  }
  public static Object unmarshall(byte[] content, Environment env, ClassLoader classloader) {
    MarshallerReaderContext context = null;
    try {
      ByteArrayInputStream stream = new ByteArrayInputStream(content);
      MarshallingConfigurationImpl marshallingConfigurationImpl = null;
      if (env != null) {
        marshallingConfigurationImpl =
            new MarshallingConfigurationImpl(
                (ObjectMarshallingStrategy[])
                    env.get(EnvironmentName.OBJECT_MARSHALLING_STRATEGIES),
                false,
                false);
      } else {
        marshallingConfigurationImpl =
            new MarshallingConfigurationImpl(
                new ObjectMarshallingStrategy[] {
                  new SerializablePlaceholderResolverStrategy(
                      ClassObjectMarshallingStrategyAcceptor.DEFAULT)
                },
                false,
                false);
      }
      ObjectMarshallingStrategyStore objectMarshallingStrategyStore =
          marshallingConfigurationImpl.getObjectMarshallingStrategyStore();
      context =
          new MarshallerReaderContext(
              stream, null, null, objectMarshallingStrategyStore, null, env);
      if (classloader != null) {
        context.classLoader = classloader;
      } else {
        context.classLoader = ContentMarshallerHelper.class.getClassLoader();
      }
      ExtensionRegistry registry = PersisterHelper.buildRegistry(context, null);
      Header _header = PersisterHelper.readFromStreamWithHeader(context, registry);
      Variable parseFrom = JBPMMessages.Variable.parseFrom(_header.getPayload(), registry);
      Object value = ProtobufProcessMarshaller.unmarshallVariableValue(context, parseFrom);

      if (value instanceof Map) {
        Map result = new HashMap();
        Map<String, Variable> variablesMap = (Map<String, Variable>) value;
        for (String key : variablesMap.keySet()) {
          result.put(
              key,
              ProtobufProcessMarshaller.unmarshallVariableValue(context, variablesMap.get(key)));
        }
        return result;
      }
      return value;
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return null;
  }
  public static ContentData marshal(Object o, Environment env) {
    MarshallerWriteContext context = null;
    ContentData content = null;
    try {
      MarshallingConfigurationImpl marshallingConfigurationImpl = null;
      if (env != null) {
        marshallingConfigurationImpl =
            new MarshallingConfigurationImpl(
                (ObjectMarshallingStrategy[])
                    env.get(EnvironmentName.OBJECT_MARSHALLING_STRATEGIES),
                false,
                false);
      } else {
        marshallingConfigurationImpl =
            new MarshallingConfigurationImpl(
                new ObjectMarshallingStrategy[] {
                  new SerializablePlaceholderResolverStrategy(
                      ClassObjectMarshallingStrategyAcceptor.DEFAULT)
                },
                false,
                false);
      }
      ObjectMarshallingStrategyStore objectMarshallingStrategyStore =
          marshallingConfigurationImpl.getObjectMarshallingStrategyStore();
      ByteArrayOutputStream stream = new ByteArrayOutputStream();

      context =
          new MarshallerWriteContext(stream, null, null, null, objectMarshallingStrategyStore, env);
      Variable marshallVariable = null;
      if (o instanceof Map) {
        marshallVariable =
            ProtobufProcessMarshaller.marshallVariablesMap(context, (Map<String, Object>) o);
      } else {
        marshallVariable = ProtobufProcessMarshaller.marshallVariable(context, "results", o);
      }
      PersisterHelper.writeToStreamWithHeader(context, marshallVariable);

      context.close();

      byte[] toByteArray = stream.toByteArray();

      content = new ContentData();
      content.setContent(toByteArray);
      content.setType(o.getClass().getCanonicalName());
      content.setAccessType(AccessType.Inline);

    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return content;
  }
  public SingleSessionCommandService(
      KnowledgeBase kbase, KnowledgeSessionConfiguration conf, Environment env) {
    if (conf == null) {
      conf = new SessionConfiguration();
    }
    this.env = env;

    checkEnvironment(this.env);

    this.sessionInfo = new SessionInfo();

    initTransactionManager(this.env);

    // create session but bypass command service
    this.ksession = kbase.newStatefulKnowledgeSession(conf, this.env);

    this.kContext = new FixedKnowledgeCommandContext(null, null, null, this.ksession, null);

    this.commandService = new DefaultCommandService(kContext);

    ((AcceptsTimerJobFactoryManager) ((InternalKnowledgeRuntime) ksession).getTimerService())
        .getTimerJobFactoryManager()
        .setCommandService(this);

    this.marshallingHelper = new SessionMarshallingHelper(this.ksession, conf);
    MarshallingConfigurationImpl config =
        (MarshallingConfigurationImpl)
            ((DefaultMarshaller) this.marshallingHelper.getMarshaller())
                .getMarshallingConfiguration();
    config.setMarshallProcessInstances(false);
    config.setMarshallWorkItems(false);

    this.sessionInfo.setJPASessionMashallingHelper(this.marshallingHelper);
    ((InternalKnowledgeRuntime) this.ksession)
        .setEndOperationListener(new EndOperationListenerImpl(this.sessionInfo));

    // Use the App scoped EntityManager if the user has provided it, and it is open.
    // - open the entity manager before the transaction begins.
    PersistenceContext persistenceContext = jpm.getApplicationScopedPersistenceContext();

    boolean transactionOwner = false;
    try {
      transactionOwner = txm.begin();

      registerRollbackSync();

      persistenceContext.joinTransaction();
      persistenceContext.persist(this.sessionInfo);

      txm.commit(transactionOwner);
    } catch (RuntimeException re) {
      rollbackTransaction(re, transactionOwner);
      throw re;
    } catch (Exception t1) {
      rollbackTransaction(t1, transactionOwner);
      throw new RuntimeException("Wrapped exception see cause", t1);
    }

    // update the session id to be the same as the session info id
    ((InternalKnowledgeRuntime) ksession).setId(this.sessionInfo.getId());
  }