/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher.events;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGContentEvent;
import de.innovationgate.webgate.api.WGContentEventListener;
import de.innovationgate.webgate.api.WGContentType;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDatabaseConnectListener;
import de.innovationgate.webgate.api.WGDatabaseEvent;
import de.innovationgate.webgate.api.WGDatabaseEventListener;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGDocumentKey;
import de.innovationgate.webgate.api.workflow.WGWorkflowEventListener;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.expressions.ExpressionEngine;
import de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory;
import de.innovationgate.wgpublisher.expressions.ExpressionResult;
import de.innovationgate.wgpublisher.expressions.tmlscript.RhinoExpressionEngine;
import de.innovationgate.wgpublisher.webtml.utils.TMLContext;
import de.innovationgate.wgpublisher.webtml.utils.TMLUserProfile;
public class EventManager implements WGDatabaseEventListener, WGContentEventListener, WGWorkflowEventListener, WGDatabaseConnectListener {
private static final String EVENTNAME_CONTENTHASBEENSAVED = "contenthasbeensaved";
private static final String EVENTNAME_CONTENTHASBEENMOVED = "contenthasbeenmoved";
private static final String EVENTNAME_CONTENTHASBEENDELETED = "contenthasbeendeleted";
public static final String EVENTNAME_WORKFLOWMAIL = "workflowmail";
public static final String EVENTNAME_STATUSCHANGE = "statuschange";
public static final String EVENTNAME_SAVECONTENT = "savecontent";
public static final String EVENTNAME_CREATECONTENT = "createcontent";
private WGACore _core;
private Logger _log = Logger.getLogger("wga.events");
public static final String KEY_SCRIPTS = "##SCRIPTS##";
public class ContentEventPath implements EventPath {
public static final String ENTRYNAME_TYPE = "eventtype";
public static final String ENTRYNAME_CONTENTKEY = "contentkey";
public static final String ENTRYNAME_CONTENTTYPE = "contenttype";
public static final String ENTRYNAME_DB = "db";
public static final String ENTRYNAME_EVENT = "event";
private String _event;
private String _db;
private String _contenttype;
private String _contentkey;
private ContentEventPath(String event, String db, String contenttype, String contentkey) {
_event = event;
_db = db;
_contenttype = contenttype;
_contentkey = contentkey;
}
public EventPathEntry[] getEventHierarchy() {
EventPathEntry[] entryArray = new EventPathEntry[5];
entryArray[0] = new EventPathEntry(ENTRYNAME_DB, _db);
entryArray[1] = new EventPathEntry(ENTRYNAME_TYPE, "content");
entryArray[2] = new EventPathEntry(ENTRYNAME_EVENT, _event);
entryArray[3] = new EventPathEntry(ENTRYNAME_CONTENTTYPE, _contenttype);
entryArray[4] = new EventPathEntry(ENTRYNAME_CONTENTKEY, _contentkey);
return entryArray;
}
}
private Map eventMapRoot = new HashMap();
public EventManager(WGACore core) {
_core = core;
}
public void removeDatabaseEvents(String key) {
eventMapRoot.remove(new EventPathEntry("db", key));
}
public ContentEventPath createContentEventPath(String event, String db, String contenttype, String contentkey) {
return new ContentEventPath(event, db, contenttype, contentkey);
}
public void registerEventReceiver(EventPath eventPath, EventReceiver eventReceiver) {
EventPathEntry[] hierarchy = eventPath.getEventHierarchy();
mapEventReceiver(eventMapRoot, hierarchy, eventReceiver, 0);
}
/**
* @param eventPath
* @param eventScript
* @param i
*/
private void mapEventReceiver(Map refMap, EventPathEntry[] hierarchy, EventReceiver eventReceiver, int i) {
EventPathEntry newEntry = hierarchy[i];
Map nextMap = (Map) refMap.get(newEntry);
if (nextMap == null) {
nextMap = new HashMap();
refMap.put(newEntry, nextMap);
}
if ((i+1) < hierarchy.length) {
mapEventReceiver(nextMap, hierarchy, eventReceiver, i+1);
}
else {
List eventScripts = (List) nextMap.get(KEY_SCRIPTS);
if (eventScripts == null) {
eventScripts = new ArrayList();
nextMap.put(KEY_SCRIPTS, eventScripts);
}
eventScripts.add(eventReceiver);
}
}
public List executeEvent(EventPath eventPath, WGDocument doc, TMLUserProfile userProfile, Map contextObjects) {
Event event = new Event(eventPath, doc, userProfile, contextObjects);
EventPathEntry[] hierarchy = eventPath.getEventHierarchy();
List results = new ArrayList();
findEventReceivers(eventMapRoot, hierarchy, 0, event, results);
return results;
}
/**
* @param eventMapRoot
* @param hierarchy
* @param i
* @param event
*/
private void findEventReceivers(Map refMap, EventPathEntry[] hierarchy, int i, Event event, List results) {
List scripts = (List) refMap.get(KEY_SCRIPTS);
if (scripts != null) {
Iterator scriptsIt = scripts.iterator();
EventReceiver eventReceiver;
while (scriptsIt.hasNext()) {
eventReceiver = (EventReceiver) scriptsIt.next();
if (eventReceiver instanceof EventScript) {
results.add(executeEventScript((EventScript) eventReceiver, event));
}
else if (eventReceiver instanceof EventListener) {
results.add(((EventListener) eventReceiver).wgaEvent(event));
}
}
}
if (i < hierarchy.length) {
EventPathEntry pathEntry = hierarchy[i];
Map nextMap = (Map) refMap.get(pathEntry);
if (nextMap != null) {
findEventReceivers(nextMap, hierarchy, i+1, event, results);
}
nextMap = (Map) refMap.get(pathEntry.toWildcardVersion());
if (nextMap != null) {
findEventReceivers(nextMap, hierarchy, i+1, event, results);
}
}
}
/**
* @param eventScript
* @param event
* @return
*/
private Object executeEventScript(EventScript eventScript, Event event) {
// Empty scripts might get stored by CM
if (WGUtils.isEmpty(eventScript.getCode())) {
return null;
}
// Get engine
ExpressionEngine engine = ExpressionEngineFactory.getEngine(eventScript.getType());
if (engine == null) {
_log.error("Error executing event script. Unknown script type: " + eventScript.getType());
return null;
}
// Create necessary context objects
TMLContext context;
try {
context = new TMLContext(event.getdocument(), _core, event.getUserProfile(), null);
}
catch (WGAPIException e) {
_log.error("Error creating context for event script.", e);
return null;
}
Map objects = new HashMap();
objects.put("event", event);
objects.put(RhinoExpressionEngine.PARAM_SCRIPTNAME, eventScript.getDescription());
// Execute script
ExpressionResult result = engine.evaluateExpression(eventScript.getCode(), context, ExpressionEngine.TYPE_SCRIPT, objects);
if (result.isError()) {
_log.error("Error executing event script", result.getException());
return null;
}
else {
return result.getResult();
}
}
public void databaseUpdate(WGDatabaseEvent event) {
// Verify database is open(able)
WGDatabase db = event.getDatabase();
WGDocumentKey docKey = event.getEditedDocumentKey();
if (event.getType() == WGDatabaseEvent.TYPE_UPDATE && (docKey == null || docKey.getDocType() == WGDocument.TYPE_CONTENTTYPE)) {
updateDatabaseEvents(db);
}
}
public void updateDatabaseEvents(WGDatabase db) {
if (!db.getRoles().contains(WGDatabase.ROLE_CONTENT) || !db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
return;
}
String dbKey = (String) db.getAttribute(WGACore.DBATTRIB_DBKEY);
if (!db.isSessionOpen()) {
try {
if (db.openSession() < WGDatabase.ACCESSLEVEL_READER) {
_log.error("Could not update events for database '" + dbKey + "' because of access denial");
return;
}
db.getSessionContext().setTask("WGA Event Manager");
}
catch (WGAPIException e) {
_log.error("Could not update events for database '" + dbKey + "' because of database unavailability");
return;
}
}
// Re-register db events
try {
removeDatabaseEvents(dbKey);
List contentTypesList = db.getContentTypes();
if (contentTypesList == null) {
return;
}
Iterator contentTypes = contentTypesList.iterator();
WGContentType contentType;
while (contentTypes.hasNext()) {
contentType = (WGContentType) contentTypes.next();
updateDatabaseEvents(dbKey, contentType);
}
} catch (WGAPIException e) {
_log.error("Could not update events for database '" + dbKey + "'.", e);
}
}
private void updateDatabaseEvents(String dbKey, WGContentType contentType) throws WGAPIException {
// Create content
String ctEvent = (String) contentType.getMetaData(WGContentType.META_EVENT_CREATECONTENT);
if (ctEvent != null) {
String desc = "Eventscript createContent of Contenttype '" + contentType.getName() + "' (" + contentType.getDatabase().getDbReference() + ")";
EventScript eventScript = new EventScript(ctEvent, desc);
EventPath eventPath = createContentEventPath(EVENTNAME_CREATECONTENT, dbKey, contentType.getName(), "*");
registerEventReceiver(eventPath, eventScript);
}
// Save content
ctEvent = (String) contentType.getMetaData(WGContentType.META_EVENT_SAVECONTENT);
if (ctEvent != null) {
String desc = "Eventscript saveContent of Contenttype '" + contentType.getName() + "' (" + contentType.getDatabase().getDbReference() + ")";
EventScript eventScript = new EventScript(ctEvent, desc);
EventPath eventPath = createContentEventPath(EVENTNAME_SAVECONTENT, dbKey, contentType.getName(), "*");
registerEventReceiver(eventPath, eventScript);
}
// Workflow mail
ctEvent = (String) contentType.getMetaData(WGContentType.META_EVENT_WORKFLOWMAIL);
if (ctEvent != null) {
String desc = "Eventscript workflowMail of Contenttype '" + contentType.getName() + "' (" + contentType.getDatabase().getDbReference() + ")";
EventScript eventScript = new EventScript(ctEvent, desc);
EventPath eventPath = createContentEventPath(EVENTNAME_WORKFLOWMAIL, dbKey, contentType.getName(), "*");
registerEventReceiver(eventPath, eventScript);
}
// Status change
ctEvent = (String) contentType.getMetaData(WGContentType.META_EVENT_STATUSCHANGE);
if (ctEvent != null) {
String desc = "Eventscript statusChange of Contenttype '" + contentType.getName() + "' (" + contentType.getDatabase().getDbReference() + ")";
EventScript eventScript = new EventScript(ctEvent, desc);
EventPath eventPath = createContentEventPath(EVENTNAME_STATUSCHANGE, dbKey, contentType.getName(), "*");
registerEventReceiver(eventPath, eventScript);
}
}
/* (Kein Javadoc)
* @see de.innovationgate.webgate.api.WGDatabaseEventListener#isTemporary()
*/
public boolean isTemporary() {
return false;
}
/* (Kein Javadoc)
* @see de.innovationgate.webgate.api.WGContentEventListener#contentCreated(de.innovationgate.webgate.api.WGContentEvent)
*/
public void contentCreated(WGContentEvent contentEvent) {
WGContent newContent = null;
try {
newContent = contentEvent.getContent();
}
catch (WGAPIException e) {
_log.error("Cannot process create event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
WGDatabase db = contentEvent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return;
}
String dbKey = (String) db.getAttribute(WGACore.DBATTRIB_DBKEY);
HttpServletRequest request = (HttpServletRequest) db.getSessionContext().getAttribute(WGACore.DBSESSIONCONTEXT_REQUEST);
TMLUserProfile userProfile = null;
if (request != null) {
userProfile = (TMLUserProfile) request.getAttribute(WGACore.ATTRIB_PROFILE + db.getDbReference());
}
String contentType = contentEvent.getContentType();
WGDocumentKey docKey = new WGDocumentKey(contentEvent.getDocumentKey());
EventPath eventPath = createContentEventPath(EVENTNAME_CREATECONTENT, dbKey, contentType, docKey.getName());
executeEvent(eventPath, newContent, userProfile, null);
}
/* (Kein Javadoc)
* @see de.innovationgate.webgate.api.WGContentEventListener#contentSaved(de.innovationgate.webgate.api.WGContentEvent)
*/
public boolean contentSaved(WGContentEvent contentEvent) {
WGContent newContent = null;
try {
newContent = contentEvent.getContent();
}
catch (WGAPIException e) {
_log.error("Cannot process save event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return false;
}
WGDatabase db = contentEvent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return true;
}
String dbKey = (String) db.getAttribute(WGACore.DBATTRIB_DBKEY);
HttpServletRequest request = (HttpServletRequest) db.getSessionContext().getAttribute(WGACore.DBSESSIONCONTEXT_REQUEST);
TMLUserProfile userProfile = null;
if (request != null) {
userProfile = (TMLUserProfile) request.getAttribute(WGACore.ATTRIB_PROFILE + db.getDbReference());
}
String contentType = contentEvent.getContentType();
WGDocumentKey docKey = new WGDocumentKey(contentEvent.getDocumentKey());
EventPath eventPath = createContentEventPath(EVENTNAME_SAVECONTENT, dbKey, contentType, docKey.getName());
Iterator results = executeEvent(eventPath, newContent, userProfile, null).iterator();
while (results.hasNext()) {
Object result = results.next();
if (result instanceof List) {
result = ((List) result).get(0);
}
if (result instanceof Boolean && ((Boolean) result).booleanValue() == false) {
return false;
}
if (result instanceof Number && ((Number) result).intValue() == 0) {
return false;
}
}
return true;
}
/* (Kein Javadoc)
* @see de.innovationgate.webgate.api.WGWorkflowEventListener#workflowMail(de.innovationgate.webgate.api.WGWorkflowEvent)
*/
public void workflowMail(de.innovationgate.webgate.api.workflow.WGWorkflowEvent workflowEvent) {
WGContent newContent = workflowEvent.getContent();
WGDatabase db = newContent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return;
}
String dbKey = (String) db.getAttribute(WGACore.DBATTRIB_DBKEY);
String contentType = "*";
try {
if (newContent.getStructEntry() != null && newContent.getStructEntry().getContentType() != null) {
contentType = newContent.getStructEntry().getContentType().getName();
}
EventPath eventPath = createContentEventPath(EVENTNAME_WORKFLOWMAIL, dbKey, contentType, newContent.getContentKey().toString());
Map contextObjects = new HashMap();
contextObjects.put("mailbody", workflowEvent.getMailBody());
executeEvent(eventPath, newContent, null, contextObjects).iterator();
}
catch (WGAPIException e) {
_log.error("Unable to process workflowMail event.", e);
}
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.WGContentEventListener#contentHasBeenDeleted(de.innovationgate.webgate.api.WGContentEvent)
*/
public void contentHasBeenDeleted(WGContentEvent contentEvent) {
WGDatabase db = contentEvent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return;
}
WGContent content = null;
try {
content = contentEvent.getContent();
}
catch (WGAPIException e) {
_log.error("Cannot process has been deleted event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
if (content == null) {
try {
content = db.getDummyContent(db.getDefaultLanguage());
}
catch (WGAPIException e) {
_log.error("Cannot process deletion event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ") because no context is available.", e);
return;
}
}
String dbKey = (String) db.getDbReference();
HttpServletRequest request = (HttpServletRequest) db.getSessionContext().getAttribute(WGACore.DBSESSIONCONTEXT_REQUEST);
TMLUserProfile userProfile = null;
if (request != null) {
userProfile = (TMLUserProfile) request.getAttribute(WGACore.ATTRIB_PROFILE + db.getDbReference());
}
String contentType = "*";
if (contentEvent.getContentType() != null) {
contentType = contentEvent.getContentType();
}
WGDocumentKey docKey = new WGDocumentKey(contentEvent.getDocumentKey());
EventPath eventPath = createContentEventPath(EVENTNAME_CONTENTHASBEENDELETED, dbKey, contentType, docKey.getName());
Map contextObjects = new HashMap();
contextObjects.put("contentkey", docKey.getName());
contextObjects.put("contenttype", contentType);
executeEvent(eventPath, content, userProfile, null);
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.WGContentEventListener#contentHasBeenSaved(de.innovationgate.webgate.api.WGContentEvent)
*/
public void contentHasBeenSaved(WGContentEvent contentEvent) {
WGContent newContent = null;
try {
newContent = contentEvent.getContent();
if (newContent == null) {
//_log.warn("Cannot process update event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ") because the content is not retrievable. Might have been deleted in the meantime.");
return;
}
}
catch (WGAPIException e) {
_log.error("Cannot process update event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
WGDatabase db = contentEvent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return;
}
String dbKey = (String) db.getAttribute(WGACore.DBATTRIB_DBKEY);
HttpServletRequest request = (HttpServletRequest) db.getSessionContext().getAttribute(WGACore.DBSESSIONCONTEXT_REQUEST);
TMLUserProfile userProfile = null;
if (request != null) {
userProfile = (TMLUserProfile) request.getAttribute(WGACore.ATTRIB_PROFILE + db.getDbReference());
}
String contentType = "*";
try {
if (newContent != null && newContent.hasCompleteRelationships()) {
contentType = newContent.getStructEntry().getContentType().getName();
}
EventPath eventPath = createContentEventPath(EVENTNAME_CONTENTHASBEENSAVED, dbKey, contentType, newContent.getContentKey().toString());
executeEvent(eventPath, newContent, userProfile, null);
}
catch (WGAPIException e) {
_log.error("Cannot process update event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
}
public void databaseConnected(WGDatabaseEvent event) {
updateDatabaseEvents(event.getDatabase());
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.WGDatabaseConnectListener#databaseConnectionError(de.innovationgate.webgate.api.WGDatabaseEvent)
*/
public void databaseConnectionError(WGDatabaseEvent event) {
}
public void contentHasBeenMoved(WGContentEvent contentEvent) {
WGContent newContent = null;
try {
newContent = contentEvent.getContent();
if (newContent == null) {
//_log.warn("Cannot process move event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ") because the content is not retrievable. Might have been deleted in the meantime.");
return;
}
}
catch (WGAPIException e) {
_log.error("Cannot process move event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
WGDatabase db = contentEvent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return;
}
String dbKey = (String) db.getAttribute(WGACore.DBATTRIB_DBKEY);
HttpServletRequest request = (HttpServletRequest) db.getSessionContext().getAttribute(WGACore.DBSESSIONCONTEXT_REQUEST);
TMLUserProfile userProfile = null;
if (request != null) {
userProfile = (TMLUserProfile) request.getAttribute(WGACore.ATTRIB_PROFILE + db.getDbReference());
}
String contentType = "*";
try {
if (newContent != null && newContent.hasCompleteRelationships()) {
contentType = newContent.getStructEntry().getContentType().getName();
}
EventPath eventPath = createContentEventPath(EVENTNAME_CONTENTHASBEENMOVED, dbKey, contentType, newContent.getContentKey().toString());
executeEvent(eventPath, newContent, userProfile, null);
}
catch (WGAPIException e) {
_log.error("Cannot process move event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
}
public void contentStatusChanged(WGContentEvent contentEvent) {
WGContent newContent = null;
try {
newContent = contentEvent.getContent();
}
catch (WGAPIException e) {
_log.error("Cannot process status change event for content '" + contentEvent.getDocumentKey() + "' (DB " + contentEvent.getDatabase().getDbReference() + ").", e);
return;
}
WGDatabase db = contentEvent.getDatabase();
if (!db.getSessionContext().isContentTypeEventsEnabled()) {
return;
}
String dbKey = (String) db.getDbReference();
HttpServletRequest request = (HttpServletRequest) db.getSessionContext().getAttribute(WGACore.DBSESSIONCONTEXT_REQUEST);
TMLUserProfile userProfile = null;
if (request != null) {
userProfile = (TMLUserProfile) request.getAttribute(WGACore.ATTRIB_PROFILE + db.getDbReference());
}
String contentType = contentEvent.getContentType();
WGDocumentKey docKey = new WGDocumentKey(contentEvent.getDocumentKey());
EventPath eventPath = createContentEventPath(EVENTNAME_STATUSCHANGE, dbKey, contentType, docKey.getName());
executeEvent(eventPath, newContent, userProfile, null).iterator();
}
}