/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-impl/src/main/java/org/sakaiproject/event/impl/BaseNotificationService.java $
* $Id: BaseNotificationService.java 122028 2013-04-01 19:49:35Z azeckoski@unicon.net $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2008 Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.
*
**********************************************************************************/
package org.sakaiproject.event.impl;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Stack;
import java.util.Vector;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.api.ComponentManager;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.entity.api.Edit;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.ResourcePropertiesEdit;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.EventTrackingService;
import org.sakaiproject.event.api.Notification;
import org.sakaiproject.event.api.NotificationAction;
import org.sakaiproject.event.api.NotificationEdit;
import org.sakaiproject.event.api.NotificationLockedException;
import org.sakaiproject.event.api.NotificationNotDefinedException;
import org.sakaiproject.event.api.NotificationService;
import org.sakaiproject.id.api.IdManager;
import org.sakaiproject.memory.api.CacheRefresher;
import org.sakaiproject.memory.api.MemoryService;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.tool.api.SessionBindingEvent;
import org.sakaiproject.tool.api.SessionBindingListener;
import org.sakaiproject.util.BaseResourcePropertiesEdit;
import org.sakaiproject.util.SingleStorageUser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* <p>
* BaseNotificationService ...
* </p>
*/
public abstract class BaseNotificationService implements NotificationService, Observer, SingleStorageUser, CacheRefresher
{
/** Our logger. */
private static Log M_log = LogFactory.getLog(BaseNotificationService.class);
/** Storage manager for this service. */
protected Storage m_storage = null;
/** A Cache for this service - Notification objects stored by notification reference. */
protected NotificationCache m_cache = null;
/** The initial portion of a relative access point URL. */
protected String m_relativeAccessPoint = null;
/** Transient notifications (NotificationEdit). */
protected List m_transients = null;
private ComponentManager componentManager = org.sakaiproject.component.cover.ComponentManager.getInstance();
/**********************************************************************************************************************************************************************************************************************************************************
* Abstractions, etc.
*********************************************************************************************************************************************************************************************************************************************************/
/**
* Construct storage for this service.
*/
protected abstract Storage newStorage();
/**
* Does the resource reference match the filter?
*
* @param filter
* The resource reference filter.
* @param ref
* The resource reference string.
* @return true if the filter matches the ref, false if not.
*/
protected boolean match(String filter, String ref)
{
if (filter == null) return true;
if (filter.length() == 0) return true;
if (ref.startsWith(filter)) return true;
return false;
}
/**
* Access the partial URL that forms the root of resource URLs.
*
* @param relative
* if true, form within the access path only (i.e. starting with /content)
* @return the partial URL that forms the root of resource URLs.
*/
protected String getAccessPoint(boolean relative)
{
return (relative ? "" : serverConfigurationService().getAccessUrl()) + m_relativeAccessPoint;
}
/**
* @return the ComponentManager collaborator
*/
protected ComponentManager getComponentManager() {
return componentManager;
}
/**
* Access the notification id extracted from a notification reference.
*
* @param ref
* The notification reference string.
* @return The the notification id extracted from a notification reference.
*/
protected String notificationId(String ref)
{
String start = getAccessPoint(true) + Entity.SEPARATOR;
int i = ref.indexOf(start);
if (i == -1) return ref;
String id = ref.substring(i + start.length());
return id;
}
/**
* Access the external URL which can be used to access the resource from outside the system.
*
* @param id
* The notification id.
* @return The the external URL which can be used to access the resource from outside the system.
*/
protected String notificationUrl(String id)
{
return getAccessPoint(false) + Entity.SEPARATOR + id;
}
/**********************************************************************************************************************************************************************************************************************************************************
* Dependencies
*********************************************************************************************************************************************************************************************************************************************************/
/**
* @return the EventTrackingService collaborator.
*/
protected abstract EventTrackingService eventTrackingService();
/**
* @return the ServerConfigurationService collaborator.
*/
protected abstract ServerConfigurationService serverConfigurationService();
/**
* @return the IdManager collaborator.
*/
protected abstract IdManager idManager();
/**
* @return the MemoryService collaborator
*/
protected abstract MemoryService memoryService();
/**********************************************************************************************************************************************************************************************************************************************************
* Configuration
*********************************************************************************************************************************************************************************************************************************************************/
/** Configuration: make the email notifications To: reply-able. */
protected boolean m_emailsToReplyable = false;
/**
* Configuration: set reply-able status for email notifications in the To:.
*
* @param value
* The setting
*/
public void setEmailToReplyable(boolean value)
{
M_log.warn("Use of this setter (emailToReplyable) is deprecated: use notify.email.to.replyable instead");
m_emailsToReplyable = value;
}
/** Configuration: make the email notifications From: reply-able. */
protected boolean m_emailsFromReplyable = false;
/**
* Configuration: set reply-able status for email notifications in the From:.
*
* @param value
* The setting
*/
public void setEmailFromReplyable(boolean value)
{
M_log.warn("Use of this setter (emailFromReplyable) is deprecated: use notify.email.from.replyable instead");
m_emailsFromReplyable = value;
}
/**********************************************************************************************************************************************************************************************************************************************************
* Init and Destroy
*********************************************************************************************************************************************************************************************************************************************************/
/**
* Final initialization, once all dependencies are set.
*/
public void init()
{
try
{
// prepare for transients
m_transients = new Vector();
m_relativeAccessPoint = REFERENCE_ROOT;
M_log.info(this + ".init() started");
// construct storage and read
m_storage = newStorage();
m_storage.open();
// make the cache
m_cache = new NotificationCache(this, notificationReference(""), eventTrackingService(), memoryService());
// start watching the events - only those generated on this server, not those from elsewhere
eventTrackingService().addLocalObserver(this);
// set these from real sakai config values
m_emailsFromReplyable = serverConfigurationService().getBoolean("notify.email.from.replyable", false);
m_emailsToReplyable = serverConfigurationService().getBoolean("notify.email.to.replyable", false);
M_log.info(this + ".init() complete");
}
catch (Exception t)
{
M_log.warn(this + ".init(): ", t);
}
}
/**
* Returns to uninitialized state.
*/
public void destroy()
{
// if we are not in a global shutdown, remove my event notification registration
if (!getComponentManager().hasBeenClosed())
{
eventTrackingService().deleteObserver(this);
}
// clean up cache
m_cache.clear();
m_cache = null;
// clean up storage
m_storage.close();
m_storage = null;
// clean up transients
m_transients.clear();
m_transients = null;
M_log.info(this + ".destroy()");
}
/**********************************************************************************************************************************************************************************************************************************************************
* NotificationService implementation
*********************************************************************************************************************************************************************************************************************************************************/
/**
* @inheritDoc
*/
public NotificationEdit addNotification()
{
// check security (throws if not permitted)
// unlock(SECURE_ADD_NOTIFICATION, notificationReference(id));
// get a new unique id
String id = idManager().createUuid();
// reserve a notification with this id from the info store - if it's in use, this will return null
NotificationEdit notification = m_storage.put(id);
/*
* if (notification == null) { throw new IdUsedException(id); }
*/
((BaseNotificationEdit) notification).setEvent(SECURE_ADD_NOTIFICATION);
return notification;
}
/**
* @inheritDoc
*/
public NotificationEdit addTransientNotification()
{
// the id is not unique, and not really used
String id = "transient";
// create an object, not through storage
NotificationEdit notification = new BaseNotificationEdit(id);
// remember it
m_transients.add(notification);
// no event, no other cluster server knows about it - it's transient and local
return notification;
}
/**
* @inheritDoc
*/
public Notification getNotification(String id) throws NotificationNotDefinedException
{
Notification notification = null;
// if we have it cached, use it (hit or miss)
String key = notificationReference(id);
if (m_cache.containsKey(key))
{
notification = (Notification) m_cache.get(key);
}
// if not in the cache, see if we have it in our info store
else
{
notification = m_storage.get(id);
// cache it (hit or miss)
m_cache.put(notification);
}
// if not found
if (notification == null) throw new NotificationNotDefinedException(id);
return notification;
}
/**
* @inheritDoc
*/
public String notificationReference(String id)
{
return getAccessPoint(true) + Entity.SEPARATOR + id;
}
/**
* @inheritDoc
*/
public NotificationEdit editNotification(String id) throws NotificationNotDefinedException, NotificationLockedException
{
// check security (throws if not permitted)
// unlock(SECURE_UPDATE_NOTIFICATION, notificationReference(id));
// check for existance
if ((m_cache.get(notificationReference(id)) == null) && (!m_storage.check(id)))
{
throw new NotificationNotDefinedException(id);
}
// ignore the cache - get the notification with a lock from the info store
NotificationEdit notification = m_storage.edit(id);
if (notification == null) throw new NotificationLockedException(id);
((BaseNotificationEdit) notification).setEvent(SECURE_UPDATE_NOTIFICATION);
return notification;
}
/**
* @inheritDoc
*/
public void commitEdit(NotificationEdit notification)
{
// check for closed edit
if (!notification.isActiveEdit())
{
try
{
throw new Exception();
}
catch (Exception e)
{
M_log.warn(this + ".commitEdit(): closed NotificationEdit", e);
}
return;
}
// update the properties
// addLiveUpdateProperties(notification.getPropertiesEdit());
// complete the edit
m_storage.commit(notification);
// track it
eventTrackingService().post(
eventTrackingService()
.newEvent(((BaseNotificationEdit) notification).getEvent(), notification.getReference(), true));
// close the edit object
((BaseNotificationEdit) notification).closeEdit();
}
/**
* @inheritDoc
*/
public void cancelEdit(NotificationEdit notification)
{
// check for closed edit
if (!notification.isActiveEdit())
{
try
{
throw new Exception();
}
catch (Exception e)
{
M_log.warn(this + ".cancelEdit(): closed NotificationEdit", e);
}
return;
}
// release the edit lock
m_storage.cancel(notification);
// close the edit object
((BaseNotificationEdit) notification).closeEdit();
}
/**
* @inheritDoc
*/
public void removeNotification(NotificationEdit notification)
{
// check for closed edit
if (!notification.isActiveEdit())
{
try
{
throw new Exception();
}
catch (Exception e)
{
M_log.warn(this + ".removeNotification(): closed NotificationEdit", e);
}
return;
}
// check security (throws if not permitted)
// unlock(SECURE_REMOVE_NOTIFICATION, notification.getReference());
// complete the edit
m_storage.remove(notification);
// track it
eventTrackingService().post(eventTrackingService().newEvent(SECURE_REMOVE_NOTIFICATION, notification.getReference(), true));
// close the edit object
((BaseNotificationEdit) notification).closeEdit();
}
/**
* @inheritDoc
*/
public List getNotifications(String function)
{
List notifications = null;
// if we have disabled the cache, don't use if
if (m_cache.disabled())
{
notifications = m_storage.getAll(function);
}
else
{
// if the cache is complete, use it
if (m_cache.isComplete())
{
notifications = m_cache.getAll(function);
}
// otherwise get all the notifications from storage
else
{
// Note: while we are getting from storage, storage might change. These can be processed
// after we get the storage entries, and put them in the cache, and mark the cache complete.
// -ggolden
synchronized (m_cache)
{
// if we were waiting and it's now complete...
if (m_cache.isComplete())
{
notifications = m_cache.getAll(function);
}
else
{
// save up any events to the cache until we get past this load
m_cache.holdEvents();
// get them all for caching
List all = m_storage.getAll();
// update the cache, and mark it complete
for (int i = 0; i < all.size(); i++)
{
Notification notification = (Notification) all.get(i);
m_cache.put(notification);
}
m_cache.setComplete();
// get those for just this function
notifications = m_cache.getAll(function);
// now we are complete, process any cached events
m_cache.processEvents();
}
}
}
}
// if none found in storage
if (notifications == null)
{
notifications = new Vector();
}
// add transients
for (Iterator it = m_transients.iterator(); it.hasNext();)
{
Notification notification = (Notification) it.next();
if (notification.containsFunction(function))
{
notifications.add(notification);
}
}
return notifications;
}
/**
* @inheritDoc
*/
public Notification findNotification(String function, String filter)
{
// start with all those for this function (just 'cause we have a nice method to get them -ggolden)
List notifications = getNotifications(function);
for (Iterator iNotifications = notifications.iterator(); iNotifications.hasNext();)
{
Notification notification = (Notification) iNotifications.next();
if (notification.getResourceFilter().equals(filter))
{
return notification;
}
}
return null;
}
/**
* {@inheritDoc}
*/
public boolean isNotificationToReplyable()
{
return this.m_emailsToReplyable;
}
/**
* {@inheritDoc}
*/
public boolean isNotificationFromReplyable()
{
return this.m_emailsFromReplyable;
}
/**********************************************************************************************************************************************************************************************************************************************************
* Observer implementation
*********************************************************************************************************************************************************************************************************************************************************/
/**
* This method is called whenever the observed object is changed. An application calls an <tt>Observable</tt> object's <code>notifyObservers</code> method to have all the object's observers notified of the change. default implementation is to
* cause the courier service to deliver to the interface controlled by my controller. Extensions can override.
*
* @param o
* the observable object.
* @param arg
* an argument passed to the <code>notifyObservers</code> method.
*/
public void update(Observable o, Object arg)
{
// arg is Event
if (!(arg instanceof Event)) return;
Event event = (Event) arg;
// check the event function against the functions we have notifications watching for
String function = event.getEvent();
// for each notification watching for this event
List notifications = getNotifications(function);
for (Iterator it = notifications.iterator(); it.hasNext();)
{
Notification notification = (Notification) it.next();
// if the resource matches the notification's resource filter
if (match(notification.getResourceFilter(), event.getResource()))
{
// cause the notification to run
notification.notify(event);
}
}
} // update
/**********************************************************************************************************************************************************************************************************************************************************
* Storage
*********************************************************************************************************************************************************************************************************************************************************/
protected interface Storage
{
/**
* Open and be ready to read / write.
*/
public void open();
/**
* Close.
*/
public void close();
/**
* Check if a notification by this id exists.
*
* @param id
* The notification id.
* @return true if a nitificaion by this id exists, false if not.
*/
public boolean check(String id);
/**
* Add a new notification with this id.
*
* @param id
* The notification id.
* @return The locked notification with this id, or null if in use.
*/
public NotificationEdit put(String id);
/**
* Get the notification with this id, or null if not found.
*
* @param id
* The notification id.
* @return The notification with this id, or null if not found.
*/
public Notification get(String id);
/**
* Get a List of all the notifications that are interested in this Event function.
*
* @param function
* The Event function
* @return The List (Notification) of all the notifications that are interested in this Event function.
*/
public List getAll(String function);
/**
* Get a List of all notifications.
*
* @return The List (Notification) of all notifications.
*/
public List getAll();
/**
* Get a lock on the notification with this id, or null if a lock cannot be gotten.
*
* @param id
* The user id.
* @return The locked Notification with this id, or null if this records cannot be locked.
*/
public NotificationEdit edit(String id);
/**
* Commit the changes and release the lock.
*
* @param user
* The notification to commit.
*/
public void commit(NotificationEdit notification);
/**
* Cancel the changes and release the lock.
*
* @param user
* The notification to commit.
*/
public void cancel(NotificationEdit notification);
/**
* Remove this notification.
*
* @param user
* The notification to remove.
*/
public void remove(NotificationEdit notification);
} // Storage
/**********************************************************************************************************************************************************************************************************************************************************
* StorageUser implementation
*********************************************************************************************************************************************************************************************************************************************************/
/**
* Construct a new rsource given just an id.
*
* @param container
* The Resource that is the container for the new resource (may be null).
* @param id
* The id for the new object.
* @param others
* (options) array of objects to load into the Resource's fields.
* @return The new resource.
*/
public Entity newResource(Entity container, String id, Object[] others)
{
return new BaseNotification(id);
}
/**
* Construct a new resource, from an XML element.
*
* @param container
* The Resource that is the container for the new resource (may be null).
* @param element
* The XML.
* @return The new resource from the XML.
*/
public Entity newResource(Entity container, Element element)
{
return new BaseNotification(element);
}
/**
* Construct a new resource from another resource of the same type.
*
* @param container
* The Resource that is the container for the new resource (may be null).
* @param other
* The other resource.
* @return The new resource as a copy of the other.
*/
public Entity newResource(Entity container, Entity other)
{
return new BaseNotification((Notification) other);
}
/**
* Construct a new rsource given just an id.
*
* @param container
* The Resource that is the container for the new resource (may be null).
* @param id
* The id for the new object.
* @param others
* (options) array of objects to load into the Resource's fields.
* @return The new resource.
*/
public Edit newResourceEdit(Entity container, String id, Object[] others)
{
BaseNotificationEdit e = new BaseNotificationEdit(id);
e.activate();
return e;
}
/**
* Construct a new resource, from an XML element.
*
* @param container
* The Resource that is the container for the new resource (may be null).
* @param element
* The XML.
* @return The new resource from the XML.
*/
public Edit newResourceEdit(Entity container, Element element)
{
BaseNotificationEdit e = new BaseNotificationEdit(element);
e.activate();
return e;
}
/**
* Construct a new resource from another resource of the same type.
*
* @param container
* The Resource that is the container for the new resource (may be null).
* @param other
* The other resource.
* @return The new resource as a copy of the other.
*/
public Edit newResourceEdit(Entity container, Entity other)
{
BaseNotificationEdit e = new BaseNotificationEdit((Notification) other);
e.activate();
return e;
}
/**
* Collect the fields that need to be stored outside the XML (for the resource).
*
* @return An array of field values to store in the record outside the XML (for the resource).
*/
public Object[] storageFields(Entity r)
{
return null;
}
/**
* Check if this resource is in draft mode.
*
* @param r
* The resource.
* @return true if the resource is in draft mode, false if not.
*/
public boolean isDraft(Entity r)
{
return false;
}
/**
* Access the resource owner user id.
*
* @param r
* The resource.
* @return The resource owner user id.
*/
public String getOwnerId(Entity r)
{
return null;
}
/**
* Access the resource date.
*
* @param r
* The resource.
* @return The resource date.
*/
public Time getDate(Entity r)
{
return null;
}
/**********************************************************************************************************************************************************************************************************************************************************
* CacheRefresher implementation (no container)
*********************************************************************************************************************************************************************************************************************************************************/
/**
* Get a new value for this key whose value has already expired in the cache.
*
* @param key
* The key whose value has expired and needs to be refreshed.
* @param oldValue
* The old exipred value of the key.
* @param event
* The event which triggered this refresh.
* @return a new value for use in the cache for this key; if null, the entry will be removed.
*/
public Object refresh(Object key, Object oldValue, Event event)
{
// key is a reference, but our storage wants an id
String id = notificationId((String) key);
// get this from storage
Notification notification = m_storage.get(id);
if (M_log.isDebugEnabled()) M_log.debug(this + ".refresh(): " + key + " : " + id);
return notification;
} // refresh
/**********************************************************************************************************************************************************************************************************************************************************
* Notification implementation
*********************************************************************************************************************************************************************************************************************************************************/
public class BaseNotification implements Notification
{
/** The Event(s) function we are watching for. */
protected List m_functions = null;
/** The resource reference filter. */
protected String m_filter = null;
/** The resource id. */
protected String m_id = null;
/** The resource properties. */
protected ResourcePropertiesEdit m_properties = null;
/** The action helper class. */
protected NotificationAction m_action = null;
/**
* Construct.
*
* @param id
* The id to use.
*/
public BaseNotification(String id)
{
// generate a new id
m_id = id;
// setup for properties
m_properties = new BaseResourcePropertiesEdit();
// setup for functions
m_functions = new Vector();
}
/**
* @inheritDoc
*/
public BaseNotification(Notification other)
{
setAll(other);
}
/**
* @inheritDoc
*/
public BaseNotification(Element el)
{
// setup for properties
m_properties = new BaseResourcePropertiesEdit();
// setup for functions
m_functions = new Vector();
m_id = el.getAttribute("id");
// the first function
String func = StringUtils.trimToNull(el.getAttribute("function"));
if (func != null)
{
m_functions.add(func);
}
m_filter = StringUtils.trimToNull(el.getAttribute("filter"));
// the children (properties, action helper)
NodeList children = el.getChildNodes();
final int length = children.getLength();
for (int i = 0; i < length; i++)
{
Node child = children.item(i);
if (child.getNodeType() != Node.ELEMENT_NODE) continue;
Element element = (Element) child;
// look for properties
if (element.getTagName().equals("properties"))
{
// re-create properties
m_properties = new BaseResourcePropertiesEdit(element);
}
// look for the helper element
else if (element.getTagName().equals("action"))
{
// the class name
String className = StringUtils.trimToNull(element.getAttribute("class"));
if (className != null)
{
// create the class
try
{
Class<?> actionClass;
try {
actionClass = Class.forName(className);
} catch (ClassNotFoundException cnfe) {
// we're trying to access a class not in the event pack's classloader
// So ask the ComponentManager
Object obj = getComponentManager().get(className);
if (obj == null) throw new ClassNotFoundException("Cannot reconstitute the NotificationAction named as " + className);
else actionClass = obj.getClass();
}
m_action = (NotificationAction) actionClass.newInstance();
// let it pick up it's settings
m_action.set(element);
}
catch (Exception e)
{
M_log.warn(this + " exception creating action helper: " + e.toString());
}
}
}
else if (element.getTagName().equals("function"))
{
func = StringUtils.trimToNull(element.getAttribute("id"));
m_functions.add(func);
}
}
}
/**
* @inheritDoc
*/
protected void setAll(Notification other)
{
BaseNotification bOther = (BaseNotification) other;
m_id = bOther.m_id;
m_filter = bOther.m_filter;
m_properties = new BaseResourcePropertiesEdit();
m_properties.addAll(bOther.m_properties);
m_functions = new Vector();
m_functions.addAll(bOther.m_functions);
if (bOther.m_action != null)
{
m_action = bOther.m_action.getClone();
}
}
/**
* @inheritDoc
*/
public void notify(Event event)
{
if (m_action != null)
{
m_action.notify(this, event);
}
}
/**
* @inheritDoc
*/
public String getFunction()
{
return (String) m_functions.get(0);
}
/**
* @inheritDoc
*/
public String getResourceFilter()
{
return m_filter;
}
/**
* @inheritDoc
*/
public List getFunctions()
{
List rv = new Vector();
rv.addAll(m_functions);
return rv;
}
/**
* @inheritDoc
*/
public boolean containsFunction(String function)
{
return m_functions.contains(function);
}
/**
* @inheritDoc
*/
public NotificationAction getAction()
{
return m_action;
}
/**
* @inheritDoc
*/
public String getUrl()
{
return notificationUrl(m_id);
}
/**
* @inheritDoc
*/
public String getReference()
{
return notificationReference(m_id);
}
/**
* @inheritDoc
*/
public String getReference(String rootProperty)
{
return getReference();
}
/**
* @inheritDoc
*/
public String getUrl(String rootProperty)
{
return getUrl();
}
/**
* Access the id of the resource.
*
* @return The id.
*/
public String getId()
{
return m_id;
}
/**
* Access the resource's properties.
*
* @return The resource's properties.
*/
public ResourceProperties getProperties()
{
return m_properties;
}
/**
* Serialize the resource into XML, adding an element to the doc under the top of the stack element.
*
* @param doc
* The DOM doc to contain the XML (or null for a string return).
* @param stack
* The DOM elements, the top of which is the containing element of the new "resource" element.
* @return The newly added element.
*/
public Element toXml(Document doc, Stack stack)
{
Element notification = doc.createElement("notification");
if (stack.isEmpty())
{
doc.appendChild(notification);
}
else
{
((Element) stack.peek()).appendChild(notification);
}
stack.push(notification);
notification.setAttribute("id", getId());
// first function
if (m_functions.size() >= 1)
{
notification.setAttribute("function", (String) m_functions.get(0));
}
if (m_filter != null) notification.setAttribute("filter", m_filter);
// properties
m_properties.toXml(doc, stack);
// action
if (m_action != null)
{
Element action = doc.createElement("action");
notification.appendChild(action);
action.setAttribute("class", m_action.getClass().getName());
m_action.toXml(action);
}
// more functions
if (m_functions.size() > 1)
{
for (int i = 1; i < m_functions.size(); i++)
{
String func = (String) m_functions.get(i);
Element funcEl = doc.createElement("function");
notification.appendChild(funcEl);
funcEl.setAttribute("id", func);
}
}
stack.pop();
return notification;
}
}
/**********************************************************************************************************************************************************************************************************************************************************
* NotificationEdit implementation
*********************************************************************************************************************************************************************************************************************************************************/
public class BaseNotificationEdit extends BaseNotification implements NotificationEdit, SessionBindingListener
{
/** The event code for this edit. */
protected String m_event = null;
/** Active flag. */
protected boolean m_active = false;
/**
* Construct.
*
* @param id
* The notification id.
*/
public BaseNotificationEdit(String id)
{
super(id);
} // BaseNotificationEdit
/**
* Construct from an existing definition, in xml.
*
* @param el
* The message in XML in a DOM element.
*/
public BaseNotificationEdit(Element el)
{
super(el);
} // BaseNotificationEdit
/**
* Construct from another Notification.
*
* @param notification
* The other notification to copy values from.
*/
public BaseNotificationEdit(Notification other)
{
super(other);
} // BaseNotificationEdit
/**
* Clean up.
*/
protected void finalize()
{
// catch the case where an edit was made but never resolved
if (m_active)
{
cancelEdit(this);
}
} // finalize
/**
* Set the Event function, clearing any that have already been set.
*
* @param event
* The Event function to watch for.
*/
public void setFunction(String function)
{
m_functions.clear();
m_functions.add(function);
} // setFunction
/**
* Add another Event function.
*
* @param event
* Another Event function to watch for.
*/
public void addFunction(String function)
{
m_functions.add(function);
} // addFunction
/**
* Set the resource reference filter.
*
* @param filter
* The resource reference filter.
*/
public void setResourceFilter(String filter)
{
m_filter = filter;
} // setResourceFilter
/**
* Set the action helper that handles the notify() action.
*
* @param action
* The action helper that handles the notify() action.
*/
public void setAction(NotificationAction action)
{
m_action = action;
} // setAction
/**
* Take all values from this object.
*
* @param other
* The notification object to take values from.
*/
protected void set(Notification other)
{
setAll(other);
} // set
/**
* Access the event code for this edit.
*
* @return The event code for this edit.
*/
protected String getEvent()
{
return m_event;
}
/**
* Set the event code for this edit.
*
* @param event
* The event code for this edit.
*/
protected void setEvent(String event)
{
m_event = event;
}
/**
* Access the resource's properties for modification
*
* @return The resource's properties.
*/
public ResourcePropertiesEdit getPropertiesEdit()
{
return m_properties;
} // getPropertiesEdit
/**
* Enable editing.
*/
protected void activate()
{
m_active = true;
} // activate
/**
* Check to see if the edit is still active, or has already been closed.
*
* @return true if the edit is active, false if it's been closed.
*/
public boolean isActiveEdit()
{
return m_active;
} // isActiveEdit
/**
* Close the edit object - it cannot be used after this.
*/
protected void closeEdit()
{
m_active = false;
} // closeEdit
/******************************************************************************************************************************************************************************************************************************************************
* SessionBindingListener implementation
*****************************************************************************************************************************************************************************************************************************************************/
public void valueBound(SessionBindingEvent event)
{
}
public void valueUnbound(SessionBindingEvent event)
{
if (M_log.isDebugEnabled()) M_log.debug(this + ".valueUnbound()");
// catch the case where an edit was made but never resolved
if (m_active)
{
cancelEdit(this);
}
} // valueUnbound
} // BaseNotificationEdit
} // BaseNotificationService