package com.celements.common.cache;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Requirement;
import org.xwiki.context.Execution;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryManager;
import com.celements.query.IQueryExecutionServiceRole;
import com.celements.web.service.IWebUtilsService;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
public abstract class AbstractDocumentReferenceCache<K> implements IDocumentReferenceCache<K> {
private Map<WikiReference, Map<K, Set<DocumentReference>>> cache = new HashMap<>();
@Requirement
protected QueryManager queryManager;
@Requirement
protected IQueryExecutionServiceRole queryExecService;
@Requirement
protected IWebUtilsService webUtils;
@Requirement
protected Execution execution;
protected XWikiContext getContext() {
return (XWikiContext) execution.getContext().getProperty(XWikiContext.EXECUTIONCONTEXT_KEY);
}
@Override
public Set<DocumentReference> getCachedDocRefs(K key) throws CacheLoadingException {
WikiReference wikiRef = null;
if (key instanceof EntityReference) {
wikiRef = webUtils.getWikiRef((EntityReference) key);
}
return getCachedDocRefs(wikiRef, key);
}
@Override
public Set<DocumentReference> getCachedDocRefs(WikiReference wikiRef)
throws CacheLoadingException {
return ImmutableSet.copyOf(Iterables.concat(getCache(wikiRef).values()));
}
@Override
public Set<DocumentReference> getCachedDocRefs(WikiReference wikiRef, K key)
throws CacheLoadingException {
Set<DocumentReference> ret = getCache(wikiRef).get(key);
if (ret != null) {
ret = ImmutableSet.copyOf(ret);
} else {
ret = ImmutableSet.of();
}
return ret;
}
@Override
public synchronized void flush(WikiReference wikiRef) {
if (wikiRef != null) {
Map<K, Set<DocumentReference>> oldCache = cache.remove(wikiRef);
if (getLogger().isTraceEnabled()) {
getLogger().trace("flush: for wiki '{}': {}", wikiRef, oldCache);
} else {
getLogger().info("flush: for wiki '{}'", wikiRef);
}
}
}
private synchronized Map<K, Set<DocumentReference>> getCache(WikiReference wikiRef)
throws CacheLoadingException {
if (wikiRef == null) {
wikiRef = webUtils.getWikiRef();
}
if (!cache.containsKey(wikiRef)) {
try {
cache.put(wikiRef, loadCache(wikiRef));
} catch (QueryException | XWikiException exc) {
throw new CacheLoadingException(exc);
}
}
return cache.get(wikiRef);
}
private Map<K, Set<DocumentReference>> loadCache(WikiReference wikiRef) throws QueryException,
XWikiException {
getLogger().debug("loadCache: start for wiki '{}'", wikiRef);
Map<K, Set<DocumentReference>> cache = new HashMap<>();
for (DocumentReference docRef : executeXWQL(wikiRef)) {
for (K key : getKeysForResult(docRef)) {
if (!cache.containsKey(key)) {
cache.put(key, new HashSet<DocumentReference>());
}
cache.get(key).add(docRef);
getLogger().trace("loadCache: put '{}' - '{}'", key, docRef);
}
}
return cache;
}
List<DocumentReference> executeXWQL(WikiReference wikiRef) throws QueryException {
String xwql = "select distinct doc.fullName from Document doc, doc.object("
+ webUtils.serializeRef(getCacheClassRef(wikiRef), true) + ") as obj";
Query query = queryManager.createQuery(xwql, Query.XWQL);
query.setWiki(wikiRef.getName());
return queryExecService.executeAndGetDocRefs(query);
}
/**
* @return the class ref that documents will be cached for.
*/
protected abstract DocumentReference getCacheClassRef(WikiReference wikiRef);
/**
* @param docRef
* a result element
* @return the keys for the given result
* @throws XWikiException
*/
protected abstract Collection<K> getKeysForResult(DocumentReference docRef) throws XWikiException;
protected abstract Logger getLogger();
}