/*
* (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* looping
*/
package org.nuxeo.ecm.platform.relations.services;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.event.CoreEventConstants;
import org.nuxeo.ecm.core.event.EventProducer;
import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
import org.nuxeo.ecm.platform.relations.api.DocumentRelationManager;
import org.nuxeo.ecm.platform.relations.api.Graph;
import org.nuxeo.ecm.platform.relations.api.Literal;
import org.nuxeo.ecm.platform.relations.api.Node;
import org.nuxeo.ecm.platform.relations.api.QNameResource;
import org.nuxeo.ecm.platform.relations.api.RelationManager;
import org.nuxeo.ecm.platform.relations.api.Resource;
import org.nuxeo.ecm.platform.relations.api.Statement;
import org.nuxeo.ecm.platform.relations.api.event.RelationEvents;
import org.nuxeo.ecm.platform.relations.api.exceptions.RelationAlreadyExistsException;
import org.nuxeo.ecm.platform.relations.api.impl.LiteralImpl;
import org.nuxeo.ecm.platform.relations.api.impl.RelationDate;
import org.nuxeo.ecm.platform.relations.api.impl.ResourceImpl;
import org.nuxeo.ecm.platform.relations.api.impl.StatementImpl;
import org.nuxeo.ecm.platform.relations.api.util.RelationConstants;
import org.nuxeo.runtime.api.Framework;
/**
* @since 5.9.2
*/
public class DocumentRelationService implements DocumentRelationManager {
private static final Log log = LogFactory.getLog(DocumentRelationService.class);
private RelationManager relationManager = null;
protected RelationManager getRelationManager() {
if (relationManager == null) {
relationManager = Framework.getLocalService(RelationManager.class);
}
return relationManager;
}
// for consistency for callers only
private static void putStatements(Map<String, Serializable> options, List<Statement> statements) {
options.put(RelationEvents.STATEMENTS_EVENT_KEY, (Serializable) statements);
}
private static void putStatements(Map<String, Serializable> options, Statement statement) {
List<Statement> statements = new LinkedList<Statement>();
statements.add(statement);
options.put(RelationEvents.STATEMENTS_EVENT_KEY, (Serializable) statements);
}
private QNameResource getNodeFromDocumentModel(DocumentModel model) {
return (QNameResource) getRelationManager().getResource(RelationConstants.DOCUMENT_NAMESPACE, model, null);
}
@Override
public void addRelation(CoreSession session, DocumentModel from, DocumentModel to, String predicate, boolean inverse)
{
addRelation(session, from, getNodeFromDocumentModel(to), predicate, inverse);
}
@Override
public void addRelation(CoreSession session, DocumentModel from, Node to, String predicate) {
addRelation(session, from, to, predicate, false);
}
@Override
public void addRelation(CoreSession session, DocumentModel from, Node to, String predicate, boolean inverse)
{
addRelation(session, from, to, predicate, inverse, false);
}
@Override
public void addRelation(CoreSession session, DocumentModel from, Node to, String predicate, boolean inverse,
boolean includeStatementsInEvents) {
addRelation(session, from, to, predicate, inverse, includeStatementsInEvents, null);
}
@Override
public void addRelation(CoreSession session, DocumentModel from, Node toResource, String predicate,
boolean inverse, boolean includeStatementsInEvents, String comment) {
Graph graph = getRelationManager().getGraph(RelationConstants.GRAPH_NAME, session);
QNameResource fromResource = getNodeFromDocumentModel(from);
Resource predicateResource = new ResourceImpl(predicate);
Statement stmt = null;
List<Statement> statements = null;
if (inverse) {
stmt = new StatementImpl(toResource, predicateResource, fromResource);
statements = graph.getStatements(toResource, predicateResource, fromResource);
if (statements != null && statements.size() > 0) {
throw new RelationAlreadyExistsException();
}
} else {
stmt = new StatementImpl(fromResource, predicateResource, toResource);
statements = graph.getStatements(fromResource, predicateResource, toResource);
if (statements != null && statements.size() > 0) {
throw new RelationAlreadyExistsException();
}
}
// Comment ?
if (!StringUtils.isEmpty(comment)) {
stmt.addProperty(RelationConstants.COMMENT, new LiteralImpl(comment));
}
Literal now = RelationDate.getLiteralDate(new Date());
if (stmt.getProperties(RelationConstants.CREATION_DATE) == null) {
stmt.addProperty(RelationConstants.CREATION_DATE, now);
}
if (stmt.getProperties(RelationConstants.MODIFICATION_DATE) == null) {
stmt.addProperty(RelationConstants.MODIFICATION_DATE, now);
}
if (session.getPrincipal() != null && stmt.getProperty(RelationConstants.AUTHOR) == null) {
stmt.addProperty(RelationConstants.AUTHOR, new LiteralImpl(session.getPrincipal().getName()));
}
// notifications
Map<String, Serializable> options = new HashMap<String, Serializable>();
String currentLifeCycleState = from.getCurrentLifeCycleState();
options.put(CoreEventConstants.DOC_LIFE_CYCLE, currentLifeCycleState);
if (includeStatementsInEvents) {
putStatements(options, stmt);
}
options.put(RelationEvents.GRAPH_NAME_EVENT_KEY, RelationConstants.GRAPH_NAME);
// before notification
notifyEvent(RelationEvents.BEFORE_RELATION_CREATION, from, options, comment, session);
// add statement
graph.add(stmt);
// XXX AT: try to refetch it from the graph so that resources are
// transformed into qname resources: useful for indexing
if (includeStatementsInEvents) {
putStatements(options, graph.getStatements(stmt));
}
// after notification
notifyEvent(RelationEvents.AFTER_RELATION_CREATION, from, options, comment, session);
}
protected void notifyEvent(String eventId, DocumentModel source, Map<String, Serializable> options, String comment,
CoreSession session) {
DocumentEventContext docCtx = new DocumentEventContext(session, session.getPrincipal(), source);
options.put("category", RelationEvents.CATEGORY);
options.put("comment", comment);
EventProducer evtProducer = Framework.getService(EventProducer.class);
evtProducer.fireEvent(docCtx.newEvent(eventId));
}
@Override
public void deleteRelation(CoreSession session, DocumentModel from, DocumentModel to, String predicate)
{
deleteRelation(session, from, to, predicate, false);
}
@Override
public void deleteRelation(CoreSession session, DocumentModel from, DocumentModel to, String predicate,
boolean includeStatementsInEvents) {
QNameResource fromResource = (QNameResource) getRelationManager().getResource(
RelationConstants.DOCUMENT_NAMESPACE, from, null);
QNameResource toResource = (QNameResource) getRelationManager().getResource(
RelationConstants.DOCUMENT_NAMESPACE, to, null);
Resource predicateResource = new ResourceImpl(predicate);
Graph graph = getRelationManager().getGraphByName(RelationConstants.GRAPH_NAME);
List<Statement> statements = graph.getStatements(fromResource, predicateResource, toResource);
if (statements == null || statements.size() == 0) {
// Silent ignore the deletion as it doesn't exist
return;
}
for (Statement stmt : statements) {
deleteRelation(session, stmt);
}
}
@Override
public void deleteRelation(CoreSession session, Statement stmt) {
deleteRelation(session, stmt, false);
}
@Override
public void deleteRelation(CoreSession session, Statement stmt, boolean includeStatementsInEvents)
{
// notifications
Map<String, Serializable> options = new HashMap<String, Serializable>();
// Find relative document
DocumentModel eventDocument = null;
if (stmt.getSubject() instanceof QNameResource) {
eventDocument = (DocumentModel) getRelationManager().getResourceRepresentation(
RelationConstants.DOCUMENT_NAMESPACE, (QNameResource) stmt.getSubject(), null);
} else if (stmt.getObject() instanceof QNameResource) {
eventDocument = (DocumentModel) getRelationManager().getResourceRepresentation(
RelationConstants.DOCUMENT_NAMESPACE, (QNameResource) stmt.getObject(), null);
}
// Complete event info and send first event
if (eventDocument != null) {
String currentLifeCycleState = eventDocument.getCurrentLifeCycleState();
options.put(CoreEventConstants.DOC_LIFE_CYCLE, currentLifeCycleState);
options.put(RelationEvents.GRAPH_NAME_EVENT_KEY, RelationConstants.GRAPH_NAME);
if (includeStatementsInEvents) {
putStatements(options, stmt);
}
// before notification
notifyEvent(RelationEvents.BEFORE_RELATION_REMOVAL, eventDocument, options, null, session);
}
// remove statement
getRelationManager().getGraphByName(RelationConstants.GRAPH_NAME).remove(stmt);
if (eventDocument != null) {
// after notification
notifyEvent(RelationEvents.AFTER_RELATION_REMOVAL, eventDocument, options, null, session);
}
}
}