/* * (C) Copyright 2006-2007 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: * Nuxeo - initial API and implementation * * $Id$ */ package org.nuxeo.ecm.platform.relations.core.listener; import java.util.ArrayList; import java.util.Arrays; import java.util.List; 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.DocumentSecurityException; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.ecm.core.api.event.CoreEventConstants; import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; import org.nuxeo.ecm.core.event.Event; import org.nuxeo.ecm.core.event.EventContext; import org.nuxeo.ecm.core.event.EventListener; import org.nuxeo.ecm.core.event.impl.DocumentEventContext; import org.nuxeo.ecm.platform.relations.api.Graph; import org.nuxeo.ecm.platform.relations.api.Node; 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.util.RelationConstants; import org.nuxeo.runtime.api.Framework; /** * Core Event listener to copy relations affecting the source document to the proxy upon publication events and the * relations that were present on the replaced proxies if any. If this core event listener is used in combination with * another core event listener that cleans relation on deleted documents, it should be executed before the cleaning * listener so as to be able to copy relations from the deleted proxies. This core event listener cannot work in * asynchronous or post commit mode. * * @author ogrisel */ public class PublishRelationsListener implements EventListener { private static final Log log = LogFactory.getLog(PublishRelationsListener.class); public static final String RENDITION_PROXY_PUBLISHED = "renditionProxyPublished"; protected RelationManager rmanager; // Override to change the list of graphs to copy relations when a document // is published, set to null to copy relations from all graphs protected List<String> graphNamesForCopyFromWork = Arrays.asList(RelationConstants.GRAPH_NAME); protected List<String> graphNamesForCopyFromReplacedProxy = Arrays.asList(RelationConstants.GRAPH_NAME, "documentComments"); public RelationManager getRelationManager() { if (rmanager == null) { rmanager = Framework.getService(RelationManager.class); } return rmanager; } public List<String> getGraphNamesForCopyFromWork() { if (graphNamesForCopyFromWork == null) { return getRelationManager().getGraphNames(); } return graphNamesForCopyFromWork; } public List<String> getGraphNamesForCopyFromReplacedProxy() { if (graphNamesForCopyFromReplacedProxy == null) { return getRelationManager().getGraphNames(); } return graphNamesForCopyFromReplacedProxy; } public void handleEvent(Event event) { EventContext ctx = event.getContext(); if (ctx instanceof DocumentEventContext) { DocumentEventContext docCtx = (DocumentEventContext) ctx; DocumentModel publishedDoc = docCtx.getSourceDocument(); if (!publishedDoc.isProxy()) { // we are only interested in the publication of proxy documents return; } CoreSession session = ctx.getCoreSession(); RelationManager rmanager = getRelationManager(); Resource publishedResource = rmanager.getResource(RelationConstants.DOCUMENT_NAMESPACE, publishedDoc, null); Resource sourceResource = null; // Copy relations from working copy if not a rendition proxy if (!RENDITION_PROXY_PUBLISHED.equals(event.getName())) { try { // fetch the archived version the proxy is pointing to DocumentModel sourceDoc = session.getSourceDocument(publishedDoc.getRef()); // fetch the working version the archived version is coming // from sourceDoc = session.getSourceDocument(sourceDoc.getRef()); sourceResource = rmanager.getResource(RelationConstants.DOCUMENT_NAMESPACE, sourceDoc, null); // copy the relations from the working copy (the source // document getting published) copyRelationsFromWorkingCopy(rmanager, sourceResource, publishedResource); } catch (DocumentSecurityException e) { log.warn("working copy of the proxy is no longer available or not readable by the current user, cannot copy the source relations"); } } // Copy relations from replaced proxies @SuppressWarnings("unchecked") List<String> replacedProxyIds = (List<String>) ctx.getProperties().get( CoreEventConstants.REPLACED_PROXY_IDS); if (replacedProxyIds != null) { for (String replacedProxyId : replacedProxyIds) { DocumentLocationImpl docLoc = new DocumentLocationImpl(ctx.getRepositoryName(), new IdRef( replacedProxyId), null); Resource replacedResource = rmanager.getResource(RelationConstants.DOCUMENT_NAMESPACE, docLoc, null); copyRelationsFromReplacedProxy(rmanager, replacedResource, publishedResource, sourceResource); } } } } protected void copyRelationsFromReplacedProxy(RelationManager rmanager, Resource replacedResource, Resource publishedResource, Resource sourceResource) { for (String graphName : getGraphNamesForCopyFromReplacedProxy()) { Graph graph = rmanager.getGraphByName(graphName); // collect existing relations to or from the source resource List<Statement> newStatements = new ArrayList<Statement>(); for (Statement stmt : graph.getStatements(replacedResource, null, null)) { if (!isCopyFromSource(stmt, sourceResource)) { // do not copy previous relations that come from a // source // copy stmt.setSubject(publishedResource); newStatements.add(stmt); } } for (Statement stmt : graph.getStatements(null, null, replacedResource)) { if (!isCopyFromSource(stmt, sourceResource)) { // do not copy previous relations that come from a // source // copy stmt.setObject(publishedResource); newStatements.add(stmt); } } if (!newStatements.isEmpty()) { // add the rewritten statements on the proxy graph.add(newStatements); } } } protected boolean isCopyFromSource(Statement stmt, Resource sourceResource) { Node[] values = stmt.getProperties(RelationConstants.COPY_FROM_WORK_VERSION); if (values == null) { return false; } else { return Arrays.asList(values).contains(sourceResource); } } protected void copyRelationsFromWorkingCopy(RelationManager rmanager, Resource sourceResource, Resource publishedResource) { for (String graphName : getGraphNamesForCopyFromWork()) { Graph graph = rmanager.getGraphByName(graphName); // collect existing relations to or from the source document List<Statement> newStatements = new ArrayList<Statement>(); for (Statement stmt : graph.getStatements(sourceResource, null, null)) { stmt.setSubject(publishedResource); stmt.addProperty(RelationConstants.COPY_FROM_WORK_VERSION, sourceResource); newStatements.add(stmt); } for (Statement stmt : graph.getStatements(null, null, sourceResource)) { stmt.setObject(publishedResource); stmt.addProperty(RelationConstants.COPY_FROM_WORK_VERSION, sourceResource); newStatements.add(stmt); } if (!newStatements.isEmpty()) { // add the rewritten statements on the proxy graph.add(newStatements); } } } }