package org.atlasapi.media.channel; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.regex.Pattern; import java.util.stream.StreamSupport; import org.joda.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.metabroadcast.common.base.Maybe; import com.metabroadcast.common.caching.BackgroundComputingValue; import com.metabroadcast.common.persistence.mongo.MongoQueryBuilder; import com.metabroadcast.common.stream.MoreCollectors; import static org.atlasapi.persistence.media.entity.IdentifiedTranslator.IDS_NAMESPACE; import static org.atlasapi.persistence.media.entity.IdentifiedTranslator.IDS_VALUE; public class CachingChannelStore implements ChannelStore, ServiceChannelStore { private final ChannelStore delegate; private final BackgroundComputingValue<List<Channel>> channels; private final Logger log = LoggerFactory.getLogger(CachingChannelStore.class); public CachingChannelStore(ChannelStore delegate) { this.delegate = delegate; channels = new BackgroundComputingValue<List<Channel>>(Duration.standardMinutes(5), new ChannelsUpdater(delegate)); } @Override public void start() { channels.start(); } @Override public void shutdown() { channels.shutdown(); } @Override public Channel createOrUpdate(Channel channel) { return delegate.createOrUpdate(channel); } @Override @Deprecated public Maybe<Channel> fromKey(String key) { for (Channel channel : channels.get()) { if (channel.getKey().equals(key)) { return Maybe.just(channel); } } return Maybe.nothing(); } @Override public Maybe<Channel> fromId(long id) { for (Channel channel : channels.get()) { if (channel.getId().equals(id)) { return Maybe.just(channel); } } return Maybe.nothing(); } @Override public Maybe<Channel> fromUri(String uri) { for (Channel channel : channels.get()) { if (channel.getUri().equals(uri)) { return Maybe.just(channel); } } return Maybe.nothing(); } @Override public Iterable<Channel> forIds(final Iterable<Long> ids) { return Iterables.filter(channels.get(), new Predicate<Channel>() { @Override public boolean apply(Channel input) { return Iterables.contains(ids, input.getId()); } }); } @Override public Iterable<Channel> all() { return channels.get(); } @Override public Iterable<Channel> allChannels(ChannelQuery query) { return delegate.allChannels(query); } @Override public Maybe<Channel> forAlias(String alias) { for (Channel channel : channels.get()) { if (channel.getAliasUrls().contains(alias)) { return Maybe.just(channel); } } return Maybe.nothing(); } @Override public Map<String, Channel> forAliases(String aliasPrefix) { final Pattern prefixPattern = Pattern.compile(String.format("^%s", Pattern.quote(aliasPrefix))); Map<String, Channel> channelMap = Maps.newHashMap(); for (Channel channel : channels.get()) { for (String alias : Iterables.filter(channel.getAliasUrls(), Predicates.contains(prefixPattern))) { if (channelMap.get(alias) == null) { channelMap.put(alias, channel); } else { log.error("Duplicate alias " + alias + " on channels " + channel.getId() + " & " + channelMap.get(alias).getId()); } } } return ImmutableMap.copyOf(channelMap); } // this method fetches channels by its aliases that are stored as ids in Mongo @Override public Iterable<Channel> forKeyPairAlias(ChannelQuery channelQuery) { return delegate.forKeyPairAlias(channelQuery); } private static class ChannelsUpdater implements Callable<List<Channel>> { private final ChannelResolver delegate; public ChannelsUpdater(ChannelResolver delegate) { this.delegate = delegate; } @Override public List<Channel> call() throws Exception { return ImmutableList.copyOf(delegate.all()); } } }