package com.celements.lastChanged;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.annotation.Requirement;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.SpaceReference;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryManager;
import com.celements.model.access.IModelAccessFacade;
import com.celements.model.access.exception.DocumentAccessException;
import com.celements.web.service.IWebUtilsService;
import com.google.common.base.Strings;
import com.xpn.xwiki.doc.XWikiDocument;
/**
* TODO write unit tests
*/
@Component
public class LastChangedService implements ILastChangedRole {
private static Logger LOGGER = LoggerFactory.getLogger(LastChangedService.class);
@Requirement
IWebUtilsService webUtilsService;
@Requirement
QueryManager queryManager;
@Requirement
IModelAccessFacade modelAccess;
final Map<WikiReference, Date> lastUpdatedWikiCache = new ConcurrentHashMap<>();
final Map<SpaceReference, Date> lastUpdatedSpaceCache = new ConcurrentHashMap<>();
synchronized void invalidateCacheForSpaceRef(SpaceReference spaceRef) {
if (hasSpaceRestriction(spaceRef)) {
lastUpdatedSpaceCache.remove(spaceRef);
lastUpdatedWikiCache.remove(webUtilsService.getWikiRef(spaceRef));
}
}
@Override
public Date getLastUpdatedDate() {
return getLastUpdatedDate((SpaceReference) null);
}
@Override
public Date getLastUpdatedDate(List<SpaceReference> spaceRefList) {
if (spaceRefList.isEmpty()) {
return getLastUpdatedDate();
} else {
Date lastUpdatedDate = null;
for (SpaceReference spaceRef : spaceRefList) {
Date spaceLastUpdatedDate = getLastUpdatedDate(spaceRef);
if ((lastUpdatedDate == null) || lastUpdatedDate.before(spaceLastUpdatedDate)) {
lastUpdatedDate = spaceLastUpdatedDate;
}
}
return lastUpdatedDate;
}
}
@Override
public Date getLastUpdatedDate(SpaceReference spaceRef) {
Date date = null;
if (hasSpaceRestriction(spaceRef)) {
date = lastUpdatedSpaceCache.get(spaceRef);
} else {
date = lastUpdatedWikiCache.get(webUtilsService.getWikiRef());
}
if (date == null) {
date = internal_getLastChangeDate(spaceRef);
}
return date;
}
Date internal_getLastChangeDate(SpaceReference spaceRef) {
Date lastChangedDate = null;
List<Object[]> lastChangedDocuments = getLastChangedDocuments(1, spaceRef);
if (lastChangedDocuments.size() > 0) {
Object[] firstRow = lastChangedDocuments.get(0);
String lastChangedDocFN = firstRow[0].toString();
String lastChangedDocLang = "";
if (firstRow[1] != null) {
lastChangedDocLang = firstRow[1].toString();
}
DocumentReference lastChangedDocRef = webUtilsService.resolveDocumentReference(
lastChangedDocFN);
XWikiDocument lastChangedDoc;
try {
if (Strings.isNullOrEmpty(lastChangedDocLang)) {
lastChangedDocLang = webUtilsService.getDefaultLanguage(spaceRef);
}
lastChangedDoc = modelAccess.getDocument(lastChangedDocRef, lastChangedDocLang);
lastChangedDate = lastChangedDoc.getDate();
updateCachedDate(spaceRef, lastChangedDate, lastChangedDocRef);
} catch (DocumentAccessException exp) {
LOGGER.error("Failed to load last updated document '{}', '{}'.", lastChangedDocRef,
lastChangedDocLang, exp);
}
} else {
LOGGER.info("internal_getLastChangeDate: empty lastChangedDocuments list for" + " space '{}'",
spaceRef);
}
LOGGER.debug("internal_getLastChangeDate: return '{}' for space '{}'", lastChangedDate,
spaceRef);
return lastChangedDate;
}
synchronized void updateCachedDate(SpaceReference spaceRef, Date lastChangedDate,
DocumentReference lastChangedDocRef) {
if (lastChangedDate != null) {
if (hasSpaceRestriction(spaceRef)) {
lastUpdatedSpaceCache.put(spaceRef, lastChangedDate);
} else {
lastUpdatedWikiCache.put(webUtilsService.getWikiRef(), lastChangedDate);
lastUpdatedSpaceCache.put(lastChangedDocRef.getLastSpaceReference(), lastChangedDate);
}
}
}
@Override
public List<Object[]> getLastChangedDocuments(int numEntries) {
return getLastChangedDocuments(numEntries, "");
}
boolean hasSpaceRestriction(SpaceReference spaceRef) {
return (spaceRef != null) && !Strings.isNullOrEmpty(spaceRef.getName());
}
@Override
public List<Object[]> getLastChangedDocuments(int numEntries, SpaceReference spaceRef) {
if (hasSpaceRestriction(spaceRef)) {
return getLastChangedDocuments(numEntries, spaceRef.getName());
} else {
return getLastChangedDocuments(numEntries, "");
}
}
@Override
@Deprecated
public List<Object[]> getLastChangedDocuments(int numEntries, String space) {
String xwql = "select doc.fullName, doc.language from XWikiDocument doc";
boolean hasSpaceRestriction = (!"".equals(space));
if (hasSpaceRestriction) {
xwql = xwql + " where doc.space = :spaceName";
}
xwql = xwql + " order by doc.date desc";
Query query;
try {
query = queryManager.createQuery(xwql, Query.XWQL);
if (hasSpaceRestriction) {
query = query.bindValue("spaceName", space);
}
return query.setLimit(numEntries).execute();
} catch (QueryException exp) {
LOGGER.error("Failed to create whats-new query for space [" + space + "].", exp);
}
return Collections.emptyList();
}
}