001/* 002 * (C) Copyright 2006-2011 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 * Thierry Delprat 018 */ 019 020package org.nuxeo.ecm.core.event.jms; 021 022import java.io.Serializable; 023import java.rmi.dgc.VMID; 024import java.security.Principal; 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.common.utils.Path; 033import org.nuxeo.ecm.core.api.CoreSession; 034import org.nuxeo.ecm.core.api.DocumentModel; 035import org.nuxeo.ecm.core.api.DocumentRef; 036import org.nuxeo.ecm.core.api.IdRef; 037import org.nuxeo.ecm.core.api.NuxeoException; 038import org.nuxeo.ecm.core.api.PathRef; 039import org.nuxeo.ecm.core.api.SimplePrincipal; 040import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; 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.impl.DocumentEventContext; 045import org.nuxeo.ecm.core.event.impl.EventBundleImpl; 046import org.nuxeo.ecm.core.event.impl.EventContextImpl; 047import org.nuxeo.ecm.core.event.impl.EventImpl; 048 049/** 050 * Serializable representation of an {@link EventBundle} that is used for JMS forwarding. 051 * 052 * @author Thierry Delprat 053 */ 054public class SerializableEventBundle implements Serializable { 055 056 private static final long serialVersionUID = 1L; 057 058 private static final Log log = LogFactory.getLog(SerializableEventBundle.class); 059 060 protected final List<Map<String, Serializable>> serialisableEvents; 061 062 protected final String eventBundleName; 063 064 protected final VMID sourceVMID; 065 066 protected boolean isDocumentEventContext = false; 067 068 protected String coreInstanceName; 069 070 public SerializableEventBundle(EventBundle events) { 071 eventBundleName = events.getName(); 072 sourceVMID = events.getSourceVMID(); 073 serialisableEvents = new ArrayList<Map<String, Serializable>>(); 074 075 for (Event event : events) { 076 if (event.isLocal()) { 077 // local event should not be exported to JMS 078 continue; 079 } 080 CoreSession evtSession = event.getContext().getCoreSession(); 081 082 String repoName = null; 083 if (evtSession != null) { 084 repoName = evtSession.getRepositoryName(); 085 if (coreInstanceName == null) { 086 coreInstanceName = repoName; 087 } 088 } 089 090 Map<String, Serializable> serializableEvent = new HashMap<String, Serializable>(); 091 092 serializableEvent.put("name", event.getName()); 093 serializableEvent.put("time", Long.toString(event.getTime())); 094 serializableEvent.put("contextProperties", (Serializable) event.getContext().getProperties()); 095 if (evtSession != null) { 096 serializableEvent.put("contextSessionId", evtSession.getSessionId()); 097 } 098 serializableEvent.put("principal", event.getContext().getPrincipal().getName()); 099 100 serializableEvent.put("contextSessionRepositoryName", repoName); 101 102 if (event.getContext() instanceof DocumentEventContext) { 103 serializableEvent.put("isDocumentEventContext", true); 104 } else { 105 serializableEvent.put("isDocumentEventContext", false); 106 } 107 108 Object[] args = event.getContext().getArguments(); 109 List<Serializable> listArgs = new ArrayList<Serializable>(); 110 for (Object arg : args) { 111 if (arg instanceof DocumentModel) { 112 DocumentModel doc = (DocumentModel) arg; 113 String strRepresentation = doc.getRepositoryName() + ":" + doc.getId() + ":" + doc.getType() + ":" 114 + doc.getPathAsString(); 115 listArgs.add("DOCREF:" + strRepresentation); 116 } else if (arg instanceof Serializable) { 117 log.debug("Adding serializable argument of class " + arg.getClass().getCanonicalName()); 118 listArgs.add((Serializable) arg); 119 } else { 120 listArgs.add(null); 121 } 122 } 123 124 serializableEvent.put("args", (Serializable) listArgs); 125 serialisableEvents.add(serializableEvent); 126 } 127 } 128 129 // Should not be necessary since this is noww done in CoreSession 130 protected Map<String, Serializable> filterContextProperties(Map<String, Serializable> properties) { 131 Map<String, Serializable> serializableProps = new HashMap<String, Serializable>(); 132 133 for (String key : properties.keySet()) { 134 Object value = properties.get(key); 135 if (value instanceof Serializable) { 136 Serializable serializableValue = (Serializable) value; 137 serializableProps.put(key, serializableValue); 138 } else { 139 log.error("ContextMap contains non serializable object under key " + key); 140 } 141 } 142 return serializableProps; 143 } 144 145 public VMID getSourceVMID() { 146 return sourceVMID; 147 } 148 149 public String getEventBundleName() { 150 return eventBundleName; 151 } 152 153 public String getCoreInstanceName() { 154 return coreInstanceName; 155 } 156 157 public class EventBundleRelayedViaJMS extends EventBundleImpl { 158 private static final long serialVersionUID = 1L; 159 160 public EventBundleRelayedViaJMS() { 161 // init VMID 162 super(sourceVMID); 163 } 164 } 165 166 @SuppressWarnings("unchecked") 167 public EventBundle reconstructEventBundle(CoreSession session) throws CannotReconstruct { 168 169 if (!session.getRepositoryName().equals(coreInstanceName)) { 170 throw new CannotReconstruct("This session can not be used on this Bundle"); 171 } 172 EventBundle bundle = new EventBundleRelayedViaJMS(); 173 174 if (serialisableEvents == null) { 175 return null; 176 } 177 178 for (Map<String, Serializable> evt : serialisableEvents) { 179 180 String eventName = (String) evt.get("name"); 181 Long time = Long.parseLong((String) evt.get("time")); 182 183 Map<String, Serializable> ctxProperties = (Map<String, Serializable>) evt.get("contextProperties"); 184 Principal principal = new SimplePrincipal((String) evt.get("principal")); 185 186 List<Serializable> listArgs = (List<Serializable>) evt.get("args"); 187 188 Object[] args = new Object[listArgs.size()]; 189 190 int idx = 0; 191 for (Serializable sArg : listArgs) { 192 Object value; 193 if (sArg == null) { 194 value = null; 195 } else if (sArg instanceof String) { 196 String arg = (String) sArg; 197 if (arg.startsWith("DOCREF:")) { 198 String[] part = arg.split(":"); 199 DocumentRef idRef = new IdRef(part[2]); 200 DocumentModel doc = null; 201 if (session != null && session.exists(idRef)) { 202 doc = session.getDocument(idRef); 203 } else { 204 String parentPath = new Path(part[4]).removeLastSegments(1).toString(); 205 doc = new DocumentModelImpl(session.getSessionId(), part[3], part[2], new Path(part[4]), 206 null, idRef, new PathRef(parentPath), null, null, null, null); 207 } 208 value = doc; 209 } else { 210 value = arg; 211 } 212 } else { 213 value = sArg; 214 } 215 args[idx] = value; 216 idx++; 217 } 218 219 EventContext ctx; 220 if ((Boolean) evt.get("isDocumentEventContext")) { 221 ctx = new DocumentEventContext(session, principal, (DocumentModel) args[0], (DocumentRef) args[1]); 222 // XXX we loose other args ... 223 } else { 224 ctx = new EventContextImpl(session, principal); 225 ((EventContextImpl) ctx).setArgs(args); 226 } 227 228 ctx.setProperties(ctxProperties); 229 Event e = new EventImpl(eventName, ctx, Event.FLAG_NONE, time); 230 bundle.push(e); 231 } 232 return bundle; 233 } 234 235 public static class CannotReconstruct extends NuxeoException { 236 237 private static final long serialVersionUID = 1L; 238 239 public CannotReconstruct(String message) { 240 super(message); 241 } 242 243 } 244 245}