/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.master; import static com.google.common.collect.Maps.newHashMap; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.threeten.bp.Instant; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.opengamma.DataNotFoundException; import com.opengamma.core.change.BasicChangeManager; import com.opengamma.core.change.ChangeManager; import com.opengamma.core.change.ChangeProvider; import com.opengamma.core.change.ChangeType; import com.opengamma.id.ObjectId; import com.opengamma.id.ObjectIdSupplier; import com.opengamma.id.ObjectIdentifiable; import com.opengamma.id.UniqueId; import com.opengamma.util.ArgumentChecker; /** * A simple, abstract in-memory master. * <p> * This master does not support versioning nor corrections. * <p> * This implementation does not copy stored elements, making it thread-hostile. * As such, this implementation is currently most useful for testing scenarios. * * @param <D> the type of the document */ public abstract class SimpleAbstractInMemoryMaster<D extends AbstractDocument> implements AbstractMaster<D>, ChangeProvider { /** * A cache of documents by identifier. */ protected final ConcurrentMap<ObjectId, D> _store = new ConcurrentHashMap<ObjectId, D>(); // CSIGNORE /** * The supplied of identifiers. */ protected final Supplier<ObjectId> _objectIdSupplier; // CSIGNORE /** * The change manager. */ protected final ChangeManager _changeManager; // CSIGNORE /** * Whether all documents should be cloned on return. True by default. */ private boolean _cloneResults = true; /** * Creates an instance. * * @param defaultOidScheme the default object identifier scheme, not null */ public SimpleAbstractInMemoryMaster(String defaultOidScheme) { this(new ObjectIdSupplier(defaultOidScheme)); } /** * Creates an instance specifying the change manager. * * @param defaultOidScheme the default object identifier scheme, not null * @param changeManager the change manager, not null */ public SimpleAbstractInMemoryMaster(String defaultOidScheme, final ChangeManager changeManager) { this(new ObjectIdSupplier(defaultOidScheme), changeManager); } /** * Creates an instance specifying the supplier of object identifiers. * * @param objectIdSupplier the supplier of object identifiers, not null */ public SimpleAbstractInMemoryMaster(final Supplier<ObjectId> objectIdSupplier) { this(objectIdSupplier, new BasicChangeManager()); } /** * Creates an instance specifying the supplier of object identifiers and change manager. * * @param objectIdSupplier the supplier of object identifiers, not null * @param changeManager the change manager, not null */ public SimpleAbstractInMemoryMaster(final Supplier<ObjectId> objectIdSupplier, final ChangeManager changeManager) { ArgumentChecker.notNull(objectIdSupplier, "objectIdSupplier"); ArgumentChecker.notNull(changeManager, "changeManager"); _objectIdSupplier = objectIdSupplier; _changeManager = changeManager; } /** * Whether to clone all results when searching. True by default. * * @return whether results are cloned. */ public boolean isCloneResults() { return _cloneResults; } /** * Specify whether to clone all results when searching. True by default. * * @param cloneResults whether to clone results when searching. */ public void setCloneResults(boolean cloneResults) { _cloneResults = cloneResults; } //------------------------------------------------------------------------- /** * Validates the specified document. * * @param document the document to validate, null to be validated */ protected abstract void validateDocument(D document); @Override public final ChangeManager changeManager() { return _changeManager; } //------------------------------------------------------------------------- @Override public final List<UniqueId> replaceVersions(ObjectIdentifiable objectIdentifiable, List<D> replacementDocuments) { return replaceAllVersions(objectIdentifiable.getObjectId(), replacementDocuments); } @Override public final List<UniqueId> replaceVersion(UniqueId uniqueId, List<D> replacementDocuments) { return replaceAllVersions(uniqueId.getObjectId(), replacementDocuments); } @Override public final List<UniqueId> replaceAllVersions(ObjectIdentifiable objectId, List<D> replacementDocuments) { ArgumentChecker.notNull(replacementDocuments, "replacementDocuments"); ArgumentChecker.notNull(objectId, "objectId"); for (D replacementDocument : replacementDocuments) { validateDocument(replacementDocument); } final Instant now = Instant.now(); ArgumentChecker.isTrue(MasterUtils.checkUniqueVersionsFrom(replacementDocuments), "No two versioned documents may have the same \"version from\" instant"); final D storedDocument = _store.get(objectId.getObjectId()); if (storedDocument == null) { throw new DataNotFoundException("Document not found: " + objectId.getObjectId()); } if (replacementDocuments.isEmpty()) { _store.remove(objectId.getObjectId()); _changeManager.entityChanged(ChangeType.REMOVED, objectId.getObjectId(), null, null, now); return Collections.emptyList(); } else { Instant storedVersionFrom = storedDocument.getVersionFromInstant(); Instant storedVersionTo = storedDocument.getVersionToInstant(); List<D> orderedReplacementDocuments = MasterUtils.adjustVersionInstants(now, storedVersionFrom, storedVersionTo, replacementDocuments); D lastReplacementDocument = orderedReplacementDocuments.get(orderedReplacementDocuments.size() - 1); lastReplacementDocument.setUniqueId(objectId.getObjectId().atLatestVersion()); if (_store.replace(objectId.getObjectId(), storedDocument, lastReplacementDocument) == false) { throw new IllegalArgumentException("Concurrent modification"); } Instant versionFromInstant = orderedReplacementDocuments.get(0).getVersionFromInstant(); Instant versionToInstant = Iterables.getLast(orderedReplacementDocuments).getVersionToInstant(); changeManager().entityChanged(ChangeType.CHANGED, objectId.getObjectId(), versionFromInstant, versionToInstant, now); updateCaches(objectId, lastReplacementDocument); return ImmutableList.of(lastReplacementDocument.getUniqueId()); } } /** * Subclasses that support additional caching should override this method. * * @param replacedObject The version removed (possibly null) * @param updatedDocument The version added (possibly null) */ protected void updateCaches(ObjectIdentifiable replacedObject, D updatedDocument) { } //------------------------------------------------------------------------- @Override public final UniqueId addVersion(ObjectIdentifiable objectId, D documentToAdd) { List<UniqueId> result = replaceVersions(objectId, Collections.singletonList(documentToAdd)); if (result.isEmpty()) { return null; } else { return result.get(0); } } @Override public final void removeVersion(final UniqueId uniqueId) { replaceVersion(uniqueId, Collections.<D>emptyList()); } @Override public final UniqueId replaceVersion(D replacementDocument) { List<UniqueId> result = replaceVersion(replacementDocument.getUniqueId(), Collections.singletonList(replacementDocument)); if (result.isEmpty()) { return null; } else { return result.get(0); } } //------------------------------------------------------------------------- @Override public Map<UniqueId, D> get(Collection<UniqueId> uniqueIds) { Map<UniqueId, D> resultMap = newHashMap(); for (UniqueId uniqueId : uniqueIds) { D doc = get(uniqueId); resultMap.put(uniqueId, doc); } return resultMap; } }