0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/event/tags/sakai_2-4-1/event-impl/impl/src/java/org/sakaiproject/event/impl/BaseNotificationService.java $
0003: * $Id: BaseNotificationService.java 13804 2006-08-17 02:47:37Z ggolden@umich.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.event.impl;
0021:
0022: import java.util.Iterator;
0023: import java.util.List;
0024: import java.util.Observable;
0025: import java.util.Observer;
0026: import java.util.Stack;
0027: import java.util.Vector;
0028:
0029: import org.apache.commons.logging.Log;
0030: import org.apache.commons.logging.LogFactory;
0031: import org.sakaiproject.component.api.ServerConfigurationService;
0032: import org.sakaiproject.component.cover.ComponentManager;
0033: import org.sakaiproject.entity.api.Edit;
0034: import org.sakaiproject.entity.api.Entity;
0035: import org.sakaiproject.entity.api.ResourceProperties;
0036: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0037: import org.sakaiproject.event.api.Event;
0038: import org.sakaiproject.event.api.EventTrackingService;
0039: import org.sakaiproject.event.api.Notification;
0040: import org.sakaiproject.event.api.NotificationAction;
0041: import org.sakaiproject.event.api.NotificationEdit;
0042: import org.sakaiproject.event.api.NotificationLockedException;
0043: import org.sakaiproject.event.api.NotificationNotDefinedException;
0044: import org.sakaiproject.event.api.NotificationService;
0045: import org.sakaiproject.id.api.IdManager;
0046: import org.sakaiproject.memory.api.CacheRefresher;
0047: import org.sakaiproject.time.api.Time;
0048: import org.sakaiproject.util.BaseResourcePropertiesEdit;
0049: import org.sakaiproject.util.StorageUser;
0050: import org.sakaiproject.util.StringUtil;
0051: import org.sakaiproject.tool.api.SessionBindingEvent;
0052: import org.sakaiproject.tool.api.SessionBindingListener;
0053: import org.w3c.dom.Document;
0054: import org.w3c.dom.Element;
0055: import org.w3c.dom.Node;
0056: import org.w3c.dom.NodeList;
0057:
0058: /**
0059: * <p>
0060: * BaseNotificationService ...
0061: * </p>
0062: */
0063: public abstract class BaseNotificationService implements
0064: NotificationService, Observer, StorageUser, CacheRefresher {
0065: /** Our logger. */
0066: private static Log M_log = LogFactory
0067: .getLog(BaseNotificationService.class);
0068:
0069: /** Storage manager for this service. */
0070: protected Storage m_storage = null;
0071:
0072: /** A Cache for this service - Notification objects stored by notification reference. */
0073: protected NotificationCache m_cache = null;
0074:
0075: /** The initial portion of a relative access point URL. */
0076: protected String m_relativeAccessPoint = null;
0077:
0078: /** Transient notifications (NotificationEdit). */
0079: protected List m_transients = null;
0080:
0081: /**********************************************************************************************************************************************************************************************************************************************************
0082: * Abstractions, etc.
0083: *********************************************************************************************************************************************************************************************************************************************************/
0084:
0085: /**
0086: * Construct storage for this service.
0087: */
0088: protected abstract Storage newStorage();
0089:
0090: /**
0091: * Does the resource reference match the filter?
0092: *
0093: * @param filter
0094: * The resource reference filter.
0095: * @param ref
0096: * The resource reference string.
0097: * @return true if the filter matches the ref, false if not.
0098: */
0099: protected boolean match(String filter, String ref) {
0100: if (filter == null)
0101: return true;
0102: if (filter.length() == 0)
0103: return true;
0104:
0105: if (ref.startsWith(filter))
0106: return true;
0107:
0108: return false;
0109: }
0110:
0111: /**
0112: * Access the partial URL that forms the root of resource URLs.
0113: *
0114: * @param relative
0115: * if true, form within the access path only (i.e. starting with /content)
0116: * @return the partial URL that forms the root of resource URLs.
0117: */
0118: protected String getAccessPoint(boolean relative) {
0119: return (relative ? "" : serverConfigurationService()
0120: .getAccessUrl())
0121: + m_relativeAccessPoint;
0122: }
0123:
0124: /**
0125: * Access the notification id extracted from a notification reference.
0126: *
0127: * @param ref
0128: * The notification reference string.
0129: * @return The the notification id extracted from a notification reference.
0130: */
0131: protected String notificationId(String ref) {
0132: String start = getAccessPoint(true) + Entity.SEPARATOR;
0133: int i = ref.indexOf(start);
0134: if (i == -1)
0135: return ref;
0136: String id = ref.substring(i + start.length());
0137: return id;
0138: }
0139:
0140: /**
0141: * Access the external URL which can be used to access the resource from outside the system.
0142: *
0143: * @param id
0144: * The notification id.
0145: * @return The the external URL which can be used to access the resource from outside the system.
0146: */
0147: protected String notificationUrl(String id) {
0148: return getAccessPoint(false) + Entity.SEPARATOR + id;
0149: }
0150:
0151: /**********************************************************************************************************************************************************************************************************************************************************
0152: * Dependencies
0153: *********************************************************************************************************************************************************************************************************************************************************/
0154:
0155: /**
0156: * @return the EventTrackingService collaborator.
0157: */
0158: protected abstract EventTrackingService eventTrackingService();
0159:
0160: /**
0161: * @return the ServerConfigurationService collaborator.
0162: */
0163: protected abstract ServerConfigurationService serverConfigurationService();
0164:
0165: /**
0166: * @return the IdManager collaborator.
0167: */
0168: protected abstract IdManager idManager();
0169:
0170: /**********************************************************************************************************************************************************************************************************************************************************
0171: * Configuration
0172: *********************************************************************************************************************************************************************************************************************************************************/
0173:
0174: /** Configuration: make the email notifications To: reply-able. */
0175: protected boolean m_emailsToReplyable = false;
0176:
0177: /**
0178: * Configuration: set reply-able status for email notifications in the To:.
0179: *
0180: * @param value
0181: * The setting
0182: */
0183: public void setEmailToReplyable(boolean value) {
0184: m_emailsToReplyable = value;
0185: }
0186:
0187: /** Configuration: make the email notifications From: reply-able. */
0188: protected boolean m_emailsFromReplyable = false;
0189:
0190: /**
0191: * Configuration: set reply-able status for email notifications in the From:.
0192: *
0193: * @param value
0194: * The setting
0195: */
0196: public void setEmailFromReplyable(boolean value) {
0197: m_emailsFromReplyable = value;
0198: }
0199:
0200: /**********************************************************************************************************************************************************************************************************************************************************
0201: * Init and Destroy
0202: *********************************************************************************************************************************************************************************************************************************************************/
0203:
0204: /**
0205: * Final initialization, once all dependencies are set.
0206: */
0207: public void init() {
0208: try {
0209: // prepare for transients
0210: m_transients = new Vector();
0211:
0212: m_relativeAccessPoint = REFERENCE_ROOT;
0213:
0214: M_log.info(this + ".init()");
0215:
0216: // construct storage and read
0217: m_storage = newStorage();
0218: m_storage.open();
0219:
0220: // make the cache
0221: m_cache = new NotificationCache(this ,
0222: notificationReference(""));
0223:
0224: // start watching the events - only those generated on this server, not those from elsewhere
0225: eventTrackingService().addLocalObserver(this );
0226:
0227: M_log.info(this + ".init()");
0228: } catch (Throwable t) {
0229: M_log.warn(this + ".init(): ", t);
0230: }
0231: }
0232:
0233: /**
0234: * Returns to uninitialized state.
0235: */
0236: public void destroy() {
0237: // if we are not in a global shutdown, remove my event notification registration
0238: if (!ComponentManager.hasBeenClosed()) {
0239: eventTrackingService().deleteObserver(this );
0240: }
0241:
0242: // clean up cache
0243: m_cache.clear();
0244: m_cache = null;
0245:
0246: // clean up storage
0247: m_storage.close();
0248: m_storage = null;
0249:
0250: // clean up transients
0251: m_transients.clear();
0252: m_transients = null;
0253:
0254: M_log.info(this + ".destroy()");
0255: }
0256:
0257: /**********************************************************************************************************************************************************************************************************************************************************
0258: * NotificationService implementation
0259: *********************************************************************************************************************************************************************************************************************************************************/
0260:
0261: /**
0262: * @inheritDoc
0263: */
0264: public NotificationEdit addNotification() {
0265: // check security (throws if not permitted)
0266: // unlock(SECURE_ADD_NOTIFICATION, notificationReference(id));
0267:
0268: // get a new unique id
0269: String id = idManager().createUuid();
0270:
0271: // reserve a notification with this id from the info store - if it's in use, this will return null
0272: NotificationEdit notification = m_storage.put(id);
0273: /*
0274: * if (notification == null) { throw new IdUsedException(id); }
0275: */
0276:
0277: ((BaseNotificationEdit) notification)
0278: .setEvent(SECURE_ADD_NOTIFICATION);
0279:
0280: return notification;
0281: }
0282:
0283: /**
0284: * @inheritDoc
0285: */
0286: public NotificationEdit addTransientNotification() {
0287: // the id is not unique, and not really used
0288: String id = "transient";
0289:
0290: // create an object, not through storage
0291: NotificationEdit notification = new BaseNotificationEdit(id);
0292:
0293: // remember it
0294: m_transients.add(notification);
0295:
0296: // no event, no other cluster server knows about it - it's transient and local
0297: return notification;
0298: }
0299:
0300: /**
0301: * @inheritDoc
0302: */
0303: public Notification getNotification(String id)
0304: throws NotificationNotDefinedException {
0305: Notification notification = null;
0306:
0307: // if we have it cached, use it (hit or miss)
0308: String key = notificationReference(id);
0309: if (m_cache.containsKey(key)) {
0310: notification = (Notification) m_cache.get(key);
0311: }
0312:
0313: // if not in the cache, see if we have it in our info store
0314: else {
0315: notification = m_storage.get(id);
0316:
0317: // cache it (hit or miss)
0318: m_cache.put(notification);
0319: }
0320:
0321: // if not found
0322: if (notification == null)
0323: throw new NotificationNotDefinedException(id);
0324:
0325: return notification;
0326: }
0327:
0328: /**
0329: * @inheritDoc
0330: */
0331: public String notificationReference(String id) {
0332: return getAccessPoint(true) + Entity.SEPARATOR + id;
0333: }
0334:
0335: /**
0336: * @inheritDoc
0337: */
0338: public NotificationEdit editNotification(String id)
0339: throws NotificationNotDefinedException,
0340: NotificationLockedException {
0341: // check security (throws if not permitted)
0342: // unlock(SECURE_UPDATE_NOTIFICATION, notificationReference(id));
0343:
0344: // check for existance
0345: if ((m_cache.get(notificationReference(id)) == null)
0346: && (!m_storage.check(id))) {
0347: throw new NotificationNotDefinedException(id);
0348: }
0349:
0350: // ignore the cache - get the notification with a lock from the info store
0351: NotificationEdit notification = m_storage.edit(id);
0352: if (notification == null)
0353: throw new NotificationLockedException(id);
0354:
0355: ((BaseNotificationEdit) notification)
0356: .setEvent(SECURE_UPDATE_NOTIFICATION);
0357:
0358: return notification;
0359: }
0360:
0361: /**
0362: * @inheritDoc
0363: */
0364: public void commitEdit(NotificationEdit notification) {
0365: // check for closed edit
0366: if (!notification.isActiveEdit()) {
0367: try {
0368: throw new Exception();
0369: } catch (Exception e) {
0370: M_log.warn(this
0371: + ".commitEdit(): closed NotificationEdit", e);
0372: }
0373: return;
0374: }
0375:
0376: // update the properties
0377: // addLiveUpdateProperties(notification.getPropertiesEdit());
0378:
0379: // complete the edit
0380: m_storage.commit(notification);
0381:
0382: // track it
0383: eventTrackingService().post(
0384: eventTrackingService().newEvent(
0385: ((BaseNotificationEdit) notification)
0386: .getEvent(),
0387: notification.getReference(), true));
0388:
0389: // close the edit object
0390: ((BaseNotificationEdit) notification).closeEdit();
0391: }
0392:
0393: /**
0394: * @inheritDoc
0395: */
0396: public void cancelEdit(NotificationEdit notification) {
0397: // check for closed edit
0398: if (!notification.isActiveEdit()) {
0399: try {
0400: throw new Exception();
0401: } catch (Exception e) {
0402: M_log.warn(this
0403: + ".cancelEdit(): closed NotificationEdit", e);
0404: }
0405: return;
0406: }
0407:
0408: // release the edit lock
0409: m_storage.cancel(notification);
0410:
0411: // close the edit object
0412: ((BaseNotificationEdit) notification).closeEdit();
0413: }
0414:
0415: /**
0416: * @inheritDoc
0417: */
0418: public void removeNotification(NotificationEdit notification) {
0419: // check for closed edit
0420: if (!notification.isActiveEdit()) {
0421: try {
0422: throw new Exception();
0423: } catch (Exception e) {
0424: M_log
0425: .warn(
0426: this
0427: + ".removeNotification(): closed NotificationEdit",
0428: e);
0429: }
0430: return;
0431: }
0432:
0433: // check security (throws if not permitted)
0434: // unlock(SECURE_REMOVE_NOTIFICATION, notification.getReference());
0435:
0436: // complete the edit
0437: m_storage.remove(notification);
0438:
0439: // track it
0440: eventTrackingService().post(
0441: eventTrackingService().newEvent(
0442: SECURE_REMOVE_NOTIFICATION,
0443: notification.getReference(), true));
0444:
0445: // close the edit object
0446: ((BaseNotificationEdit) notification).closeEdit();
0447: }
0448:
0449: /**
0450: * @inheritDoc
0451: */
0452: public List getNotifications(String function) {
0453: List notifications = null;
0454:
0455: // if we have disabled the cache, don't use if
0456: if (m_cache.disabled()) {
0457: notifications = m_storage.getAll(function);
0458: }
0459:
0460: else {
0461: // if the cache is complete, use it
0462: if (m_cache.isComplete()) {
0463: notifications = m_cache.getAll(function);
0464: }
0465:
0466: // otherwise get all the notifications from storage
0467: else {
0468: // Note: while we are getting from storage, storage might change. These can be processed
0469: // after we get the storage entries, and put them in the cache, and mark the cache complete.
0470: // -ggolden
0471: synchronized (m_cache) {
0472: // if we were waiting and it's now complete...
0473: if (m_cache.isComplete()) {
0474: notifications = m_cache.getAll(function);
0475: }
0476:
0477: else {
0478: // save up any events to the cache until we get past this load
0479: m_cache.holdEvents();
0480:
0481: // get them all for caching
0482: List all = m_storage.getAll();
0483:
0484: // update the cache, and mark it complete
0485: for (int i = 0; i < all.size(); i++) {
0486: Notification notification = (Notification) all
0487: .get(i);
0488: m_cache.put(notification);
0489: }
0490:
0491: m_cache.setComplete();
0492:
0493: // get those for just this function
0494: notifications = m_cache.getAll(function);
0495:
0496: // now we are complete, process any cached events
0497: m_cache.processEvents();
0498: }
0499: }
0500: }
0501: }
0502:
0503: // if none found in storage
0504: if (notifications == null) {
0505: notifications = new Vector();
0506: }
0507:
0508: // add transients
0509: for (Iterator it = m_transients.iterator(); it.hasNext();) {
0510: Notification notification = (Notification) it.next();
0511: if (notification.containsFunction(function)) {
0512: notifications.add(notification);
0513: }
0514: }
0515:
0516: return notifications;
0517: }
0518:
0519: /**
0520: * @inheritDoc
0521: */
0522: public Notification findNotification(String function, String filter) {
0523: // start with all those for this function (just 'cause we have a nice method to get them -ggolden)
0524: List notifications = getNotifications(function);
0525: for (Iterator iNotifications = notifications.iterator(); iNotifications
0526: .hasNext();) {
0527: Notification notification = (Notification) iNotifications
0528: .next();
0529: if (notification.getResourceFilter().equals(filter)) {
0530: return notification;
0531: }
0532: }
0533:
0534: return null;
0535: }
0536:
0537: /**
0538: * {@inheritDoc}
0539: */
0540: public boolean isNotificationToReplyable() {
0541: return this .m_emailsToReplyable;
0542: }
0543:
0544: /**
0545: * {@inheritDoc}
0546: */
0547: public boolean isNotificationFromReplyable() {
0548: return this .m_emailsFromReplyable;
0549: }
0550:
0551: /**********************************************************************************************************************************************************************************************************************************************************
0552: * Observer implementation
0553: *********************************************************************************************************************************************************************************************************************************************************/
0554:
0555: /**
0556: * 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
0557: * cause the courier service to deliver to the interface controlled by my controller. Extensions can override.
0558: *
0559: * @param o
0560: * the observable object.
0561: * @param arg
0562: * an argument passed to the <code>notifyObservers</code> method.
0563: */
0564: public void update(Observable o, Object arg) {
0565: // arg is Event
0566: if (!(arg instanceof Event))
0567: return;
0568: Event event = (Event) arg;
0569:
0570: // check the event function against the functions we have notifications watching for
0571: String function = event.getEvent();
0572:
0573: // for each notification watching for this event
0574: List notifications = getNotifications(function);
0575: for (Iterator it = notifications.iterator(); it.hasNext();) {
0576: Notification notification = (Notification) it.next();
0577:
0578: // if the resource matches the notification's resource filter
0579: if (match(notification.getResourceFilter(), event
0580: .getResource())) {
0581: // cause the notification to run
0582: notification.notify(event);
0583: }
0584: }
0585:
0586: } // update
0587:
0588: /**********************************************************************************************************************************************************************************************************************************************************
0589: * Storage
0590: *********************************************************************************************************************************************************************************************************************************************************/
0591:
0592: protected interface Storage {
0593: /**
0594: * Open and be ready to read / write.
0595: */
0596: public void open();
0597:
0598: /**
0599: * Close.
0600: */
0601: public void close();
0602:
0603: /**
0604: * Check if a notification by this id exists.
0605: *
0606: * @param id
0607: * The notification id.
0608: * @return true if a nitificaion by this id exists, false if not.
0609: */
0610: public boolean check(String id);
0611:
0612: /**
0613: * Add a new notification with this id.
0614: *
0615: * @param id
0616: * The notification id.
0617: * @return The locked notification with this id, or null if in use.
0618: */
0619: public NotificationEdit put(String id);
0620:
0621: /**
0622: * Get the notification with this id, or null if not found.
0623: *
0624: * @param id
0625: * The notification id.
0626: * @return The notification with this id, or null if not found.
0627: */
0628: public Notification get(String id);
0629:
0630: /**
0631: * Get a List of all the notifications that are interested in this Event function.
0632: *
0633: * @param function
0634: * The Event function
0635: * @return The List (Notification) of all the notifications that are interested in this Event function.
0636: */
0637: public List getAll(String function);
0638:
0639: /**
0640: * Get a List of all notifications.
0641: *
0642: * @return The List (Notification) of all notifications.
0643: */
0644: public List getAll();
0645:
0646: /**
0647: * Get a lock on the notification with this id, or null if a lock cannot be gotten.
0648: *
0649: * @param id
0650: * The user id.
0651: * @return The locked Notification with this id, or null if this records cannot be locked.
0652: */
0653: public NotificationEdit edit(String id);
0654:
0655: /**
0656: * Commit the changes and release the lock.
0657: *
0658: * @param user
0659: * The notification to commit.
0660: */
0661: public void commit(NotificationEdit notification);
0662:
0663: /**
0664: * Cancel the changes and release the lock.
0665: *
0666: * @param user
0667: * The notification to commit.
0668: */
0669: public void cancel(NotificationEdit notification);
0670:
0671: /**
0672: * Remove this notification.
0673: *
0674: * @param user
0675: * The notification to remove.
0676: */
0677: public void remove(NotificationEdit notification);
0678:
0679: } // Storage
0680:
0681: /**********************************************************************************************************************************************************************************************************************************************************
0682: * StorageUser implementation
0683: *********************************************************************************************************************************************************************************************************************************************************/
0684:
0685: /**
0686: * Construct a new continer given just an id.
0687: *
0688: * @param id
0689: * The id for the new object.
0690: * @return The new containe Resource.
0691: */
0692: public Entity newContainer(String ref) {
0693: return null;
0694: }
0695:
0696: /**
0697: * Construct a new container resource, from an XML element.
0698: *
0699: * @param element
0700: * The XML.
0701: * @return The new container resource.
0702: */
0703: public Entity newContainer(Element element) {
0704: return null;
0705: }
0706:
0707: /**
0708: * Construct a new container resource, as a copy of another
0709: *
0710: * @param other
0711: * The other contianer to copy.
0712: * @return The new container resource.
0713: */
0714: public Entity newContainer(Entity other) {
0715: return null;
0716: }
0717:
0718: /**
0719: * Construct a new rsource given just an id.
0720: *
0721: * @param container
0722: * The Resource that is the container for the new resource (may be null).
0723: * @param id
0724: * The id for the new object.
0725: * @param others
0726: * (options) array of objects to load into the Resource's fields.
0727: * @return The new resource.
0728: */
0729: public Entity newResource(Entity container, String id,
0730: Object[] others) {
0731: return new BaseNotification(id);
0732: }
0733:
0734: /**
0735: * Construct a new resource, from an XML element.
0736: *
0737: * @param container
0738: * The Resource that is the container for the new resource (may be null).
0739: * @param element
0740: * The XML.
0741: * @return The new resource from the XML.
0742: */
0743: public Entity newResource(Entity container, Element element) {
0744: return new BaseNotification(element);
0745: }
0746:
0747: /**
0748: * Construct a new resource from another resource of the same type.
0749: *
0750: * @param container
0751: * The Resource that is the container for the new resource (may be null).
0752: * @param other
0753: * The other resource.
0754: * @return The new resource as a copy of the other.
0755: */
0756: public Entity newResource(Entity container, Entity other) {
0757: return new BaseNotification((Notification) other);
0758: }
0759:
0760: /**
0761: * Construct a new continer given just an id.
0762: *
0763: * @param id
0764: * The id for the new object.
0765: * @return The new containe Resource.
0766: */
0767: public Edit newContainerEdit(String ref) {
0768: return null;
0769: }
0770:
0771: /**
0772: * Construct a new container resource, from an XML element.
0773: *
0774: * @param element
0775: * The XML.
0776: * @return The new container resource.
0777: */
0778: public Edit newContainerEdit(Element element) {
0779: return null;
0780: }
0781:
0782: /**
0783: * Construct a new container resource, as a copy of another
0784: *
0785: * @param other
0786: * The other contianer to copy.
0787: * @return The new container resource.
0788: */
0789: public Edit newContainerEdit(Entity other) {
0790: return null;
0791: }
0792:
0793: /**
0794: * Construct a new rsource given just an id.
0795: *
0796: * @param container
0797: * The Resource that is the container for the new resource (may be null).
0798: * @param id
0799: * The id for the new object.
0800: * @param others
0801: * (options) array of objects to load into the Resource's fields.
0802: * @return The new resource.
0803: */
0804: public Edit newResourceEdit(Entity container, String id,
0805: Object[] others) {
0806: BaseNotificationEdit e = new BaseNotificationEdit(id);
0807: e.activate();
0808: return e;
0809: }
0810:
0811: /**
0812: * Construct a new resource, from an XML element.
0813: *
0814: * @param container
0815: * The Resource that is the container for the new resource (may be null).
0816: * @param element
0817: * The XML.
0818: * @return The new resource from the XML.
0819: */
0820: public Edit newResourceEdit(Entity container, Element element) {
0821: BaseNotificationEdit e = new BaseNotificationEdit(element);
0822: e.activate();
0823: return e;
0824: }
0825:
0826: /**
0827: * Construct a new resource from another resource of the same type.
0828: *
0829: * @param container
0830: * The Resource that is the container for the new resource (may be null).
0831: * @param other
0832: * The other resource.
0833: * @return The new resource as a copy of the other.
0834: */
0835: public Edit newResourceEdit(Entity container, Entity other) {
0836: BaseNotificationEdit e = new BaseNotificationEdit(
0837: (Notification) other);
0838: e.activate();
0839: return e;
0840: }
0841:
0842: /**
0843: * Collect the fields that need to be stored outside the XML (for the resource).
0844: *
0845: * @return An array of field values to store in the record outside the XML (for the resource).
0846: */
0847: public Object[] storageFields(Entity r) {
0848: return null;
0849: }
0850:
0851: /**
0852: * Check if this resource is in draft mode.
0853: *
0854: * @param r
0855: * The resource.
0856: * @return true if the resource is in draft mode, false if not.
0857: */
0858: public boolean isDraft(Entity r) {
0859: return false;
0860: }
0861:
0862: /**
0863: * Access the resource owner user id.
0864: *
0865: * @param r
0866: * The resource.
0867: * @return The resource owner user id.
0868: */
0869: public String getOwnerId(Entity r) {
0870: return null;
0871: }
0872:
0873: /**
0874: * Access the resource date.
0875: *
0876: * @param r
0877: * The resource.
0878: * @return The resource date.
0879: */
0880: public Time getDate(Entity r) {
0881: return null;
0882: }
0883:
0884: /**********************************************************************************************************************************************************************************************************************************************************
0885: * CacheRefresher implementation (no container)
0886: *********************************************************************************************************************************************************************************************************************************************************/
0887:
0888: /**
0889: * Get a new value for this key whose value has already expired in the cache.
0890: *
0891: * @param key
0892: * The key whose value has expired and needs to be refreshed.
0893: * @param oldValue
0894: * The old exipred value of the key.
0895: * @param event
0896: * The event which triggered this refresh.
0897: * @return a new value for use in the cache for this key; if null, the entry will be removed.
0898: */
0899: public Object refresh(Object key, Object oldValue, Event event) {
0900: // key is a reference, but our storage wants an id
0901: String id = notificationId((String) key);
0902:
0903: // get this from storage
0904: Notification notification = m_storage.get(id);
0905:
0906: if (M_log.isDebugEnabled())
0907: M_log.debug(this + ".refresh(): " + key + " : " + id);
0908:
0909: return notification;
0910:
0911: } // refresh
0912:
0913: /**********************************************************************************************************************************************************************************************************************************************************
0914: * Notification implementation
0915: *********************************************************************************************************************************************************************************************************************************************************/
0916:
0917: public class BaseNotification implements Notification {
0918: /** The Event(s) function we are watching for. */
0919: protected List m_functions = null;
0920:
0921: /** The resource reference filter. */
0922: protected String m_filter = null;
0923:
0924: /** The resource id. */
0925: protected String m_id = null;
0926:
0927: /** The resource properties. */
0928: protected ResourcePropertiesEdit m_properties = null;
0929:
0930: /** The action helper class. */
0931: protected NotificationAction m_action = null;
0932:
0933: /**
0934: * Construct.
0935: *
0936: * @param id
0937: * The id to use.
0938: */
0939: public BaseNotification(String id) {
0940: // generate a new id
0941: m_id = id;
0942:
0943: // setup for properties
0944: m_properties = new BaseResourcePropertiesEdit();
0945:
0946: // setup for functions
0947: m_functions = new Vector();
0948: }
0949:
0950: /**
0951: * @inheritDoc
0952: */
0953: public BaseNotification(Notification other) {
0954: setAll(other);
0955: }
0956:
0957: /**
0958: * @inheritDoc
0959: */
0960: public BaseNotification(Element el) {
0961: // setup for properties
0962: m_properties = new BaseResourcePropertiesEdit();
0963:
0964: // setup for functions
0965: m_functions = new Vector();
0966:
0967: m_id = el.getAttribute("id");
0968:
0969: // the first function
0970: String func = StringUtil.trimToNull(el
0971: .getAttribute("function"));
0972: if (func != null) {
0973: m_functions.add(func);
0974: }
0975:
0976: m_filter = StringUtil.trimToNull(el.getAttribute("filter"));
0977:
0978: // the children (properties, action helper)
0979: NodeList children = el.getChildNodes();
0980: final int length = children.getLength();
0981: for (int i = 0; i < length; i++) {
0982: Node child = children.item(i);
0983: if (child.getNodeType() != Node.ELEMENT_NODE)
0984: continue;
0985: Element element = (Element) child;
0986:
0987: // look for properties
0988: if (element.getTagName().equals("properties")) {
0989: // re-create properties
0990: m_properties = new BaseResourcePropertiesEdit(
0991: element);
0992: }
0993:
0994: // look for the helper element
0995: else if (element.getTagName().equals("action")) {
0996: // the class name
0997: String className = StringUtil.trimToNull(element
0998: .getAttribute("class"));
0999: if (className != null) {
1000: // create the class
1001: try {
1002: m_action = (NotificationAction) Class
1003: .forName(className).newInstance();
1004:
1005: // let it pick up it's settings
1006: m_action.set(element);
1007: } catch (Exception e) {
1008: M_log
1009: .warn(this
1010: + " exception creating action helper: "
1011: + e.toString());
1012: }
1013: }
1014: } else if (element.getTagName().equals("function")) {
1015: func = StringUtil.trimToNull(element
1016: .getAttribute("id"));
1017: m_functions.add(func);
1018: }
1019: }
1020: }
1021:
1022: /**
1023: * @inheritDoc
1024: */
1025: protected void setAll(Notification other) {
1026: BaseNotification bOther = (BaseNotification) other;
1027: m_id = bOther.m_id;
1028: m_filter = bOther.m_filter;
1029:
1030: m_properties = new BaseResourcePropertiesEdit();
1031: m_properties.addAll(bOther.m_properties);
1032:
1033: m_functions = new Vector();
1034: m_functions.addAll(bOther.m_functions);
1035:
1036: if (bOther.m_action != null) {
1037: m_action = bOther.m_action.getClone();
1038: }
1039: }
1040:
1041: /**
1042: * @inheritDoc
1043: */
1044: public void notify(Event event) {
1045: if (m_action != null) {
1046: m_action.notify(this , event);
1047: }
1048: }
1049:
1050: /**
1051: * @inheritDoc
1052: */
1053: public String getFunction() {
1054: return (String) m_functions.get(0);
1055: }
1056:
1057: /**
1058: * @inheritDoc
1059: */
1060: public String getResourceFilter() {
1061: return m_filter;
1062: }
1063:
1064: /**
1065: * @inheritDoc
1066: */
1067: public List getFunctions() {
1068: List rv = new Vector();
1069: rv.addAll(m_functions);
1070:
1071: return rv;
1072: }
1073:
1074: /**
1075: * @inheritDoc
1076: */
1077: public boolean containsFunction(String function) {
1078: return m_functions.contains(function);
1079: }
1080:
1081: /**
1082: * @inheritDoc
1083: */
1084: public NotificationAction getAction() {
1085: return m_action;
1086: }
1087:
1088: /**
1089: * @inheritDoc
1090: */
1091: public String getUrl() {
1092: return notificationUrl(m_id);
1093: }
1094:
1095: /**
1096: * @inheritDoc
1097: */
1098: public String getReference() {
1099: return notificationReference(m_id);
1100: }
1101:
1102: /**
1103: * @inheritDoc
1104: */
1105: public String getReference(String rootProperty) {
1106: return getReference();
1107: }
1108:
1109: /**
1110: * @inheritDoc
1111: */
1112: public String getUrl(String rootProperty) {
1113: return getUrl();
1114: }
1115:
1116: /**
1117: * Access the id of the resource.
1118: *
1119: * @return The id.
1120: */
1121: public String getId() {
1122: return m_id;
1123: }
1124:
1125: /**
1126: * Access the resource's properties.
1127: *
1128: * @return The resource's properties.
1129: */
1130: public ResourceProperties getProperties() {
1131: return m_properties;
1132: }
1133:
1134: /**
1135: * Serialize the resource into XML, adding an element to the doc under the top of the stack element.
1136: *
1137: * @param doc
1138: * The DOM doc to contain the XML (or null for a string return).
1139: * @param stack
1140: * The DOM elements, the top of which is the containing element of the new "resource" element.
1141: * @return The newly added element.
1142: */
1143: public Element toXml(Document doc, Stack stack) {
1144: Element notification = doc.createElement("notification");
1145: if (stack.isEmpty()) {
1146: doc.appendChild(notification);
1147: } else {
1148: ((Element) stack.peek()).appendChild(notification);
1149: }
1150:
1151: stack.push(notification);
1152:
1153: notification.setAttribute("id", getId());
1154:
1155: // first function
1156: if (m_functions.size() >= 1) {
1157: notification.setAttribute("function",
1158: (String) m_functions.get(0));
1159: }
1160:
1161: if (m_filter != null)
1162: notification.setAttribute("filter", m_filter);
1163:
1164: // properties
1165: m_properties.toXml(doc, stack);
1166:
1167: // action
1168: if (m_action != null) {
1169: Element action = doc.createElement("action");
1170: notification.appendChild(action);
1171: action.setAttribute("class", m_action.getClass()
1172: .getName());
1173: m_action.toXml(action);
1174: }
1175:
1176: // more functions
1177: if (m_functions.size() > 1) {
1178: for (int i = 1; i < m_functions.size(); i++) {
1179: String func = (String) m_functions.get(i);
1180: Element funcEl = doc.createElement("function");
1181: notification.appendChild(funcEl);
1182: funcEl.setAttribute("id", func);
1183: }
1184: }
1185: stack.pop();
1186:
1187: return notification;
1188: }
1189: }
1190:
1191: /**********************************************************************************************************************************************************************************************************************************************************
1192: * NotificationEdit implementation
1193: *********************************************************************************************************************************************************************************************************************************************************/
1194:
1195: public class BaseNotificationEdit extends BaseNotification
1196: implements NotificationEdit, SessionBindingListener {
1197: /** The event code for this edit. */
1198: protected String m_event = null;
1199:
1200: /** Active flag. */
1201: protected boolean m_active = false;
1202:
1203: /**
1204: * Construct.
1205: *
1206: * @param id
1207: * The notification id.
1208: */
1209: public BaseNotificationEdit(String id) {
1210: super (id);
1211:
1212: } // BaseNotificationEdit
1213:
1214: /**
1215: * Construct from an existing definition, in xml.
1216: *
1217: * @param el
1218: * The message in XML in a DOM element.
1219: */
1220: public BaseNotificationEdit(Element el) {
1221: super (el);
1222:
1223: } // BaseNotificationEdit
1224:
1225: /**
1226: * Construct from another Notification.
1227: *
1228: * @param notification
1229: * The other notification to copy values from.
1230: */
1231: public BaseNotificationEdit(Notification other) {
1232: super (other);
1233:
1234: } // BaseNotificationEdit
1235:
1236: /**
1237: * Clean up.
1238: */
1239: protected void finalize() {
1240: // catch the case where an edit was made but never resolved
1241: if (m_active) {
1242: cancelEdit(this );
1243: }
1244:
1245: } // finalize
1246:
1247: /**
1248: * Set the Event function, clearing any that have already been set.
1249: *
1250: * @param event
1251: * The Event function to watch for.
1252: */
1253: public void setFunction(String function) {
1254: m_functions.clear();
1255: m_functions.add(function);
1256:
1257: } // setFunction
1258:
1259: /**
1260: * Add another Event function.
1261: *
1262: * @param event
1263: * Another Event function to watch for.
1264: */
1265: public void addFunction(String function) {
1266: m_functions.add(function);
1267:
1268: } // addFunction
1269:
1270: /**
1271: * Set the resource reference filter.
1272: *
1273: * @param filter
1274: * The resource reference filter.
1275: */
1276: public void setResourceFilter(String filter) {
1277: m_filter = filter;
1278:
1279: } // setResourceFilter
1280:
1281: /**
1282: * Set the action helper that handles the notify() action.
1283: *
1284: * @param action
1285: * The action helper that handles the notify() action.
1286: */
1287: public void setAction(NotificationAction action) {
1288: m_action = action;
1289:
1290: } // setAction
1291:
1292: /**
1293: * Take all values from this object.
1294: *
1295: * @param other
1296: * The notification object to take values from.
1297: */
1298: protected void set(Notification other) {
1299: setAll(other);
1300:
1301: } // set
1302:
1303: /**
1304: * Access the event code for this edit.
1305: *
1306: * @return The event code for this edit.
1307: */
1308: protected String getEvent() {
1309: return m_event;
1310: }
1311:
1312: /**
1313: * Set the event code for this edit.
1314: *
1315: * @param event
1316: * The event code for this edit.
1317: */
1318: protected void setEvent(String event) {
1319: m_event = event;
1320: }
1321:
1322: /**
1323: * Access the resource's properties for modification
1324: *
1325: * @return The resource's properties.
1326: */
1327: public ResourcePropertiesEdit getPropertiesEdit() {
1328: return m_properties;
1329:
1330: } // getPropertiesEdit
1331:
1332: /**
1333: * Enable editing.
1334: */
1335: protected void activate() {
1336: m_active = true;
1337:
1338: } // activate
1339:
1340: /**
1341: * Check to see if the edit is still active, or has already been closed.
1342: *
1343: * @return true if the edit is active, false if it's been closed.
1344: */
1345: public boolean isActiveEdit() {
1346: return m_active;
1347:
1348: } // isActiveEdit
1349:
1350: /**
1351: * Close the edit object - it cannot be used after this.
1352: */
1353: protected void closeEdit() {
1354: m_active = false;
1355:
1356: } // closeEdit
1357:
1358: /******************************************************************************************************************************************************************************************************************************************************
1359: * SessionBindingListener implementation
1360: *****************************************************************************************************************************************************************************************************************************************************/
1361:
1362: public void valueBound(SessionBindingEvent event) {
1363: }
1364:
1365: public void valueUnbound(SessionBindingEvent event) {
1366: if (M_log.isDebugEnabled())
1367: M_log.debug(this + ".valueUnbound()");
1368:
1369: // catch the case where an edit was made but never resolved
1370: if (m_active) {
1371: cancelEdit(this );
1372: }
1373:
1374: } // valueUnbound
1375:
1376: } // BaseNotificationEdit
1377:
1378: } // BaseNotificationService
|