001/* 002 * Copyright (c) 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Nuxeo - initial API and implementation 011 * 012 * $Id$ 013 */ 014 015package org.nuxeo.ecm.core.event.impl; 016 017import java.io.Serializable; 018import java.rmi.dgc.VMID; 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import javax.security.auth.login.LoginContext; 027import javax.security.auth.login.LoginException; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.ecm.core.api.CoreInstance; 032import org.nuxeo.ecm.core.api.CoreSession; 033import org.nuxeo.ecm.core.api.DocumentModel; 034import org.nuxeo.ecm.core.api.DocumentNotFoundException; 035import org.nuxeo.ecm.core.api.DocumentRef; 036import org.nuxeo.ecm.core.event.DeletedDocumentModel; 037import org.nuxeo.ecm.core.event.Event; 038import org.nuxeo.ecm.core.event.EventBundle; 039import org.nuxeo.ecm.core.event.EventContext; 040import org.nuxeo.ecm.core.event.ReconnectedEventBundle; 041import org.nuxeo.runtime.api.Framework; 042 043/** 044 * Default implementation for an {@link EventBundle} that need to be reconnected to a usable Session. 045 * 046 * @author tiry 047 */ 048public class ReconnectedEventBundleImpl implements ReconnectedEventBundle { 049 050 private static final long serialVersionUID = 1L; 051 052 protected EventBundle sourceEventBundle; 053 054 /** Lister name or names. */ 055 protected String listenerName; 056 057 protected List<Event> reconnectedEvents; 058 059 protected LoginContext loginCtx; 060 061 protected CoreSession reconnectedCoreSession; 062 063 private static final Log log = LogFactory.getLog(ReconnectedEventBundleImpl.class); 064 065 protected ReconnectedEventBundleImpl() { 066 } 067 068 public ReconnectedEventBundleImpl(EventBundle sourceEventBundle) { 069 this.sourceEventBundle = sourceEventBundle; 070 } 071 072 /** @since 5.6 */ 073 public ReconnectedEventBundleImpl(EventBundle sourceEventBundle, String listenerName) { 074 this.sourceEventBundle = sourceEventBundle; 075 this.listenerName = listenerName; 076 } 077 078 protected CoreSession getReconnectedCoreSession(String repoName) { 079 if (reconnectedCoreSession == null) { 080 try { 081 loginCtx = Framework.login(); 082 } catch (LoginException e) { 083 log.error("Cannot log in", e); 084 return null; 085 } 086 reconnectedCoreSession = CoreInstance.openCoreSessionSystem(repoName); 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<Event>(); 101 for (Event event : sourceEventBundle) { 102 EventContext ctx = event.getContext(); 103 CoreSession session = ctx.getRepositoryName() == null ? null 104 : getReconnectedCoreSession(ctx.getRepositoryName()); 105 106 List<Object> newArgs = new ArrayList<Object>(); 107 for (Object arg : ctx.getArguments()) { 108 Object newArg = arg; 109 if (refetchDocumentModel(session, arg) && session.getPrincipal() != null) { 110 DocumentModel oldDoc = (DocumentModel) arg; 111 DocumentRef ref = oldDoc.getRef(); 112 if (ref != null) { 113 try { 114 if (session.exists(oldDoc.getRef())) { 115 newArg = session.getDocument(oldDoc.getRef()); 116 } else { 117 // probably deleted doc 118 newArg = new DeletedDocumentModel(oldDoc); 119 } 120 } catch (DocumentNotFoundException e) { 121 log.error("Can not refetch Doc with ref " + ref.toString(), e); 122 } 123 } 124 } 125 // XXX treat here other cases !!!! 126 newArgs.add(newArg); 127 } 128 129 EventContext newCtx = null; 130 if (ctx instanceof DocumentEventContext) { 131 newCtx = new DocumentEventContext(session, ctx.getPrincipal(), (DocumentModel) newArgs.get(0), 132 (DocumentRef) newArgs.get(1)); 133 } else { 134 newCtx = new EventContextImpl(session, ctx.getPrincipal()); 135 ((EventContextImpl) newCtx).setArgs(newArgs.toArray()); 136 } 137 138 Map<String, Serializable> newProps = new HashMap<String, Serializable>(); 139 for (Entry<String, Serializable> prop : ctx.getProperties().entrySet()) { 140 Serializable propValue = prop.getValue(); 141 if (refetchDocumentModel(session, propValue)) { 142 DocumentModel oldDoc = (DocumentModel) propValue; 143 DocumentRef oldRef = oldDoc.getRef(); 144 try { 145 if (session.exists(oldRef)) { 146 propValue = session.getDocument(oldRef); 147 } else { 148 log.warn("Listener " + (listenerName == null ? "" : "'" + listenerName + "' ") 149 + "cannot refetch missing document: " + oldRef + " (" 150 + oldDoc.getPathAsString() + ")"); 151 } 152 } catch (DocumentNotFoundException e) { 153 log.error("Can not refetch Doc with ref " + oldRef, e); 154 } 155 } 156 // XXX treat here other cases !!!! 157 newProps.put(prop.getKey(), propValue); 158 } 159 newCtx.setProperties(newProps); 160 Event newEvt = new EventImpl(event.getName(), newCtx, event.getFlags(), event.getTime()); 161 reconnectedEvents.add(newEvt); 162 } 163 } 164 return reconnectedEvents; 165 } 166 167 protected boolean refetchDocumentModel(CoreSession session, Object eventProperty) { 168 if (eventProperty instanceof DocumentModel && session != null) { 169 DocumentModel doc = (DocumentModel) eventProperty; 170 if (Boolean.TRUE.equals(doc.getContextData(SKIP_REFETCH_DOCUMENT_CONTEXT_KEY))) { 171 return false; 172 } 173 return true; 174 } 175 return false; 176 } 177 178 @Override 179 public String getName() { 180 return sourceEventBundle.getName(); 181 } 182 183 @Override 184 public VMID getSourceVMID() { 185 return sourceEventBundle.getSourceVMID(); 186 } 187 188 @Override 189 public boolean hasRemoteSource() { 190 return sourceEventBundle.hasRemoteSource(); 191 } 192 193 @Override 194 public boolean isEmpty() { 195 return sourceEventBundle.isEmpty(); 196 } 197 198 @Override 199 public Event peek() { 200 return getReconnectedEvents().get(0); 201 } 202 203 @Override 204 public void push(Event event) { 205 throw new UnsupportedOperationException(); 206 } 207 208 @Override 209 public int size() { 210 return sourceEventBundle.size(); 211 } 212 213 @Override 214 public Iterator<Event> iterator() { 215 return getReconnectedEvents().iterator(); 216 } 217 218 @Override 219 public void disconnect() { 220 if (reconnectedCoreSession != null) { 221 reconnectedCoreSession.close(); 222 } 223 reconnectedCoreSession = null; 224 reconnectedEvents = null; 225 if (loginCtx != null) { 226 try { 227 loginCtx.logout(); 228 } catch (LoginException e) { 229 log.error("Cannot log out", e); 230 } finally { 231 loginCtx = null; 232 } 233 } 234 } 235 236 @Override 237 public boolean comesFromJMS() { 238 return false; 239 } 240 241 @Override 242 public boolean containsEventName(String eventName) { 243 return sourceEventBundle.containsEventName(eventName); 244 } 245 246 public List<String> getEventNames() { 247 List<String> eventNames = new ArrayList<String>(); 248 for (Event event : sourceEventBundle) { 249 eventNames.add(event.getName()); 250 } 251 return eventNames; 252 } 253}