package org.atlasapi.persistence;
import org.atlasapi.application.v3.DefaultApplication;
import org.atlasapi.media.channel.CachingChannelStore;
import org.atlasapi.media.channel.ChannelGroupStore;
import org.atlasapi.media.channel.ChannelStore;
import org.atlasapi.media.channel.MongoChannelGroupStore;
import org.atlasapi.media.channel.MongoChannelStore;
import org.atlasapi.media.channel.ServiceChannelStore;
import org.atlasapi.media.entity.Event;
import org.atlasapi.media.entity.Topic;
import org.atlasapi.media.product.IdSettingProductStore;
import org.atlasapi.media.product.ProductResolver;
import org.atlasapi.media.product.ProductStore;
import org.atlasapi.media.segment.IdSettingSegmentWriter;
import org.atlasapi.media.segment.MongoSegmentResolver;
import org.atlasapi.media.segment.MongoSegmentWriter;
import org.atlasapi.media.segment.SegmentResolver;
import org.atlasapi.media.segment.SegmentWriter;
import org.atlasapi.messaging.v3.ContentEquivalenceAssertionMessage;
import org.atlasapi.messaging.v3.ContentEquivalenceAssertionMessenger;
import org.atlasapi.messaging.v3.EntityUpdatedMessage;
import org.atlasapi.messaging.v3.JacksonMessageSerializer;
import org.atlasapi.messaging.v3.MessagingModule;
import org.atlasapi.messaging.v3.ScheduleUpdateMessage;
import org.atlasapi.persistence.audit.NoLoggingPersistenceAuditLog;
import org.atlasapi.persistence.audit.PerHourAndDayMongoPersistenceAuditLog;
import org.atlasapi.persistence.audit.PersistenceAuditLog;
import org.atlasapi.persistence.content.ContentGroupResolver;
import org.atlasapi.persistence.content.ContentGroupWriter;
import org.atlasapi.persistence.content.ContentPurger;
import org.atlasapi.persistence.content.ContentResolver;
import org.atlasapi.persistence.content.ContentWriter;
import org.atlasapi.persistence.content.DefaultEquivalentContentResolver;
import org.atlasapi.persistence.content.EquivalenceWritingContentWriter;
import org.atlasapi.persistence.content.EquivalentContentResolver;
import org.atlasapi.persistence.content.IdSettingContentWriter;
import org.atlasapi.persistence.content.KnownTypeContentResolver;
import org.atlasapi.persistence.content.LookupBackedContentIdGenerator;
import org.atlasapi.persistence.content.LookupResolvingContentResolver;
import org.atlasapi.persistence.content.MessageQueueingContentWriter;
import org.atlasapi.persistence.content.MessageQueuingContentGroupWriter;
import org.atlasapi.persistence.content.PeopleQueryResolver;
import org.atlasapi.persistence.content.listing.MongoProgressStore;
import org.atlasapi.persistence.content.mongo.MongoContentGroupResolver;
import org.atlasapi.persistence.content.mongo.MongoContentGroupWriter;
import org.atlasapi.persistence.content.mongo.MongoContentLister;
import org.atlasapi.persistence.content.mongo.MongoContentPurger;
import org.atlasapi.persistence.content.mongo.MongoContentResolver;
import org.atlasapi.persistence.content.mongo.MongoContentTables;
import org.atlasapi.persistence.content.mongo.MongoContentWriter;
import org.atlasapi.persistence.content.mongo.MongoPersonStore;
import org.atlasapi.persistence.content.mongo.MongoPlayerStore;
import org.atlasapi.persistence.content.mongo.MongoProductStore;
import org.atlasapi.persistence.content.mongo.MongoServiceStore;
import org.atlasapi.persistence.content.mongo.MongoTopicStore;
import org.atlasapi.persistence.content.organisation.IdSettingOrganisationStore;
import org.atlasapi.persistence.content.organisation.MongoOrganisationStore;
import org.atlasapi.persistence.content.organisation.OrganisationStore;
import org.atlasapi.persistence.content.organisation.QueueingOrganisationStore;
import org.atlasapi.persistence.content.people.EquivalatingPeopleResolver;
import org.atlasapi.persistence.content.people.IdSettingPersonStore;
import org.atlasapi.persistence.content.people.ItemsPeopleWriter;
import org.atlasapi.persistence.content.people.PersonStore;
import org.atlasapi.persistence.content.people.QueuingItemsPeopleWriter;
import org.atlasapi.persistence.content.people.QueuingPersonWriter;
import org.atlasapi.persistence.content.schedule.mongo.MongoScheduleStore;
import org.atlasapi.persistence.event.EventResolver;
import org.atlasapi.persistence.event.EventStore;
import org.atlasapi.persistence.event.EventWriter;
import org.atlasapi.persistence.event.IdSettingEventStore;
import org.atlasapi.persistence.event.MessageQueueingEventWriter;
import org.atlasapi.persistence.event.MongoEventStore;
import org.atlasapi.persistence.ids.MongoSequentialIdGenerator;
import org.atlasapi.persistence.logging.AdapterLog;
import org.atlasapi.persistence.lookup.LookupWriter;
import org.atlasapi.persistence.lookup.TransitiveLookupWriter;
import org.atlasapi.persistence.lookup.entry.LookupEntryStore;
import org.atlasapi.persistence.lookup.mongo.MongoLookupEntryStore;
import org.atlasapi.persistence.player.CachingPlayerResolver;
import org.atlasapi.persistence.player.PlayerResolver;
import org.atlasapi.persistence.service.CachingServiceResolver;
import org.atlasapi.persistence.service.ServiceResolver;
import org.atlasapi.persistence.shorturls.MongoShortUrlSaver;
import org.atlasapi.persistence.shorturls.ShortUrlSaver;
import org.atlasapi.persistence.topic.MessageQueueingTopicWriter;
import org.atlasapi.persistence.topic.TopicCreatingTopicResolver;
import org.atlasapi.persistence.topic.TopicQueryResolver;
import org.atlasapi.persistence.topic.TopicStore;
import com.metabroadcast.common.ids.IdGenerator;
import com.metabroadcast.common.ids.SubstitutionTableNumberCodec;
import com.metabroadcast.common.persistence.mongo.DatabasedMongo;
import com.metabroadcast.common.persistence.mongo.health.MongoIOProbe;
import com.metabroadcast.common.properties.Parameter;
import com.metabroadcast.common.queue.MessageSender;
import com.metabroadcast.common.time.SystemClock;
import com.google.common.base.Optional;
import com.mongodb.Mongo;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import org.joda.time.DateTime;
import org.springframework.context.annotation.Bean;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* This abstraction logic of the ContentPersistenceModule is done because we do not want to
* wire Spring and our Dropwizard microservices project. Currently, BT ingesters extracted from
* monolith will be using atlas-persistence to write to mongoDB directly until we decided to
* move it to using the Atlas POST api.
*
* This is intended for non-DI use.
*/
public class ConstructorBasedMongoContentPersistenceModule implements ContentPersistenceModule {
private static final String LOOKUP = "lookup";
private final ReadPreference readPreference;
private final Mongo mongo;
private final DatabasedMongo db;
private final AdapterLog log;
private final MessagingModule messagingModule;
private final String contentChanges;
private final String topicChanges;
private final String scheduleChanges;
private final String contentGroupChanges;
private final String eventChanges;
private final String organisationChanges;
private final boolean messagingEnabled;
private final String auditDbName;
private final boolean auditEnabled;
private final String equivAssertDest;
// This decides whether to use MongoChannelStore or CachingChannelStore which has
// additional methods.
private final Parameter processingConfig;
//This MongoContentPersistenceModule is intended to be used by projects without DI.
public ConstructorBasedMongoContentPersistenceModule(
Mongo mongo,
DatabasedMongo db,
MessagingModule messagingModule,
String auditDbName,
AdapterLog log,
ReadPreference readPreference,
String contentChanges,
String topicChanges,
String scheduleChanges,
String contentGroupChanges,
String eventChanges,
String organisationChanges,
boolean messagingEnabled,
boolean auditEnabled,
Parameter processingConfig,
String equivAssertDest
) {
this.mongo = checkNotNull(mongo);
this.db = checkNotNull(db);
this.log = checkNotNull(log);
this.messagingModule = checkNotNull(messagingModule);
this.auditDbName = checkNotNull(auditDbName);
this.readPreference = checkNotNull(readPreference);
this.contentChanges = checkNotNull(contentChanges);
this.topicChanges = checkNotNull(topicChanges);
this.scheduleChanges = checkNotNull(scheduleChanges);
this.contentGroupChanges = checkNotNull(contentGroupChanges);
this.eventChanges = checkNotNull(eventChanges);
this.organisationChanges = checkNotNull(organisationChanges);
this.messagingEnabled = messagingEnabled;
this.auditEnabled = checkNotNull(auditEnabled);
this.processingConfig = checkNotNull(processingConfig);
this.equivAssertDest = checkNotNull(equivAssertDest);
}
public MessageSender<EntityUpdatedMessage> contentChanges() {
return messagingModule.messageSenderFactory().makeMessageSender(contentChanges,
JacksonMessageSerializer.forType(EntityUpdatedMessage.class));
}
public MessageSender<EntityUpdatedMessage> topicChanges() {
return messagingModule.messageSenderFactory().makeMessageSender(topicChanges,
JacksonMessageSerializer.forType(EntityUpdatedMessage.class));
}
public MessageSender<ScheduleUpdateMessage> scheduleChanges() {
return messagingModule.messageSenderFactory().makeMessageSender(scheduleChanges,
JacksonMessageSerializer.forType(ScheduleUpdateMessage.class));
}
public MessageSender<EntityUpdatedMessage> eventChanges() {
return messagingModule.messageSenderFactory().makeMessageSender(eventChanges,
JacksonMessageSerializer.forType(EntityUpdatedMessage.class));
}
public MessageSender<EntityUpdatedMessage> organizationChanges() {
return messagingModule.messageSenderFactory().makeMessageSender(organisationChanges,
JacksonMessageSerializer.forType(EntityUpdatedMessage.class));
}
private ContentEquivalenceAssertionMessenger messenger() {
return ContentEquivalenceAssertionMessenger.create(
messagingModule.messageSenderFactory()
.makeMessageSender(
equivAssertDest,
JacksonMessageSerializer.forType(
ContentEquivalenceAssertionMessage.class
)
),
new SystemClock(),
lookupStore()
);
}
@Override
public ContentGroupWriter contentGroupWriter() {
MessageSender<EntityUpdatedMessage> messageSender = messagingModule.messageSenderFactory()
.makeMessageSender(
contentGroupChanges,
JacksonMessageSerializer.forType(EntityUpdatedMessage.class)
);
SystemClock clock = new SystemClock();
return new MessageQueuingContentGroupWriter(
new MongoContentGroupWriter(db, persistenceAuditLog(), clock),
messageSender,
clock
);
}
@Override
public ContentGroupResolver contentGroupResolver() {
return new MongoContentGroupResolver(db);
}
@Override
public ContentWriter contentWriter() {
ContentWriter contentWriter = new MongoContentWriter(
db, lookupStore(), persistenceAuditLog(),
playerResolver(), serviceResolver(), new SystemClock()
);
contentWriter = new EquivalenceWritingContentWriter(contentWriter, explicitLookupWriter());
if (messagingEnabled) {
contentWriter = new MessageQueueingContentWriter(
messenger(),
contentChanges(),
contentWriter,
contentResolver()
);
}
contentWriter = new IdSettingContentWriter(
contentWriter, lookupBackedContentIdGenerator()
);
return contentWriter;
}
@Override
public ContentWriter nonIdSettingContentWriter() {
ContentWriter contentWriter = new MongoContentWriter(
db, lookupStore(), persistenceAuditLog(),
playerResolver(), serviceResolver(), new SystemClock()
);
contentWriter = new EquivalenceWritingContentWriter(contentWriter, explicitLookupWriter());
if (messagingEnabled) {
contentWriter = new MessageQueueingContentWriter(
messenger(),
contentChanges(),
contentWriter,
contentResolver()
);
}
return contentWriter;
}
@Override
public ContentResolver contentResolver() {
return new LookupResolvingContentResolver(knownTypeContentResolver(), lookupStore());
}
public KnownTypeContentResolver knownTypeContentResolver() {
return new MongoContentResolver(db, lookupStore());
}
public MongoLookupEntryStore lookupStore() {
return new MongoLookupEntryStore(db.collection(LOOKUP),
persistenceAuditLog(), readPreference);
}
@Override
public LookupBackedContentIdGenerator lookupBackedContentIdGenerator() {
return new LookupBackedContentIdGenerator(lookupStore(), contentIdGenerator());
}
@Override
public IdGenerator contentIdGenerator() {
return new MongoSequentialIdGenerator(db, "content");
}
protected LookupWriter explicitLookupWriter() {
MongoLookupEntryStore entryStore = new MongoLookupEntryStore(db.collection("lookup"),
persistenceAuditLog(), ReadPreference.primary());
return TransitiveLookupWriter.explicitTransitiveLookupWriter(entryStore);
}
public LookupWriter generatedLookupWriter() {
MongoLookupEntryStore entryStore = new MongoLookupEntryStore(db.collection("lookup"),
persistenceAuditLog(), ReadPreference.primary());
return TransitiveLookupWriter.generatedTransitiveLookupWriter(entryStore);
}
/**
* We are passing in channel store here instead of initializing it like the other arguements
* is because the start() and stop() of CachingChannelStore has to be called pre-construction
* and post-destruction.
* So, we would like to use the singleton ChannelStore bean initialized in the
* Spring MongoContentPersistenceModule.
*/
public MongoScheduleStore scheduleStore(ChannelStore channelStore) {
try {
return new MongoScheduleStore(
db,
channelStore,
contentResolver(),
equivContentResolver(),
scheduleChanges()
);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public EquivalentContentResolver equivContentResolver() {
return new DefaultEquivalentContentResolver(knownTypeContentResolver(), lookupStore());
}
@Override
public ItemsPeopleWriter itemsPeopleWriter() {
return new QueuingItemsPeopleWriter(personWriter(), log);
}
public QueuingPersonWriter personWriter() {
return new QueuingPersonWriter(personStore(), log);
}
public PersonStore personStore() {
LookupEntryStore personLookupEntryStore = new MongoLookupEntryStore(
db.collection("peopleLookup"),
persistenceAuditLog(),
readPreference
);
PersonStore personStore = new MongoPersonStore(db,
TransitiveLookupWriter.explicitTransitiveLookupWriter(personLookupEntryStore),
personLookupEntryStore,
persistenceAuditLog());
//For now people occupy the same id space as content.
personStore = new IdSettingPersonStore(
personStore, new MongoSequentialIdGenerator(db, "content")
);
return personStore;
}
@Override
public ShortUrlSaver shortUrlSaver() {
return new MongoShortUrlSaver(db);
}
public MongoContentLister contentLister() {
return new MongoContentLister(db, knownTypeContentResolver());
}
@Override
public TopicStore topicStore() {
TopicStore store = new MongoTopicStore(db, persistenceAuditLog());
if (messagingEnabled) {
store = new MessageQueueingTopicWriter(topicChanges(), store);
}
return new TopicCreatingTopicResolver(store, new MongoSequentialIdGenerator(db, "topic"));
}
public ServiceResolver serviceResolver() {
return new CachingServiceResolver(new MongoServiceStore(db));
}
public PlayerResolver playerResolver() {
return new CachingPlayerResolver(new MongoPlayerStore(db));
}
@Override
public TopicQueryResolver topicQueryResolver() {
return new MongoTopicStore(db, persistenceAuditLog());
}
@Override
public SegmentWriter segmentWriter() {
return new IdSettingSegmentWriter(
new MongoSegmentWriter(db, new SubstitutionTableNumberCodec()),
segmentResolver(),
new MongoSequentialIdGenerator(db, "segment")
);
}
@Override
public SegmentResolver segmentResolver() {
return new MongoSegmentResolver(db, new SubstitutionTableNumberCodec());
}
/**
* @deprecated Use {@link MongoContentPersistenceModule#eventWriter()} and
* {@link MongoContentPersistenceModule#eventResolver()} instead
*/
@Deprecated
protected EventStore eventStore() {
return new EventStore() {
private EventWriter eventWriter = eventWriter();
private EventResolver eventResolver = eventResolver();
@Override public Optional<Event> fetch(Long id) {
return eventResolver.fetch(id);
}
@Override public Optional<Event> fetch(String uri) {
return eventResolver.fetch(uri);
}
@Override public Iterable<Event> fetch(Optional<Topic> eventGroup,
Optional<DateTime> from) {
return eventResolver.fetch(eventGroup, from);
}
@Override public Event createOrUpdate(Event event) {
return eventWriter.createOrUpdate(event);
}
};
}
public EventWriter eventWriter() {
IdSettingEventStore eventStore = new IdSettingEventStore(new MongoEventStore(db),
new MongoSequentialIdGenerator(db, "events"));
if(messagingEnabled) {
return new MessageQueueingEventWriter(eventStore, eventChanges());
}
return eventStore;
}
@Bean
public EventResolver eventResolver() {
return new MongoEventStore(db);
}
public OrganisationStore organisationStore() {
LookupEntryStore organisationLookupEntryStore = new MongoLookupEntryStore(
db.collection("organisationLookup"),
persistenceAuditLog(),
readPreference
);
OrganisationStore organisationStore = new MongoOrganisationStore(db,
TransitiveLookupWriter.explicitTransitiveLookupWriter(organisationLookupEntryStore),
organisationLookupEntryStore,
persistenceAuditLog());
organisationStore = new IdSettingOrganisationStore(
organisationStore, new MongoSequentialIdGenerator(db, "organisations")
);
if (messagingEnabled) {
organisationStore = new QueueingOrganisationStore(
organizationChanges(),
organisationStore
);
}
return organisationStore;
}
public ServiceChannelStore channelStore() {
if (processingConfig == null || !processingConfig.toBoolean()) {
return new CachingChannelStore(
new MongoChannelStore(db, channelGroupStore(), channelGroupStore())
);
}
return new MongoChannelStore(db, channelGroupStore(), channelGroupStore());
}
public ChannelGroupStore channelGroupStore() {
return new MongoChannelGroupStore(db);
}
@Override
public ProductStore productStore() {
return new IdSettingProductStore(
(ProductStore)productResolver(),
new MongoSequentialIdGenerator(db, "product")
);
}
@Override
public ProductResolver productResolver() {
return new MongoProductStore(db);
}
MongoIOProbe mongoIoSetProbe() {
return new MongoIOProbe(mongo).withWriteConcern(WriteConcern.REPLICAS_SAFE);
}
@Override
public PeopleQueryResolver peopleQueryResolver() {
return new EquivalatingPeopleResolver(
personStore(),
new MongoLookupEntryStore(
db.collection("peopleLookup"),
persistenceAuditLog(),
readPreference
)
);
}
public ContentPurger contentPurger() {
return new MongoContentPurger(contentLister(),
contentResolver(),
contentWriter(),
new MongoContentTables(db),
db.collection("lookup"),
explicitLookupWriter(),
generatedLookupWriter());
}
public PersistenceAuditLog persistenceAuditLog() {
if (auditEnabled) {
return new PerHourAndDayMongoPersistenceAuditLog(
new DatabasedMongo(mongo, auditDbName)
);
} else {
return new NoLoggingPersistenceAuditLog();
}
}
public MongoProgressStore mongoProgressStore() {
return new MongoProgressStore(db);
}
}