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