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