/*
* (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id$
*/
package org.nuxeo.ecm.core.event.impl;
import java.io.Serializable;
import java.rmi.dgc.VMID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.CoreInstance;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentNotFoundException;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.event.DeletedDocumentModel;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.ecm.core.event.EventBundle;
import org.nuxeo.ecm.core.event.EventContext;
import org.nuxeo.ecm.core.event.ReconnectedEventBundle;
import org.nuxeo.runtime.api.Framework;
/**
* Default implementation for an {@link EventBundle} that need to be reconnected to a usable Session.
*
* @author tiry
*/
public class ReconnectedEventBundleImpl implements ReconnectedEventBundle {
private static final long serialVersionUID = 1L;
protected EventBundle sourceEventBundle;
/** Lister name or names. */
protected String listenerName;
protected transient List<Event> reconnectedEvents;
protected transient LoginContext loginCtx;
protected transient CoreSession reconnectedCoreSession;
private static final Log log = LogFactory.getLog(ReconnectedEventBundleImpl.class);
protected ReconnectedEventBundleImpl() {
}
public ReconnectedEventBundleImpl(EventBundle sourceEventBundle) {
this.sourceEventBundle = sourceEventBundle;
}
/** @since 5.6 */
public ReconnectedEventBundleImpl(EventBundle sourceEventBundle, String listenerName) {
this.sourceEventBundle = sourceEventBundle;
this.listenerName = listenerName;
}
protected CoreSession getReconnectedCoreSession(String repoName) {
if (reconnectedCoreSession == null) {
try {
loginCtx = Framework.login();
} catch (LoginException e) {
log.error("Cannot log in", e);
return null;
}
reconnectedCoreSession = CoreInstance.openCoreSessionSystem(repoName);
} else {
// Sanity Check
if (!reconnectedCoreSession.getRepositoryName().equals(repoName)) {
if (repoName != null) {
throw new IllegalStateException("Can no reconnected a Bundle tied to several Core instances !");
}
}
}
return reconnectedCoreSession;
}
protected List<Event> getReconnectedEvents() {
if (reconnectedEvents == null) {
reconnectedEvents = new ArrayList<Event>();
for (Event event : sourceEventBundle) {
EventContext ctx = event.getContext();
CoreSession session = ctx.getRepositoryName() == null ? null
: getReconnectedCoreSession(ctx.getRepositoryName());
List<Object> newArgs = new ArrayList<Object>();
for (Object arg : ctx.getArguments()) {
Object newArg = arg;
if (refetchDocumentModel(session, arg) && session.getPrincipal() != null) {
DocumentModel oldDoc = (DocumentModel) arg;
DocumentRef ref = oldDoc.getRef();
if (ref != null) {
try {
if (session.exists(oldDoc.getRef())) {
newArg = session.getDocument(oldDoc.getRef());
} else {
// probably deleted doc
newArg = new DeletedDocumentModel(oldDoc);
}
} catch (DocumentNotFoundException e) {
log.error("Can not refetch Doc with ref " + ref.toString(), e);
}
}
}
// XXX treat here other cases !!!!
newArgs.add(newArg);
}
EventContext newCtx = null;
if (ctx instanceof DocumentEventContext) {
newCtx = new DocumentEventContext(session, ctx.getPrincipal(), (DocumentModel) newArgs.get(0),
(DocumentRef) newArgs.get(1));
} else {
newCtx = new EventContextImpl(session, ctx.getPrincipal());
((EventContextImpl) newCtx).setArgs(newArgs.toArray());
}
Map<String, Serializable> newProps = new HashMap<String, Serializable>();
for (Entry<String, Serializable> prop : ctx.getProperties().entrySet()) {
Serializable propValue = prop.getValue();
if (refetchDocumentModel(session, propValue)) {
DocumentModel oldDoc = (DocumentModel) propValue;
DocumentRef oldRef = oldDoc.getRef();
try {
if (session.exists(oldRef)) {
propValue = session.getDocument(oldRef);
} else {
log.warn("Listener " + (listenerName == null ? "" : "'" + listenerName + "' ")
+ "cannot refetch missing document: " + oldRef + " ("
+ oldDoc.getPathAsString() + ")");
}
} catch (DocumentNotFoundException e) {
log.error("Can not refetch Doc with ref " + oldRef, e);
}
}
// XXX treat here other cases !!!!
newProps.put(prop.getKey(), propValue);
}
newCtx.setProperties(newProps);
Event newEvt = new EventImpl(event.getName(), newCtx, event.getFlags(), event.getTime());
reconnectedEvents.add(newEvt);
}
}
return reconnectedEvents;
}
protected boolean refetchDocumentModel(CoreSession session, Object eventProperty) {
if (eventProperty instanceof DocumentModel && session != null) {
DocumentModel doc = (DocumentModel) eventProperty;
if (Boolean.TRUE.equals(doc.getContextData(SKIP_REFETCH_DOCUMENT_CONTEXT_KEY))) {
return false;
}
return true;
}
return false;
}
@Override
public String getName() {
return sourceEventBundle.getName();
}
@Override
public VMID getSourceVMID() {
return sourceEventBundle.getSourceVMID();
}
@Override
public boolean hasRemoteSource() {
return sourceEventBundle.hasRemoteSource();
}
@Override
public boolean isEmpty() {
return sourceEventBundle.isEmpty();
}
@Override
public Event peek() {
return getReconnectedEvents().get(0);
}
@Override
public void push(Event event) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return sourceEventBundle.size();
}
@Override
public Iterator<Event> iterator() {
return getReconnectedEvents().iterator();
}
@Override
public void disconnect() {
if (reconnectedCoreSession != null) {
reconnectedCoreSession.close();
}
reconnectedCoreSession = null;
reconnectedEvents = null;
if (loginCtx != null) {
try {
loginCtx.logout();
} catch (LoginException e) {
log.error("Cannot log out", e);
} finally {
loginCtx = null;
}
}
}
@Override
public boolean comesFromJMS() {
return false;
}
@Override
public boolean containsEventName(String eventName) {
return sourceEventBundle.containsEventName(eventName);
}
public List<String> getEventNames() {
List<String> eventNames = new ArrayList<String>();
for (Event event : sourceEventBundle) {
eventNames.add(event.getName());
}
return eventNames;
}
}