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