001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.platform.relations.core.listener; 021 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.List; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.ecm.core.api.CoreSession; 029import org.nuxeo.ecm.core.api.DocumentModel; 030import org.nuxeo.ecm.core.api.DocumentSecurityException; 031import org.nuxeo.ecm.core.api.IdRef; 032import org.nuxeo.ecm.core.api.event.CoreEventConstants; 033import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 034import org.nuxeo.ecm.core.event.Event; 035import org.nuxeo.ecm.core.event.EventContext; 036import org.nuxeo.ecm.core.event.EventListener; 037import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 038import org.nuxeo.ecm.platform.relations.api.Graph; 039import org.nuxeo.ecm.platform.relations.api.Node; 040import org.nuxeo.ecm.platform.relations.api.RelationManager; 041import org.nuxeo.ecm.platform.relations.api.Resource; 042import org.nuxeo.ecm.platform.relations.api.Statement; 043import org.nuxeo.ecm.platform.relations.api.util.RelationConstants; 044import org.nuxeo.runtime.api.Framework; 045 046/** 047 * Core Event listener to copy relations affecting the source document to the proxy upon publication events and the 048 * relations that were present on the replaced proxies if any. If this core event listener is used in combination with 049 * another core event listener that cleans relation on deleted documents, it should be executed before the cleaning 050 * listener so as to be able to copy relations from the deleted proxies. This core event listener cannot work in 051 * asynchronous or post commit mode. 052 * 053 * @author ogrisel 054 */ 055public class PublishRelationsListener implements EventListener { 056 057 private static final Log log = LogFactory.getLog(PublishRelationsListener.class); 058 059 public static final String RENDITION_PROXY_PUBLISHED = "renditionProxyPublished"; 060 061 protected RelationManager rmanager; 062 063 // Override to change the list of graphs to copy relations when a document 064 // is published, set to null to copy relations from all graphs 065 066 protected List<String> graphNamesForCopyFromWork = Arrays.asList(RelationConstants.GRAPH_NAME); 067 068 protected List<String> graphNamesForCopyFromReplacedProxy = Arrays.asList(RelationConstants.GRAPH_NAME, 069 "documentComments"); 070 071 public RelationManager getRelationManager() { 072 if (rmanager == null) { 073 rmanager = Framework.getService(RelationManager.class); 074 } 075 return rmanager; 076 } 077 078 public List<String> getGraphNamesForCopyFromWork() { 079 if (graphNamesForCopyFromWork == null) { 080 return getRelationManager().getGraphNames(); 081 } 082 return graphNamesForCopyFromWork; 083 } 084 085 public List<String> getGraphNamesForCopyFromReplacedProxy() { 086 if (graphNamesForCopyFromReplacedProxy == null) { 087 return getRelationManager().getGraphNames(); 088 } 089 return graphNamesForCopyFromReplacedProxy; 090 } 091 092 public void handleEvent(Event event) { 093 EventContext ctx = event.getContext(); 094 095 if (ctx instanceof DocumentEventContext) { 096 DocumentEventContext docCtx = (DocumentEventContext) ctx; 097 DocumentModel publishedDoc = docCtx.getSourceDocument(); 098 if (!publishedDoc.isProxy()) { 099 // we are only interested in the publication of proxy documents 100 return; 101 } 102 CoreSession session = ctx.getCoreSession(); 103 RelationManager rmanager = getRelationManager(); 104 105 Resource publishedResource = rmanager.getResource(RelationConstants.DOCUMENT_NAMESPACE, publishedDoc, null); 106 Resource sourceResource = null; 107 108 // Copy relations from working copy if not a rendition proxy 109 if (!RENDITION_PROXY_PUBLISHED.equals(event.getName())) { 110 try { 111 // fetch the archived version the proxy is pointing to 112 DocumentModel sourceDoc = session.getSourceDocument(publishedDoc.getRef()); 113 114 // fetch the working version the archived version is coming 115 // from 116 sourceDoc = session.getSourceDocument(sourceDoc.getRef()); 117 118 sourceResource = rmanager.getResource(RelationConstants.DOCUMENT_NAMESPACE, sourceDoc, null); 119 120 // copy the relations from the working copy (the source 121 // document getting published) 122 copyRelationsFromWorkingCopy(rmanager, sourceResource, publishedResource); 123 } catch (DocumentSecurityException e) { 124 log.warn("working copy of the proxy is no longer available or not readable by the current user, cannot copy the source relations"); 125 } 126 } 127 128 // Copy relations from replaced proxies 129 @SuppressWarnings("unchecked") 130 List<String> replacedProxyIds = (List<String>) ctx.getProperties().get( 131 CoreEventConstants.REPLACED_PROXY_IDS); 132 if (replacedProxyIds != null) { 133 for (String replacedProxyId : replacedProxyIds) { 134 DocumentLocationImpl docLoc = new DocumentLocationImpl(ctx.getRepositoryName(), new IdRef( 135 replacedProxyId), null); 136 Resource replacedResource = rmanager.getResource(RelationConstants.DOCUMENT_NAMESPACE, docLoc, null); 137 copyRelationsFromReplacedProxy(rmanager, replacedResource, publishedResource, sourceResource); 138 } 139 } 140 } 141 } 142 143 protected void copyRelationsFromReplacedProxy(RelationManager rmanager, Resource replacedResource, 144 Resource publishedResource, Resource sourceResource) { 145 for (String graphName : getGraphNamesForCopyFromReplacedProxy()) { 146 Graph graph = rmanager.getGraphByName(graphName); 147 148 // collect existing relations to or from the source resource 149 List<Statement> newStatements = new ArrayList<Statement>(); 150 for (Statement stmt : graph.getStatements(replacedResource, null, null)) { 151 if (!isCopyFromSource(stmt, sourceResource)) { 152 // do not copy previous relations that come from a 153 // source 154 // copy 155 stmt.setSubject(publishedResource); 156 newStatements.add(stmt); 157 } 158 } 159 for (Statement stmt : graph.getStatements(null, null, replacedResource)) { 160 if (!isCopyFromSource(stmt, sourceResource)) { 161 // do not copy previous relations that come from a 162 // source 163 // copy 164 stmt.setObject(publishedResource); 165 newStatements.add(stmt); 166 } 167 } 168 if (!newStatements.isEmpty()) { 169 // add the rewritten statements on the proxy 170 graph.add(newStatements); 171 } 172 } 173 } 174 175 protected boolean isCopyFromSource(Statement stmt, Resource sourceResource) { 176 Node[] values = stmt.getProperties(RelationConstants.COPY_FROM_WORK_VERSION); 177 if (values == null) { 178 return false; 179 } else { 180 return Arrays.asList(values).contains(sourceResource); 181 } 182 } 183 184 protected void copyRelationsFromWorkingCopy(RelationManager rmanager, Resource sourceResource, 185 Resource publishedResource) { 186 for (String graphName : getGraphNamesForCopyFromWork()) { 187 Graph graph = rmanager.getGraphByName(graphName); 188 189 // collect existing relations to or from the source document 190 List<Statement> newStatements = new ArrayList<Statement>(); 191 for (Statement stmt : graph.getStatements(sourceResource, null, null)) { 192 stmt.setSubject(publishedResource); 193 stmt.addProperty(RelationConstants.COPY_FROM_WORK_VERSION, sourceResource); 194 newStatements.add(stmt); 195 } 196 for (Statement stmt : graph.getStatements(null, null, sourceResource)) { 197 stmt.setObject(publishedResource); 198 stmt.addProperty(RelationConstants.COPY_FROM_WORK_VERSION, sourceResource); 199 newStatements.add(stmt); 200 } 201 202 if (!newStatements.isEmpty()) { 203 // add the rewritten statements on the proxy 204 graph.add(newStatements); 205 } 206 } 207 } 208}