public void RemoveVariableIfFound(
            string deploymentId,
            string variableName)
        {
            lock (this) {
                var entry = DeploymentsWithVariables.Get(deploymentId);

                var variable = entry?.GetVariable(variableName);
                if (variable == null) {
                    return;
                }

                if (Log.IsDebugEnabled) {
                    Log.Debug("Removing variable '" + variableName + "'");
                }

                entry.Remove(variableName);

                if (OptionalStateHandler != null && !variable.MetaData.IsConstant) {
                    var readers = variableVersionsPerCP[variable.VariableNumber];
                    ICollection<int> cps = Collections.GetEmptySet<int>();
                    if (readers != null) {
                        cps = readers.Keys;
                    }

                    OptionalStateHandler.RemoveVariable(variable, deploymentId, cps);
                }

                var number = variable.VariableNumber;
                variableVersionsPerCP[number] = null;
                changeCallbacksPerCP[number] = null;
            }
        }
        public void AddVariable(
            string deploymentId,
            VariableMetaData metaData,
            string optionalDeploymentIdContext,
            DataInputOutputSerde optionalSerde)
        {
            lock (this) {
                // check if already exists
                var deploymentEntry = DeploymentsWithVariables.Get(deploymentId);
                if (deploymentEntry != null) {
                    var variableX = deploymentEntry.GetVariable(metaData.VariableName);
                    if (variableX != null) {
                        throw new ArgumentException(
                            "Variable already exists by name '" +
                            metaData.VariableName +
                            "' and deployment '" +
                            deploymentId +
                            "'");
                    }
                }
                else {
                    deploymentEntry = new VariableDeployment();
                    DeploymentsWithVariables.Put(deploymentId, deploymentEntry);
                }

                // find empty spot
                var emptySpot = -1;
                var count = 0;
                foreach (var entry in variableVersionsPerCP) {
                    if (entry == null) {
                        emptySpot = count;
                        break;
                    }

                    count++;
                }

                int variableNumber;
                if (emptySpot != -1) {
                    variableNumber = emptySpot;
                    variableVersionsPerCP[emptySpot] = new ConcurrentDictionary<int, VariableReader>();
                    changeCallbacksPerCP[emptySpot] = null;
                }
                else {
                    variableNumber = currentVariableNumber;
                    variableVersionsPerCP.Add(new ConcurrentDictionary<int, VariableReader>());
                    changeCallbacksPerCP.Add(null);
                    currentVariableNumber++;
                }

                var variable = new Variable(variableNumber, deploymentId, metaData, optionalDeploymentIdContext);
                deploymentEntry.AddVariable(metaData.VariableName, variable);

                if (OptionalStateHandler != null && !metaData.IsConstant) {
                    OptionalStateHandler.AddVariable(deploymentId, metaData.VariableName, variable, optionalSerde);
                }
            }
        }
        public void Commit()
        {
            var entry = versionThreadLocal.CurrentThread;
            if (entry.Uncommitted == null) {
                return;
            }

            // get new version for adding the new values (1 or many new values)
            var newVersion = currentVersionNumber + 1;

            if (currentVersionNumber == ROLLOVER_READER_BOUNDARY) {
                // Roll over to new collections;
                // This honors existing threads that will now use the "high" collection in the reader for high version requests
                // and low collection (new and updated) for low version requests
                RollOver();
                newVersion = 2;
            }

            var timestamp = timeProvider.Time;

            // apply all uncommitted changes
            foreach (var uncommittedEntry in entry.Uncommitted.ToList()) {
                IDictionary<int, VariableReader> cps = variableVersionsPerCP[uncommittedEntry.Key];
                var reader = cps.Get(uncommittedEntry.Value.First);
                var versions = reader.VersionsLow;

                // add new value as a new version
                var newValue = uncommittedEntry.Value.Second;
                var oldValue = versions.AddValue(newVersion, newValue, timestamp);

                // make a callback that the value changed
                var cpsCallback = changeCallbacksPerCP[uncommittedEntry.Key];
                var callbacks = cpsCallback?.Get(uncommittedEntry.Value.First);
                if (callbacks != null) {
                    foreach (var callback in callbacks) {
                        callback.Update(newValue, oldValue);
                    }
                }

                // Check current state - see if the variable exists in the state handler
                if (OptionalStateHandler != null) {
                    var metaData = reader.MetaData;
                    if (!metaData.IsConstant) {
                        var agentInstanceId = metaData.OptionalContextName == null
                            ? DEFAULT_AGENT_INSTANCE_ID
                            : uncommittedEntry.Value.First;
                        OptionalStateHandler.SetState(reader.Variable, agentInstanceId, newValue);
                    }
                }
            }

            // this makes the new values visible to other threads (not this thread unless set-version called again)
            currentVersionNumber = newVersion;
            entry.Uncommitted = null; // clean out uncommitted variables
        }
        public void DeallocateVariableState(
            string deploymentId,
            string variableName,
            int agentInstanceId)
        {
            var entry = DeploymentsWithVariables.Get(deploymentId);
            if (entry == null) {
                throw new ArgumentException("Failed to find variable deployment id '" + deploymentId + "'");
            }

            var variable = entry.GetVariable(variableName);
            if (variable == null) {
                throw new ArgumentException("Failed to find variable '" + variableName + "'");
            }

            IDictionary<int, VariableReader> cps = variableVersionsPerCP[variable.VariableNumber];
            cps.Remove(agentInstanceId);

            if (OptionalStateHandler != null && !variable.MetaData.IsConstant) {
                OptionalStateHandler.RemoveState(variable, agentInstanceId);
            }
        }
        public void AllocateVariableState(
            string deploymentId,
            string variableName,
            int agentInstanceId,
            bool recovery,
            NullableObject<object> initialValue,
            EventBeanTypedEventFactory eventBeanTypedEventFactory)
        {
            var entry = DeploymentsWithVariables.Get(deploymentId);
            if (entry == null) {
                throw new ArgumentException("Failed to find variable deployment id '" + deploymentId + "'");
            }

            var variable = entry.GetVariable(variableName);
            if (variable == null) {
                throw new ArgumentException("Failed to find variable '" + variableName + "'");
            }

            // Check current state - see if the variable exists in the state handler
            object initialState;
            if (initialValue != null) {
                initialState = initialValue.Value;
            }
            else {
                initialState = variable.MetaData.ValueWhenAvailable;
            }

            if (variable.MetaData.EventType != null && initialState != null && !(initialState is EventBean)) {
                initialState = eventBeanTypedEventFactory.AdapterForTypedObject(
                    initialState,
                    variable.MetaData.EventType);
            }

            if (OptionalStateHandler != null && !variable.MetaData.IsConstant) {
                var priorValue = OptionalStateHandler.GetHasState(variable, agentInstanceId);
                if (recovery) {
                    if (priorValue != null) {
                        initialState = priorValue.Value;
                    }
                }
                else {
                    if (priorValue == null) { // we do not already have a value
                        OptionalStateHandler.SetState(variable, agentInstanceId, initialState);
                    }
                    else {
                        initialState = priorValue.Value;
                    }
                }
            }

            // create new holder for versions
            var timestamp = timeProvider.Time;
            var valuePerVersion = new VersionedValueList<object>(
                variableName,
                currentVersionNumber,
                initialState,
                timestamp,
                millisecondLifetimeOldVersions,
                ReadWriteLock.ReadLock,
                HIGH_WATERMARK_VERSIONS,
                false);
            IDictionary<int, VariableReader> cps = variableVersionsPerCP[variable.VariableNumber];
            var reader = new VariableReader(variable, versionThreadLocal, valuePerVersion);
            cps.Put(agentInstanceId, reader);
        }