package com.celements.common.observation.converter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.annotation.Requirement;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.EntityReferenceResolver;
import org.xwiki.observation.event.Event;
import org.xwiki.observation.remote.LocalEventData;
import org.xwiki.observation.remote.RemoteEventData;
import com.google.common.base.Strings;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.internal.observation.remote.converter.AbstractXWikiEventConverter;
@Component("Annotation")
public class AnnotationEventConverter extends AbstractXWikiEventConverter {
private static Logger LOGGER = LoggerFactory.getLogger(AnnotationEventConverter.class);
public static final String CONTEXT_WIKI = "contextwiki";
public static final String CONTEXT_USER = "contextuser";
public static final String DOC_NAME = "docname";
public static final String DOC_PARENT = "docparent";
public static final String DOC_VERSION = "docversion";
public static final String DOC_LANGUAGE = "doclanguage";
public static final String ORIGDOC_PARENT = "origdocparent";
public static final String ORIGDOC_VERSION = "origdocversion";
public static final String ORIGDOC_LANGUAGE = "origdoclanguage";
private static final String CFG_SRC_KEY = "celements.observation.noAnnotationMissingWarning";
@Requirement("relative")
private EntityReferenceResolver<String> relativeRefResolver;
@Requirement
private ConfigurationSource configSrc;
@Override
public int getPriority() {
// default < this < SerializableEventConverter
return 1500;
}
@Override
public boolean toRemote(LocalEventData localEvent, RemoteEventData remoteEvent) {
Event event = localEvent.getEvent();
LOGGER.trace("toRemote: start for event '{}'", event.getClass());
if (shouldConvert(event.getClass())) {
LOGGER.trace("toRemote: serialize event '{}'", event.getClass());
remoteEvent.setEvent((Serializable) event);
remoteEvent.setSource(serializeSource(localEvent));
remoteEvent.setData(serializeData(localEvent));
return true;
} else {
LOGGER.debug("toRemote: skip event '{}'", event.getClass());
}
return false;
}
protected Serializable serializeSource(LocalEventData localEvent) {
Serializable ret = null;
Object source = localEvent.getSource();
if (source instanceof XWikiDocument) {
ret = serializeXWikiDocument((XWikiDocument) source);
} else {
ret = (Serializable) source;
}
return ret;
}
protected Serializable serializeData(LocalEventData localEvent) {
Serializable ret = null;
Object data = localEvent.getData();
if (data instanceof XWikiContext) {
ret = serializeXWikiContext((XWikiContext) localEvent.getData());
} else {
ret = (Serializable) data;
}
return ret;
}
@Override
public boolean fromRemote(RemoteEventData remoteEvent, LocalEventData localEvent) {
Event event = (Event) remoteEvent.getEvent();
LOGGER.trace("fromRemote: start for event '{}'", event.getClass());
if (shouldConvert(event.getClass())) {
LOGGER.trace("fromRemote: unserialize event '{}'", event.getClass());
localEvent.setEvent(event);
localEvent.setSource(unserializeSource(remoteEvent));
localEvent.setData(unserializeData(remoteEvent));
return true;
} else {
LOGGER.debug("fromRemote: skip event '{}'", event.getClass());
}
return false;
}
protected Object unserializeSource(RemoteEventData remoteEvent) {
Object source = remoteEvent.getSource();
if (source instanceof Map<?, ?>) {
source = unserializeDocument((Serializable) source);
}
return source;
}
protected Object unserializeData(RemoteEventData remoteEvent) {
Object data = remoteEvent.getData();
if (data instanceof Map<?, ?>) {
data = unserializeXWikiContext((Serializable) data);
}
return data;
}
boolean shouldConvert(Class<? extends Event> eventClass) {
if (eventClass.isAnnotationPresent(Remote.class)) {
return true;
} else {
logWarning(eventClass.getName());
return false;
}
}
private void logWarning(String eventClassName) {
for (Object field : configSrc.getProperty(CFG_SRC_KEY, List.class)) {
if (eventClassName.startsWith(field.toString())) {
return;
}
}
LOGGER.warn("Local/Remote Annotation missing on event class '{}'", eventClassName);
}
@Override
@SuppressWarnings({ "unchecked", "deprecation" })
protected Serializable serializeXWikiDocument(XWikiDocument document) {
HashMap<String, Serializable> remoteDataMap = (HashMap<String, Serializable>) super.serializeXWikiDocument(
document);
// having to use getParent() here instead of getParentReference()
// because the latter always returns an absolute document reference but we want to
// serialize the relative reference.
// getRelativeParentReference() is not (yet) public in 2.7.2
remoteDataMap.put(DOC_PARENT, document.getParent());
XWikiDocument originalDocument = document.getOriginalDocument();
if (originalDocument != null) {
remoteDataMap.put(ORIGDOC_PARENT, originalDocument.getParent());
}
return remoteDataMap;
}
@Override
@SuppressWarnings("unchecked")
protected XWikiDocument unserializeDocument(Serializable remoteData) {
XWikiDocument doc = super.unserializeDocument(remoteData);
Map<String, Serializable> remoteDataMap = (Map<String, Serializable>) remoteData;
String parentFN = (String) remoteDataMap.get(DOC_PARENT);
if (!Strings.isNullOrEmpty(parentFN)) {
doc.setParentReference(relativeRefResolver.resolve(parentFN, EntityType.DOCUMENT));
}
XWikiDocument origDoc = doc.getOriginalDocument();
if (origDoc != null) {
String origParentFN = (String) remoteDataMap.get(ORIGDOC_PARENT);
if (!Strings.isNullOrEmpty(origParentFN)) {
origDoc.setParentReference(relativeRefResolver.resolve(origParentFN, EntityType.DOCUMENT));
}
}
return doc;
}
}