001/* 002 * (C) Copyright 2006-2013 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.core.event.impl; 023 024import java.io.Serializable; 025import java.rmi.dgc.VMID; 026import java.util.ArrayList; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Map; 031import java.util.Map.Entry; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.ecm.core.api.CoreInstance; 036import org.nuxeo.ecm.core.api.CoreSession; 037import org.nuxeo.ecm.core.api.DocumentModel; 038import org.nuxeo.ecm.core.api.DocumentNotFoundException; 039import org.nuxeo.ecm.core.api.DocumentRef; 040import org.nuxeo.ecm.core.event.DeletedDocumentModel; 041import org.nuxeo.ecm.core.event.Event; 042import org.nuxeo.ecm.core.event.EventBundle; 043import org.nuxeo.ecm.core.event.EventContext; 044import org.nuxeo.ecm.core.event.ReconnectedEventBundle; 045import org.nuxeo.runtime.api.Framework; 046import org.nuxeo.runtime.api.login.NuxeoLoginContext; 047 048/** 049 * Default implementation for an {@link EventBundle} that need to be reconnected to a usable Session. 050 * 051 * @author tiry 052 */ 053public class ReconnectedEventBundleImpl implements ReconnectedEventBundle { 054 055 private static final long serialVersionUID = 1L; 056 057 protected EventBundle sourceEventBundle; 058 059 /** Lister name or names. */ 060 protected String listenerName; 061 062 protected transient List<Event> reconnectedEvents; 063 064 protected transient NuxeoLoginContext loginCtx; 065 066 protected transient CoreSession reconnectedCoreSession; 067 068 private static final Log log = LogFactory.getLog(ReconnectedEventBundleImpl.class); 069 070 protected ReconnectedEventBundleImpl() { 071 } 072 073 public ReconnectedEventBundleImpl(EventBundle sourceEventBundle) { 074 this.sourceEventBundle = sourceEventBundle; 075 } 076 077 /** @since 5.6 */ 078 public ReconnectedEventBundleImpl(EventBundle sourceEventBundle, String listenerName) { 079 this.sourceEventBundle = sourceEventBundle; 080 this.listenerName = listenerName; 081 } 082 083 protected CoreSession getReconnectedCoreSession(String repoName, String originatingUsername) { 084 if (reconnectedCoreSession == null) { 085 loginCtx = Framework.loginSystem(); 086 reconnectedCoreSession = CoreInstance.getCoreSessionSystem(repoName, originatingUsername); 087 } else { 088 // Sanity Check 089 if (!reconnectedCoreSession.getRepositoryName().equals(repoName)) { 090 if (repoName != null) { 091 throw new IllegalStateException("Can no reconnected a Bundle tied to several Core instances !"); 092 } 093 } 094 } 095 return reconnectedCoreSession; 096 } 097 098 protected List<Event> getReconnectedEvents() { 099 if (reconnectedEvents == null) { 100 reconnectedEvents = new ArrayList<>(); 101 for (Event event : sourceEventBundle) { 102 EventContext ctx = event.getContext(); 103 String repositoryName = ctx.getRepositoryName(); 104 CoreSession session; 105 if (repositoryName == null) { 106 session = null; 107 } else { 108 String originatingUsername = ctx.getPrincipal().getActingUser(); 109 session = getReconnectedCoreSession(repositoryName, originatingUsername); 110 } 111 112 List<Object> newArgs = new ArrayList<>(); 113 for (Object arg : ctx.getArguments()) { 114 Object newArg = arg; 115 if (refetchDocumentModel(session, arg) && session.getPrincipal() != null) { // NOSONAR 116 DocumentModel oldDoc = (DocumentModel) arg; 117 DocumentRef ref = oldDoc.getRef(); 118 if (ref != null) { 119 try { 120 if (session.exists(oldDoc.getRef())) { 121 newArg = session.getDocument(oldDoc.getRef()); 122 } else { 123 // probably deleted doc 124 newArg = new DeletedDocumentModel(oldDoc); 125 } 126 } catch (DocumentNotFoundException e) { 127 log.error("Can not refetch Doc with ref " + ref.toString(), e); 128 } 129 } 130 } 131 // XXX treat here other cases !!!! 132 newArgs.add(newArg); 133 } 134 135 EventContext newCtx = null; 136 if (ctx instanceof DocumentEventContext) { 137 newCtx = new DocumentEventContext(session, ctx.getPrincipal(), (DocumentModel) newArgs.get(0), 138 (DocumentRef) newArgs.get(1)); 139 } else { 140 newCtx = new EventContextImpl(session, ctx.getPrincipal()); 141 ((EventContextImpl) newCtx).setArgs(newArgs.toArray()); 142 } 143 144 Map<String, Serializable> newProps = new HashMap<>(); 145 for (Entry<String, Serializable> prop : ctx.getProperties().entrySet()) { 146 Serializable propValue = prop.getValue(); 147 if (refetchDocumentModel(session, propValue)) { 148 DocumentModel oldDoc = (DocumentModel) propValue; 149 DocumentRef oldRef = oldDoc.getRef(); 150 try { 151 if (session.exists(oldRef)) { // NOSONAR 152 propValue = session.getDocument(oldRef); 153 } else { 154 log.warn("Listener " + (listenerName == null ? "" : "'" + listenerName + "' ") 155 + "cannot refetch missing document: " + oldRef + " (" 156 + oldDoc.getPathAsString() + ")"); 157 } 158 } catch (DocumentNotFoundException e) { 159 log.error("Can not refetch Doc with ref " + oldRef, e); 160 } 161 } 162 // XXX treat here other cases !!!! 163 newProps.put(prop.getKey(), propValue); 164 } 165 newCtx.setProperties(newProps); 166 Event newEvt = new EventImpl(event.getName(), newCtx, event.getFlags(), event.getTime()); 167 reconnectedEvents.add(newEvt); 168 } 169 } 170 return reconnectedEvents; 171 } 172 173 protected boolean refetchDocumentModel(CoreSession session, Object eventProperty) { 174 if (eventProperty instanceof DocumentModel && session != null) { 175 DocumentModel doc = (DocumentModel) eventProperty; 176 if (Boolean.TRUE.equals(doc.getContextData(SKIP_REFETCH_DOCUMENT_CONTEXT_KEY))) { 177 return false; 178 } 179 return true; 180 } 181 return false; 182 } 183 184 @Override 185 public String getName() { 186 return sourceEventBundle.getName(); 187 } 188 189 @Override 190 public VMID getSourceVMID() { 191 return sourceEventBundle.getSourceVMID(); 192 } 193 194 @Override 195 public boolean hasRemoteSource() { 196 return sourceEventBundle.hasRemoteSource(); 197 } 198 199 @Override 200 public boolean isEmpty() { 201 return sourceEventBundle.isEmpty(); 202 } 203 204 @Override 205 public Event peek() { 206 return getReconnectedEvents().get(0); 207 } 208 209 @Override 210 public void push(Event event) { 211 throw new UnsupportedOperationException(); 212 } 213 214 @Override 215 public int size() { 216 return sourceEventBundle.size(); 217 } 218 219 @Override 220 public Iterator<Event> iterator() { 221 return getReconnectedEvents().iterator(); 222 } 223 224 @Override 225 public void disconnect() { 226 reconnectedCoreSession = null; 227 reconnectedEvents = null; 228 if (loginCtx != null) { 229 loginCtx.close(); 230 loginCtx = null; 231 } 232 } 233 234 @Override 235 public boolean containsEventName(String eventName) { 236 return sourceEventBundle.containsEventName(eventName); 237 } 238 239 public List<String> getEventNames() { 240 List<String> eventNames = new ArrayList<>(); 241 for (Event event : sourceEventBundle) { 242 eventNames.add(event.getName()); 243 } 244 return eventNames; 245 } 246}