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