org.snmp4j.agent.db.MOXodusPersistence Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J-Agent-DB 3 - MOXodusPersistence.java
_##
_## Copyright (C) 2017-2018 Frank Fock (SNMP4J.org)
_##
_## Licensed under the Apache License, Version 2.0 (the "License");
_## you may not use this file except in compliance with the License.
_## You may obtain a copy of the License at
_##
_## http://www.apache.org/licenses/LICENSE-2.0
_##
_## Unless required by applicable law or agreed to in writing, software
_## distributed under the License is distributed on an "AS IS" BASIS,
_## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
_## See the License for the specific language governing permissions and
_## limitations under the License.
_##
_##########################################################################*/
package org.snmp4j.agent.db;
import jetbrains.exodus.ArrayByteIterable;
import jetbrains.exodus.ByteIterable;
import jetbrains.exodus.ByteIterator;
import jetbrains.exodus.bindings.CompressedUnsignedLongArrayByteIterable;
import jetbrains.exodus.env.*;
import org.jetbrains.annotations.NotNull;
import org.snmp4j.agent.*;
import org.snmp4j.agent.io.ImportMode;
import org.snmp4j.agent.mo.*;
import org.snmp4j.asn1.BER;
import org.snmp4j.asn1.BERInputStream;
import org.snmp4j.asn1.BEROutputStream;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.smi.*;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.*;
/**
* The {@link MOXodusPersistence} class provides persistent storage for SNMP4J-Agent using the
* {@link MOXodusPersistenceProvider} wrapper that actually implements the
* {@link org.snmp4j.agent.io.MOPersistenceProvider} interface of SNMP4J-Agent.
* As storage engine, the Xodus open source (Apache 2 License) DB is used.
* See https://github.com/JetBrains/xodus} for details.
* The database approach has the following advantages compared to the standard sequential persistence provider coming
* with SNMP4J-Agent:
*
* - Only changed objects are written again to disk. The default DefaultMOPersistenceProvider needs to save all
* objects in a sequence.
* - DB size is smaller - if changes are limited to approximately less than 40% of the MIB objects during
* runtime.
* - Agent shutdown is much faster because no objects need to be saved anymore.
* - No data loss if agent is killed.
*
* The following sample code from {@link org.snmp4j.agent.db.sample.SampleAgent} illustrates how this class is created
* and assigned to the agent during its initialization.
*
*
* File configFile = new File(myConfigDir);
* MOXodusPersistence moXodusPersistence = new MOXodusPersistence(moServers, Environments.newInstance(configFile));
* MOXodusPersistenceProvider moXodusPersistenceProvider = new MOXodusPersistenceProvider(moXodusPersistence);
* OctetString defaultEngineID = new OctetString(MPv3.createLocalEngineID());
* OctetString engineID = moXodusPersistenceProvider.getEngineId(defaultEngineID);
* ...
* agent = new AgentConfigManager(engineID, messageDispatcher, null, moServers, ThreadPool.create("SampleAgent", 3),
* (defaultEngineID == engineID) ? configurationFactory : null,
* moXodusPersistenceProvider,
* new EngineBootsCounterFile(bootCounterFile), null, dhKickstartParameters);
* agent.addAgentStateListener(new AgentStateListener() {
* public void agentStateChanged(AgentConfigManager agentConfigManager, AgentState newState) {
* switch (newState.getState()) {
* case AgentState.STATE_INITIALIZED:
* moXodusPersistence.registerChangeListenersWithServer(server);
* break;
* case AgentState.STATE_SHUTDOWN:
* moXodusPersistence.unregisterChangeListenersWithServer(server);
* break;
* }
* }
* });
*
*
*
* @author Frank Fock
* @version 3.2.0
* @since 3.0
*/
public class MOXodusPersistence implements MOChangeListener {
private static final LogAdapter logger = LogFactory.getLogger(MOXodusPersistence.class);
public enum SavingStrategy {
/**
* The default strategy saves changed data only if a corresponding {@link MOChangeEvent} has been received.
*/
onChangeEventsOnly,
/**
* Save only when the {@link #save()} method is being called and the persistent data do no longer match the
* data in the agent.
*/
checkForModificationsOnSave,
/**
* Always save everything when {@link #save()} is being called without checking for modifications.
*/
fullDumpOnSave
}
private MOServer[] moServers;
private Environment environment;
private boolean ignoreChangeListenerEvents;
private int continuousChangeListening = 0;
private SavingStrategy savingStrategy = SavingStrategy.onChangeEventsOnly;
/**
* Creates a new {@link MOXodusPersistence} from an array of {@link MOServer} instances and an Xodus
* {@link Environment}. The data of modified objects are stored whenever a corresponding {@link MOChangeEvent} is
* received.
* @param moServers
* the {@link ManagedObject} servers of the agent to be supported with persistent storage capabilities by this
* object.
* @param environment
* the Xodus environment that actually holds the persistent data.
*/
public MOXodusPersistence(MOServer[] moServers, Environment environment) {
this(moServers, environment, SavingStrategy.onChangeEventsOnly);
}
/**
* Creates a new {@link MOXodusPersistence} from an array of {@link MOServer} instances and an Xodus
* {@link Environment}.
* @param moServers
* the {@link ManagedObject} servers of the agent to be supported with persistent storage capabilities by this
* object.
* @param environment
* the Xodus environment that actually holds the persistent data.
* @param savingStrategy
* defines when and how modified objects of the agent should be saved into persistent storage.
*/
public MOXodusPersistence(MOServer[] moServers, Environment environment, SavingStrategy savingStrategy) {
this.moServers = moServers;
this.environment = environment;
this.savingStrategy = savingStrategy;
}
public SavingStrategy getSavingStrategy() {
return savingStrategy;
}
public void setSavingStrategy(SavingStrategy savingStrategy) {
this.savingStrategy = savingStrategy;
}
public boolean isIgnoreChangeListenerEvents() {
return ignoreChangeListenerEvents;
}
/**
* Defines whether {@link MOChangeEvent}s should be ignored or not. This method can be used to disable persistent
* storage activities when the default strategy {@link SavingStrategy#onChangeEventsOnly} is active and other bulk
* operations change MIB data in the agent. When activating the processing of {@link MOChangeEvent}s is activated
* again by setting this value to {@code false}, the missed events will not be processed again. Thus, if data has
* changed that need to be persistent, the {@link #save()} has to be called with strategy
* {@link SavingStrategy#checkForModificationsOnSave} or {@link SavingStrategy#fullDumpOnSave} manually.
*
* @param ignoreChangeListenerEvents
* {@code true} to disable event processing and saving changes triggered by {@link MOChangeEvent}s.
*/
public void setIgnoreChangeListenerEvents(boolean ignoreChangeListenerEvents) {
this.ignoreChangeListenerEvents = ignoreChangeListenerEvents;
}
public Environment getEnvironment() {
return environment;
}
public boolean isContinuousChangeListening() {
return continuousChangeListening > 1;
}
/**
* Register this object as {@link MOChangeListener} on all {@link RandomAccessManagedObject} instances in the
* provided {@link MOServer}.
*
* @param moServer
* a {@link MOServer} holding {@link RandomAccessManagedObject}s that should be persisted.
*
*/
public synchronized void registerChangeListenersWithServer(MOServer moServer) {
DefaultMOServer.registerChangeListener(moServer, this, mo -> mo instanceof RandomAccessManagedObject);
continuousChangeListening = 1;
}
/**
* Removes a former registration of this object as {@link MOChangeListener} on all {@link RandomAccessManagedObject}
* instances in the provided {@link MOServer}.
*
* @param moServer
* a {@link MOServer} holding {@link RandomAccessManagedObject}s that should not be persisted anymore.
*
*/
public synchronized void unregisterChangeListenersWithServer(MOServer moServer) {
DefaultMOServer.unregisterChangeListener(moServer, this, mo -> mo instanceof RandomAccessManagedObject);
continuousChangeListening = 0;
}
/**
* Checks if there is already MIB data stored for the specified context.
* To check the default context ({@code null}), please use the empty {@link OctetString}. This method should be
* called before calling {@link #load(ImportMode)} because afterwards it will return {@code true} for all contexts
* that were present in {@link #getMOServer()} and for the default context (empty context).
* @param context
* a non-null context string. The empty (zero length) {@link OctetString} represents the default context.
* @return
* {@code true} if there has been data stored for this context - even if no {@link RandomAccessManagedObject}
* actually has stored any data.
* @since 3.0.1
*/
public synchronized boolean isContextLoadable(OctetString context) {
final Transaction txn = environment.beginReadonlyTransaction();
try {
return environment.storeExists(storeNameFromContext(context), txn);
} finally {
txn.abort();
}
}
/**
* Load the contents of all {@link RandomAccessManagedObject}s using
* {@link RandomAccessManagedObject#importInstance(OID, List, ImportMode)} calls. The provided {@link ImportMode}
* thereby defines how the data handles existing data.
* Data is loaded for all contexts and managed objects found in the {link MOServer} instances provided during
* object creation.
* While loading, the member {@link #ignoreChangeListenerEvents} is set to {@code true} to ignore updates caused
* by loading data into the {@link RandomAccessManagedObject} instances.
*
* @param importMode
* controls how existing data is used or not used during import.
*/
public synchronized void load(ImportMode importMode) {
try {
setIgnoreChangeListenerEvents(true);
continuousChangeListening = (continuousChangeListening > 0) ? continuousChangeListening = 2 : 0;
final Transaction txn = environment.beginReadonlyTransaction();
for (MOServer moServer : moServers) {
Map stores = new HashMap<>();
for (OctetString context : moServer.getContexts()) {
try {
if (!stores.containsKey(context)) {
stores.put(context, createStore(txn, context));
}
Iterator>> moIterator = moServer.iterator();
runSynchronization(stores, txn, importMode, moIterator);
} catch (ReadonlyTransactionException rotex) {
logger.info("No persistent data for context '" + context + "' context found");
}
}
if (moServer.isContextSupported(null)) {
try {
stores.put(new OctetString(), environment.openStore("",
StoreConfig.WITHOUT_DUPLICATES, txn));
Iterator>> moIterator = moServer.iterator();
runSynchronization(stores, txn, importMode, moIterator);
} catch (ReadonlyTransactionException rotex) {
logger.info("No persistent data for context default context found");
}
}
}
txn.abort();
}
finally {
setIgnoreChangeListenerEvents(false);
}
}
@NotNull
protected Store createStore(Transaction txn, OctetString context) {
return environment.openStore(storeNameFromContext(context), StoreConfig.WITHOUT_DUPLICATES, txn);
}
/**
* Return a string store name for the provided SNMPv3 context.
* @param context
* a context name or {@code null} for the default context.
* @return
* a store name, by default {@code context == null ? "" : context.toHexString()}.
* @since 3.0.1
*/
protected String storeNameFromContext(OctetString context) {
return context == null ? "" : context.toHexString();
}
public MOServer[] getMOServer() {
return moServers;
}
protected void runSynchronization(Map stores, Transaction txn, ImportMode importMode,
Iterator>> moIterator) {
// import mode == null -> export
for (; moIterator.hasNext(); ) {
Map.Entry> entry = moIterator.next();
if (entry.getValue() instanceof MOScalar) {
logger.debug("MOScalar " + entry.getValue());
}
if (entry.getValue() instanceof RandomAccessManagedObject) {
RandomAccessManagedObject> randomAccessManagedObject = (RandomAccessManagedObject) entry.getValue();
if (randomAccessManagedObject.isVolatile()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignored " + randomAccessManagedObject + " because it is volatile");
}
continue;
}
OctetString context = new OctetString();
if (entry.getKey() instanceof MOContextScope) {
context = ((MOContextScope) entry.getKey()).getContext();
}
Store store = stores.get(context);
if (store == null) {
logger.info("DB store for context '"+context+"' not found, creating it.");
store = createStore(txn, context);
}
Cursor cursor = store.openCursor(txn);
OID oid = entry.getKey().getLowerBound();
HashSet exported = null;
final ByteIterable objectKey = cursor.getSearchKeyRange(getKey(oid, new OID()));
if (objectKey != null) {
OID instanceOID;
while ((instanceOID = getKeyOid(cursor.getKey())).startsWith(oid)) {
OID index = instanceOID.getSuffix(oid);
ByteIterable rawValues = cursor.getValue();
List rawVBS = decodeInstanceData(rawValues);
if (rawVBS != null && rawVBS.size() > 0) {
if (importMode == null) {
if (exported == null) {
exported = new HashSet<>(randomAccessManagedObject.instanceCount());
}
List exportVbs = null;
if (!randomAccessManagedObject.isVolatile(index)) {
exportVbs = randomAccessManagedObject.exportInstance(index);
}
if (exportVbs == null) {
cursor.deleteCurrent();
} else {
if (savingStrategy == SavingStrategy.checkForModificationsOnSave) {
byte[] exportRawValues = encodeInstanceData(exportVbs);
ArrayByteIterable exportByteIterable = new ArrayByteIterable(exportRawValues);
if (rawValues.compareTo(exportByteIterable) != 0) {
ByteIterable key = getKey(oid, index);
if (logger.isDebugEnabled()) {
logger.debug("Saving modified " + context + ":" + oid + "|" + index +
" ("+key+") = " +
new OctetString(exportRawValues).toHexString() +
" <- " + exportVbs);
}
store.put(txn, key, new ArrayByteIterable(rawValues));
exported.add(index);
}
}
else {
putInstanceData(txn, context, store, oid, index, exportVbs);
exported.add(index);
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Loading data for " + oid + " with index " + index +
" ("+cursor.getKey()+"): " + rawVBS);
}
randomAccessManagedObject.importInstance(index, rawVBS, importMode);
}
} else {
// unknown & unsupported entity
if (logger.isWarnEnabled()) {
logger.warn("Unable to load persistent data: " +
new OctetString(rawValues.getBytesUnsafe()).toHexString());
}
break;
}
if (!cursor.getNext()) {
break;
}
}
}
// export new instances
if (importMode == null) {
// export mode
for (Iterator oidIterator = randomAccessManagedObject.instanceIterator();
oidIterator.hasNext(); ) {
OID nextInstanceOID = oidIterator.next();
if (exported == null || !exported.contains(nextInstanceOID)) {
List vbs = null;
if (!randomAccessManagedObject.isVolatile(nextInstanceOID)) {
vbs = randomAccessManagedObject.exportInstance(nextInstanceOID);
}
if (vbs != null) {
putInstanceData(txn, context, store, oid, nextInstanceOID, vbs);
}
}
}
}
}
}
}
private void putInstanceData(Transaction txn, OctetString context, Store store, OID oid,
OID index, List vbs) {
byte[] rawValues = encodeInstanceData(vbs);
ByteIterable key = getKey(oid, index);
if (logger.isDebugEnabled()) {
logger.debug("Saving " + context + ":" + oid + "|" + index + " ("+key+") = " +
new OctetString(rawValues).toHexString() + " <- " + vbs);
}
store.put(txn, key, new ArrayByteIterable(rawValues));
}
protected List decodeInstanceData(ByteIterable rawData) {
BERInputStream inputStream =
new BERInputStream(ByteBuffer.wrap(rawData.getBytesUnsafe(), 0, rawData.getLength()));
try {
BER.MutableByte pduType;
pduType = new BER.MutableByte();
int vbLength = BER.decodeHeader(inputStream, pduType);
if (pduType.getValue() != BER.SEQUENCE) {
throw new IOException("Encountered invalid tag, SEQUENCE expected: " +
pduType.getValue());
}
// rest read count
int startPos = (int) inputStream.getPosition();
ArrayList variableBindings = new ArrayList<>();
while (inputStream.getPosition() - startPos < vbLength) {
VariableBinding vb = decodeVariableBinding(inputStream);
variableBindings.add(vb);
}
if (inputStream.getPosition() - startPos != vbLength) {
throw new IOException("Length of VB sequence (" + vbLength +
") does not match real length: " +
((int) inputStream.getPosition() - startPos));
}
return variableBindings;
} catch (IOException e) {
logger.error(e);
}
return null;
}
protected byte[] encodeInstanceData(List vbs) {
ArrayList exports = new ArrayList<>(vbs.size());
// exports.add(new VariableBinding(instanceSubID, new Integer32(0)));
exports.addAll(vbs);
int vbLength = 0;
for (VariableBinding vb : exports) {
int indexOIDLength = getIndexOIDLength(vb.getOid().getValue());
int subLength = indexOIDLength + BER.getBERLengthOfLength(indexOIDLength) + 1 + vb.getVariable().getBERLength();
vbLength += BER.getBERLengthOfLength(subLength) + 1 + subLength;
}
BEROutputStream berOutputStream =
new BEROutputStream(ByteBuffer.allocate(vbLength + BER.getBERLengthOfLength(vbLength) + 1));
try {
BER.encodeHeader(berOutputStream, BER.SEQUENCE, vbLength);
for (VariableBinding vb : exports) {
encodeVariableBinding(vb, berOutputStream);
}
berOutputStream.flush();
berOutputStream.close();
return berOutputStream.getBuffer().array();
} catch (IOException e) {
logger.error(e);
return null;
}
}
protected VariableBinding decodeVariableBinding(BERInputStream inputStream) throws IOException {
BER.MutableByte type = new BER.MutableByte();
int length = BER.decodeHeader(inputStream, type);
if (type.getValue() != BER.SEQUENCE) {
throw new IOException("Invalid sequence encoding: " + type.getValue());
}
OID index = new OID(decodeIndexOID(inputStream, type));
Variable variable = AbstractVariable.createFromBER(inputStream);
return new VariableBinding(index, variable);
}
protected void encodeVariableBinding(VariableBinding vb, BEROutputStream outputStream) throws IOException {
Variable variable = vb.getVariable();
int indexOIDLength = getIndexOIDLength(vb.getOid().getValue());
int length = indexOIDLength + BER.getBERLengthOfLength(indexOIDLength) + variable.getBERLength();
BER.encodeHeader(outputStream, BER.SEQUENCE,
length);
encodeIndexOID(outputStream, BER.OID, vb.getOid().getValue());
variable.encodeBER(outputStream);
}
protected ByteIterable getKey(OID oid, OID instanceID) {
if (instanceID == null || instanceID.size() == 0) {
return CompressedUnsignedLongArrayByteIterable.getIterable(oid.toUnsignedLongArray());
}
OID instanceOID = new OID(oid.getValue(), instanceID.getValue());
return CompressedUnsignedLongArrayByteIterable.getIterable(instanceOID.toUnsignedLongArray());
}
protected OID getKeyOid(ByteIterable key) {
int len = key.getLength();
ByteIterator byteIterator = key.iterator();
int bytesPerLong = byteIterator.next();
int[] values = new int[(len-1)/bytesPerLong];
for (int i = 0; i < values.length; ++i) {
values[i] = (int) byteIterator.nextLong(bytesPerLong);
}
return new OID(values);
}
/**
* Saves the data of the {@link MOServer}s associated with this instance to persistent storage depoending on
* the currently configured {@link SavingStrategy}. If that strategy is {@link SavingStrategy#onChangeEventsOnly},
* calling this method will have no effect, except that it sets {@link #setIgnoreChangeListenerEvents(boolean)} to
* {@code false} in any case.
*/
public synchronized void save() {
try {
if (savingStrategy != SavingStrategy.onChangeEventsOnly || !isContinuousChangeListening()) {
for (MOServer moServer : moServers) {
final Transaction txn = environment.beginExclusiveTransaction();
Map stores = new HashMap<>();
for (OctetString context : moServer.getContexts()) {
if (!stores.containsKey(context)) {
stores.put(context, createStore(txn, context));
}
Iterator>> moIterator = moServer.iterator();
runSynchronization(stores, txn, null, moIterator);
}
if (moServer.isContextSupported(null)) {
stores.put(new OctetString(), environment.openStore("",
StoreConfig.WITHOUT_DUPLICATES, txn));
Iterator>> moIterator = moServer.iterator();
runSynchronization(stores, txn, null, moIterator);
}
txn.flush();
txn.commit();
}
}
}
finally {
setIgnoreChangeListenerEvents(false);
}
}
public static void encodeIndexOID(OutputStream os, byte type, int[] oid)
throws IOException {
BER.encodeHeader(os, type, getIndexOIDLength(oid));
int encodedLength = oid.length;
int rpos = 0;
while (encodedLength-- > 0) {
BER.encodeSubID(os, oid[rpos++]);
}
}
public static int getIndexOIDLength(int[] value) {
int length = 0;
for (int i = 0; i < value.length; i++) {
length += BER.getSubIDLength(value[i]);
}
return length;
}
public static int[] decodeIndexOID(BERInputStream is, BER.MutableByte type)
throws IOException {
int subidentifier;
int length;
// get the type
type.setValue((byte) is.read());
if (type.getValue() != 0x06) {
throw new IOException("Wrong type. Not an OID: " + type.getValue() +
BER.getPositionMessage(is));
}
length = BER.decodeLength(is);
int[] oid = new int[length];
// in SNMP pos = 1, but we want to encode any unsigned int at first/second position!
int pos = 0;
while (length > 0) {
subidentifier = 0;
int b;
do { /* shift and add in low order 7 bits */
int next = is.read();
if (next < 0) {
throw new IOException("Unexpected end of input stream" +
BER.getPositionMessage(is));
}
b = next & 0xFF;
subidentifier = (subidentifier << 7) + (b & ~BER.ASN_BIT8);
length--;
} while ((length > 0) && ((b & BER.ASN_BIT8) != 0)); /* last byte has high bit clear */
oid[pos++] = subidentifier;
}
if (pos < oid.length) {
int[] value = new int[pos];
System.arraycopy(oid, 0, value, 0, pos);
return value;
}
return oid;
}
/**
* A ManagedObject change is being prepared. To cancel preparation set the
* deny reason to a SNMPv2/v3 error status.
*
* @param changeEvent
* the change event object.
*/
public void beforePrepareMOChange(MOChangeEvent changeEvent) {
}
/**
* A change has been prepared. Setting the deny reason of the supplied event
* object will be ignored.
*
* @param changeEvent
* the change event object.
*/
public void afterPrepareMOChange(MOChangeEvent changeEvent) {
}
/**
* A ManagedObject change is being committed. To cancel the commit phase set
* the deny reason to a SNMPv2/v3 error status.
*
* NOTE: Canceling the commit phase must be avoided. Setting a deny reason
* has only an effect if {@link MOChangeEvent#isDeniable()} returns
* {@code true}. Otherwise, you will need to throw an exception.
*
* @param changeEvent
* the change event object.
*/
public void beforeMOChange(MOChangeEvent changeEvent) {
}
/**
* A change has been committed. Setting the deny reason of the supplied event
* object will be ignored.
*
* @param changeEvent
* the change event object.
*/
public void afterMOChange(MOChangeEvent changeEvent) {
if (logger.isDebugEnabled()) {
logger.debug("Managed object " + changeEvent.getChangedObject() + " changed");
}
if (!isIgnoreChangeListenerEvents()) {
ManagedObject managedObject = changeEvent.getChangedObject();
if (managedObject instanceof RandomAccessManagedObject) {
RandomAccessManagedObject randomAccessManagedObject = (RandomAccessManagedObject) managedObject;
if (randomAccessManagedObject.isVolatile()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignored change of " + changeEvent.getChangedObject() + " because it is volatile");
}
return;
}
Set contexts = getContexts(managedObject);
if (contexts.size() > 0) {
OID instanceID = changeEvent.getOID();
if (changeEvent.getOidType() != MOChangeEvent.OidType.index) {
instanceID = instanceID.getSuffix(managedObject.getScope().getLowerBound());
}
else if (randomAccessManagedObject.isVolatile(instanceID)) {
if (logger.isDebugEnabled()) {
logger.debug("Sub-instance " + instanceID + " from " + changeEvent + " is volatile");
}
return;
}
@SuppressWarnings("unchecked")
List vbs = randomAccessManagedObject.exportInstance(instanceID);
final Transaction txn = environment.beginTransaction();
if (changeEvent.getModification() == null) {
for (OctetString context : contexts) {
Store store = createStore(txn, context);
putInstanceData(txn, context, store, managedObject.getScope().getLowerBound(),
instanceID, vbs);
}
} else {
switch (changeEvent.getModification()) {
case removed:
for (OctetString context : contexts) {
Store store = createStore(txn, context);
store.delete(txn, getKey(managedObject.getScope().getLowerBound(), instanceID));
if (logger.isDebugEnabled()) {
logger.debug("Removed managed object " +
changeEvent.getChangedObject() + "|" + instanceID);
}
}
break;
case added:
case updated:
for (OctetString context : contexts) {
Store store = createStore(txn, context);
putInstanceData(txn, context, store, managedObject.getScope().getLowerBound(),
instanceID, vbs);
}
break;
}
}
txn.flush();
txn.commit();
} else {
logger.warn("Managed object " + changeEvent.getChangedObject() +
" is not registered to any MOServer known to " + this);
}
}
}
else if (logger.isDebugEnabled()) {
logger.debug("Ignored change event "+changeEvent);
}
}
protected Set getContexts(ManagedObject managedObject) {
Set contexts = new HashSet<>();
for (MOServer moServer : moServers) {
contexts.addAll(Arrays.asList(moServer.getRegisteredContexts(managedObject)));
}
return contexts;
}
}