0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/user/tags/sakai_2-4-1/user-impl/impl/src/java/org/sakaiproject/user/impl/BasePreferencesService.java $
0003: * $Id: BasePreferencesService.java 10977 2006-06-21 18:48:29Z 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.user.impl;
0021:
0022: import java.util.Collection;
0023: import java.util.Hashtable;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Map;
0027: import java.util.Set;
0028: import java.util.Stack;
0029: import java.util.Vector;
0030:
0031: import org.apache.commons.logging.Log;
0032: import org.apache.commons.logging.LogFactory;
0033: import org.sakaiproject.authz.api.FunctionManager;
0034: import org.sakaiproject.authz.api.SecurityService;
0035: import org.sakaiproject.component.api.ServerConfigurationService;
0036: import org.sakaiproject.entity.api.Edit;
0037: import org.sakaiproject.entity.api.Entity;
0038: import org.sakaiproject.entity.api.EntityManager;
0039: import org.sakaiproject.entity.api.HttpAccess;
0040: import org.sakaiproject.entity.api.Reference;
0041: import org.sakaiproject.entity.api.ResourceProperties;
0042: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0043: import org.sakaiproject.event.api.EventTrackingService;
0044: import org.sakaiproject.event.api.NotificationService;
0045: import org.sakaiproject.exception.IdUnusedException;
0046: import org.sakaiproject.exception.IdUsedException;
0047: import org.sakaiproject.exception.InUseException;
0048: import org.sakaiproject.exception.PermissionException;
0049: import org.sakaiproject.memory.api.MemoryService;
0050: import org.sakaiproject.time.api.Time;
0051: import org.sakaiproject.user.api.Preferences;
0052: import org.sakaiproject.user.api.PreferencesEdit;
0053: import org.sakaiproject.user.api.PreferencesService;
0054: import org.sakaiproject.user.api.UserDirectoryService;
0055: import org.sakaiproject.util.BaseResourceProperties;
0056: import org.sakaiproject.util.BaseResourcePropertiesEdit;
0057: import org.sakaiproject.util.StorageUser;
0058: import org.sakaiproject.util.StringUtil;
0059: import org.sakaiproject.tool.api.Session;
0060: import org.sakaiproject.tool.api.SessionBindingEvent;
0061: import org.sakaiproject.tool.api.SessionBindingListener;
0062: import org.sakaiproject.tool.api.SessionManager;
0063: import org.w3c.dom.Document;
0064: import org.w3c.dom.Element;
0065: import org.w3c.dom.Node;
0066: import org.w3c.dom.NodeList;
0067:
0068: /**
0069: * <p>
0070: * BasePreferencesService is a Sakai Preferences implementation.
0071: * </p>
0072: */
0073: public abstract class BasePreferencesService implements
0074: PreferencesService, StorageUser {
0075: /** Our log (commons). */
0076: private static Log M_log = LogFactory
0077: .getLog(BasePreferencesService.class);
0078:
0079: /** Storage manager for this service. */
0080: protected Storage m_storage = null;
0081:
0082: /** The initial portion of a relative access point URL. */
0083: protected String m_relativeAccessPoint = null;
0084:
0085: /** The session cache variable for current user's preferences */
0086: protected String ATTR_PREFERENCE = "attr_preference";
0087:
0088: /** The session cache variable for indicating whether the current user's preference was null when last looked */
0089: protected String ATTR_PREFERENCE_IS_NULL = "attr_preference_is_null";
0090:
0091: /**********************************************************************************************************************************************************************************************************************************************************
0092: * Abstractions, etc.
0093: *********************************************************************************************************************************************************************************************************************************************************/
0094:
0095: /**
0096: * Construct storage for this service.
0097: */
0098: protected abstract Storage newStorage();
0099:
0100: /**
0101: * Access the partial URL that forms the root of resource URLs.
0102: *
0103: * @param relative
0104: * if true, form within the access path only (i.e. starting with /content)
0105: * @return the partial URL that forms the root of resource URLs.
0106: */
0107: protected String getAccessPoint(boolean relative) {
0108: return (relative ? "" : serverConfigurationService()
0109: .getAccessUrl())
0110: + m_relativeAccessPoint;
0111: }
0112:
0113: /**
0114: * @inheritDoc
0115: */
0116: public String preferencesReference(String id) {
0117: return getAccessPoint(true) + Entity.SEPARATOR + id;
0118: }
0119:
0120: /**
0121: * Access the preferences id extracted from a preferences reference.
0122: *
0123: * @param ref
0124: * The preferences reference string.
0125: * @return The the preferences id extracted from a preferences reference.
0126: */
0127: protected String preferencesId(String ref) {
0128: String start = getAccessPoint(true) + Entity.SEPARATOR;
0129: int i = ref.indexOf(start);
0130: if (i == -1)
0131: return ref;
0132: String id = ref.substring(i + start.length());
0133: return id;
0134: }
0135:
0136: /**
0137: * Check security permission.
0138: *
0139: * @param lock
0140: * The lock id string.
0141: * @param resource
0142: * The resource reference string, or null if no resource is involved.
0143: * @return true if allowd, false if not
0144: */
0145: protected boolean unlockCheck(String lock, String resource) {
0146: if (!securityService().unlock(lock, resource)) {
0147: return false;
0148: }
0149:
0150: return true;
0151: }
0152:
0153: /**
0154: * Check security permission.
0155: *
0156: * @param lock
0157: * The lock id string.
0158: * @param resource
0159: * The resource reference string, or null if no resource is involved.
0160: * @exception PermissionException
0161: * Thrown if the user does not have access
0162: */
0163: protected void unlock(String lock, String resource)
0164: throws PermissionException {
0165: if (!unlockCheck(lock, resource)) {
0166: throw new PermissionException(sessionManager()
0167: .getCurrentSessionUserId(), lock, resource);
0168: }
0169: }
0170:
0171: /**********************************************************************************************************************************************************************************************************************************************************
0172: * Dependencies
0173: *********************************************************************************************************************************************************************************************************************************************************/
0174:
0175: /**
0176: * @return the MemoryService collaborator.
0177: */
0178: protected abstract MemoryService memoryService();
0179:
0180: /**
0181: * @return the ServerConfigurationService collaborator.
0182: */
0183: protected abstract ServerConfigurationService serverConfigurationService();
0184:
0185: /**
0186: * @return the EntityManager collaborator.
0187: */
0188: protected abstract EntityManager entityManager();
0189:
0190: /**
0191: * @return the SecurityService collaborator.
0192: */
0193: protected abstract SecurityService securityService();
0194:
0195: /**
0196: * @return the FunctionManager collaborator.
0197: */
0198: protected abstract FunctionManager functionManager();
0199:
0200: /**
0201: * @return the SessionManager collaborator.
0202: */
0203: protected abstract SessionManager sessionManager();
0204:
0205: /**
0206: * @return the EventTrackingService collaborator.
0207: */
0208: protected abstract EventTrackingService eventTrackingService();
0209:
0210: /**
0211: * @return the UserDirectoryService collaborator.
0212: */
0213: protected abstract UserDirectoryService userDirectoryService();
0214:
0215: /**********************************************************************************************************************************************************************************************************************************************************
0216: * Init and Destroy
0217: *********************************************************************************************************************************************************************************************************************************************************/
0218:
0219: /**
0220: * Final initialization, once all dependencies are set.
0221: */
0222: public void init() {
0223: try {
0224: m_relativeAccessPoint = REFERENCE_ROOT;
0225:
0226: // construct storage and read
0227: m_storage = newStorage();
0228: m_storage.open();
0229:
0230: // register as an entity producer
0231: entityManager()
0232: .registerEntityProducer(this , REFERENCE_ROOT);
0233:
0234: // register functions
0235: functionManager().registerFunction(SECURE_ADD_PREFS);
0236: functionManager().registerFunction(SECURE_EDIT_PREFS);
0237: functionManager().registerFunction(SECURE_REMOVE_PREFS);
0238:
0239: M_log.info("init()");
0240: } catch (Throwable t) {
0241: M_log.warn("init(): ", t);
0242: }
0243: }
0244:
0245: /**
0246: * Returns to uninitialized state.
0247: */
0248: public void destroy() {
0249: m_storage.close();
0250: m_storage = null;
0251:
0252: M_log.info("destroy()");
0253: }
0254:
0255: /**********************************************************************************************************************************************************************************************************************************************************
0256: * PreferencesService implementation
0257: *********************************************************************************************************************************************************************************************************************************************************/
0258:
0259: /**
0260: * @inheritDoc
0261: */
0262: public Preferences getPreferences(String id) {
0263: Preferences prefs = findPreferences(id);
0264:
0265: // if not found at all
0266: if (prefs == null) {
0267: // throwaway empty preferences %%%
0268: prefs = new BasePreferences(id);
0269: }
0270:
0271: return prefs;
0272: }
0273:
0274: /**
0275: * @inheritDoc
0276: */
0277: public PreferencesEdit edit(String id) throws PermissionException,
0278: InUseException, IdUnusedException {
0279: // security
0280: unlock(SECURE_EDIT_PREFS, preferencesReference(id));
0281:
0282: // check for existance
0283: if (!m_storage.check(id)) {
0284: throw new IdUnusedException(id);
0285: }
0286:
0287: // ignore the cache - get the user with a lock from the info store
0288: PreferencesEdit edit = m_storage.edit(id);
0289: if (edit == null)
0290: throw new InUseException(id);
0291:
0292: ((BasePreferences) edit).setEvent(SECURE_EDIT_PREFS);
0293:
0294: return edit;
0295: }
0296:
0297: /**
0298: * @inheritDoc
0299: */
0300: public void commit(PreferencesEdit edit) {
0301: // check for closed edit
0302: if (!edit.isActiveEdit()) {
0303: try {
0304: throw new Exception();
0305: } catch (Exception e) {
0306: M_log.warn("commit(): closed PreferencesEdit", e);
0307: }
0308: return;
0309: }
0310:
0311: // update the properties
0312: // addLiveUpdateProperties(user.getPropertiesEdit());
0313:
0314: // complete the edit
0315: m_storage.commit(edit);
0316:
0317: SessionManager sManager = sessionManager();
0318: Session s = sManager.getCurrentSession();
0319:
0320: // update the session cache if the preference is for current session user
0321: if (sManager.getCurrentSessionUserId().equals(edit.getId())) {
0322: s.setAttribute(ATTR_PREFERENCE, new BasePreferences(
0323: (BasePreferences) edit));
0324: s.setAttribute(ATTR_PREFERENCE_IS_NULL, Boolean.FALSE);
0325: }
0326:
0327: // track it
0328: eventTrackingService().post(
0329: eventTrackingService().newEvent(
0330: ((BasePreferences) edit).getEvent(),
0331: edit.getReference(), true));
0332:
0333: // close the edit object
0334: ((BasePreferences) edit).closeEdit();
0335: }
0336:
0337: /**
0338: * @inheritDoc
0339: */
0340: public void cancel(PreferencesEdit edit) {
0341: // if this was an add, remove it
0342: if (((BasePreferences) edit).m_event.equals(SECURE_ADD_PREFS)) {
0343: remove(edit);
0344: } else {
0345: // check for closed edit
0346: if (!edit.isActiveEdit()) {
0347: try {
0348: throw new Exception();
0349: } catch (Exception e) {
0350: M_log.warn("cancel(): closed PreferencesEdit", e);
0351: }
0352: return;
0353: }
0354:
0355: // release the edit lock
0356: m_storage.cancel(edit);
0357:
0358: // close the edit object
0359: ((BasePreferences) edit).closeEdit();
0360: }
0361: }
0362:
0363: /**
0364: * @inheritDoc
0365: */
0366: public void remove(PreferencesEdit edit) {
0367: // check for closed edit
0368: if (!edit.isActiveEdit()) {
0369: try {
0370: throw new Exception();
0371: } catch (Exception e) {
0372: M_log.warn("remove(): closed PreferencesEdit", e);
0373: }
0374: return;
0375: }
0376:
0377: // complete the edit
0378: m_storage.remove(edit);
0379:
0380: // track it
0381: eventTrackingService().post(
0382: eventTrackingService().newEvent(SECURE_REMOVE_PREFS,
0383: edit.getReference(), true));
0384:
0385: // close the edit object
0386: ((BasePreferences) edit).closeEdit();
0387: }
0388:
0389: /**
0390: * Find the preferences object, in cache or storage.
0391: *
0392: * @param id
0393: * The preferences id.
0394: * @return The preferences object found in cache or storage, or null if not found.
0395: */
0396: protected BasePreferences findPreferences(String id) {
0397: BasePreferences prefs = null;
0398:
0399: if (id != null) {
0400: Session session = sessionManager().getCurrentSession();
0401:
0402: if (id.equals(sessionManager().getCurrentSessionUserId())) {
0403: // if the preference is for current user
0404: if (session.getAttribute(ATTR_PREFERENCE_IS_NULL) != null) {
0405: if (!((Boolean) session
0406: .getAttribute(ATTR_PREFERENCE_IS_NULL))
0407: .booleanValue()) {
0408: // if the cache indicate the preference is not null, get the preferences from cache
0409: prefs = new BasePreferences(
0410: (BasePreferences) session
0411: .getAttribute(ATTR_PREFERENCE));
0412: }
0413: } else {
0414: //otherwise, get preferences from storage and update session cache
0415: prefs = (BasePreferences) m_storage.get(id);
0416: if (prefs != null) {
0417: session.setAttribute(ATTR_PREFERENCE_IS_NULL,
0418: Boolean.FALSE);
0419: session.setAttribute(ATTR_PREFERENCE,
0420: new BasePreferences(prefs));
0421: } else {
0422: session.setAttribute(ATTR_PREFERENCE_IS_NULL,
0423: Boolean.TRUE);
0424: session.removeAttribute(ATTR_PREFERENCE);
0425: }
0426: }
0427: } else {
0428: // uf the preference is not for current user, ignore cache completely
0429: prefs = (BasePreferences) m_storage.get(id);
0430: }
0431: }
0432:
0433: return prefs;
0434: }
0435:
0436: /**
0437: * @inheritDoc
0438: */
0439: public boolean allowUpdate(String id) {
0440: return unlockCheck(SECURE_EDIT_PREFS, preferencesReference(id));
0441: }
0442:
0443: /**
0444: * @inheritDoc
0445: */
0446: public PreferencesEdit add(String id) throws PermissionException,
0447: IdUsedException {
0448: // check security (throws if not permitted)
0449: unlock(SECURE_ADD_PREFS, preferencesReference(id));
0450:
0451: // reserve a user with this id from the info store - if it's in use, this will return null
0452: PreferencesEdit edit = m_storage.put(id);
0453: if (edit == null) {
0454: throw new IdUsedException(id);
0455: }
0456:
0457: ((BasePreferences) edit).setEvent(SECURE_ADD_PREFS);
0458:
0459: return edit;
0460: }
0461:
0462: /**********************************************************************************************************************************************************************************************************************************************************
0463: * EntityProducer implementation
0464: *********************************************************************************************************************************************************************************************************************************************************/
0465:
0466: /**
0467: * @inheritDoc
0468: */
0469: public String getLabel() {
0470: return "preferences";
0471: }
0472:
0473: /**
0474: * @inheritDoc
0475: */
0476: public boolean willArchiveMerge() {
0477: return false;
0478: }
0479:
0480: /**
0481: * @inheritDoc
0482: */
0483: public HttpAccess getHttpAccess() {
0484: return null;
0485: }
0486:
0487: /**
0488: * @inheritDoc
0489: */
0490: public boolean parseEntityReference(String reference, Reference ref) {
0491: // for preferences access
0492: if (reference.startsWith(REFERENCE_ROOT)) {
0493: String id = null;
0494:
0495: // we will get null, service, user/preferences Id
0496: String[] parts = StringUtil.split(reference,
0497: Entity.SEPARATOR);
0498:
0499: if (parts.length > 2) {
0500: id = parts[2];
0501: }
0502:
0503: ref.set(APPLICATION_ID, null, id, null, null);
0504:
0505: return true;
0506: }
0507:
0508: return false;
0509: }
0510:
0511: /**
0512: * @inheritDoc
0513: */
0514: public String getEntityDescription(Reference ref) {
0515: return null;
0516: }
0517:
0518: /**
0519: * @inheritDoc
0520: */
0521: public ResourceProperties getEntityResourceProperties(Reference ref) {
0522: return null;
0523: }
0524:
0525: /**
0526: * @inheritDoc
0527: */
0528: public Entity getEntity(Reference ref) {
0529: return null;
0530: }
0531:
0532: /**
0533: * @inheritDoc
0534: */
0535: public Collection getEntityAuthzGroups(Reference ref, String userId) {
0536: // double check that it's mine
0537: if (APPLICATION_ID != ref.getType())
0538: return null;
0539:
0540: Collection rv = new Vector();
0541:
0542: // for preferences access: no additional role realms
0543: try {
0544: rv.add(userDirectoryService().userReference(ref.getId()));
0545:
0546: ref.addUserTemplateAuthzGroup(rv, userId);
0547: } catch (NullPointerException e) {
0548: M_log.warn("getEntityAuthzGroups(): " + e);
0549: }
0550:
0551: return rv;
0552: }
0553:
0554: /**
0555: * @inheritDoc
0556: */
0557: public String getEntityUrl(Reference ref) {
0558: return null;
0559: }
0560:
0561: /**
0562: * @inheritDoc
0563: */
0564: public String archive(String siteId, Document doc, Stack stack,
0565: String archivePath, List attachments) {
0566: return "";
0567: }
0568:
0569: /**
0570: * @inheritDoc
0571: */
0572: public String merge(String siteId, Element root,
0573: String archivePath, String fromSiteId, Map attachmentNames,
0574: Map userIdTrans, Set userListAllowImport) {
0575: return "";
0576: }
0577:
0578: /**********************************************************************************************************************************************************************************************************************************************************
0579: * Preferences implementation
0580: *********************************************************************************************************************************************************************************************************************************************************/
0581:
0582: public class BasePreferences implements PreferencesEdit,
0583: SessionBindingListener {
0584: /** The user id. */
0585: protected String m_id = null;
0586:
0587: /** The properties. */
0588: protected ResourcePropertiesEdit m_properties = null;
0589:
0590: /** The sets of keyed ResourceProperties. */
0591: protected Map m_props = null;
0592:
0593: /**
0594: * Construct.
0595: *
0596: * @param id
0597: * The user id.
0598: */
0599: public BasePreferences(String id) {
0600: m_id = id;
0601:
0602: // setup for properties
0603: ResourcePropertiesEdit props = new BaseResourcePropertiesEdit();
0604: m_properties = props;
0605:
0606: m_props = new Hashtable();
0607:
0608: // if the id is not null (a new user, rather than a reconstruction)
0609: // and not the anon (id == "") user,
0610: // add the automatic (live) properties
0611: // %%% if ((m_id != null) && (m_id.length() > 0)) addLiveProperties(props);
0612: }
0613:
0614: /**
0615: * Construct from another Preferences object.
0616: *
0617: * @param user
0618: * The user object to use for values.
0619: */
0620: public BasePreferences(Preferences prefs) {
0621: setAll(prefs);
0622: }
0623:
0624: /**
0625: * Construct from information in XML.
0626: *
0627: * @param el
0628: * The XML DOM Element definining the user.
0629: */
0630: public BasePreferences(Element el) {
0631: // setup for properties
0632: m_properties = new BaseResourcePropertiesEdit();
0633:
0634: m_props = new Hashtable();
0635:
0636: m_id = el.getAttribute("id");
0637:
0638: // the children (properties)
0639: NodeList children = el.getChildNodes();
0640: final int length = children.getLength();
0641: for (int i = 0; i < length; i++) {
0642: Node child = children.item(i);
0643: if (child.getNodeType() != Node.ELEMENT_NODE)
0644: continue;
0645: Element element = (Element) child;
0646:
0647: // look for properties
0648: if (element.getTagName().equals("properties")) {
0649: // re-create properties
0650: m_properties = new BaseResourcePropertiesEdit(
0651: element);
0652: }
0653:
0654: // look for a set of preferences
0655: else if (element.getTagName().equals("prefs")) {
0656: String key = element.getAttribute("key");
0657:
0658: // convert old pre Sakai 2.2 keys to new values (copied here to avoid build dependencies - WATCH OUT! -ggolden)
0659: if (key.startsWith(NotificationService.PREFS_TYPE)) {
0660: if (key.endsWith("AnnouncementService")) {
0661: // matches AnnouncementService.APPLICATION_ID
0662: key = NotificationService.PREFS_TYPE
0663: + "sakai:announcement";
0664: } else if (key
0665: .endsWith("ContentHostingService")) {
0666: // matches ContentHostingService.APPLICATION_ID
0667: key = NotificationService.PREFS_TYPE
0668: + "sakai:content";
0669: } else if (key.endsWith("MailArchiveService")) {
0670: // matches MailArchiveService.APPLICATION_ID
0671: key = NotificationService.PREFS_TYPE
0672: + "sakai:mailarchive";
0673: } else if (key.endsWith("SyllabusService")) {
0674: // matches SyllabusService.APPLICATION_ID
0675: key = NotificationService.PREFS_TYPE
0676: + "sakai:syllabus";
0677: }
0678: } else if (key.endsWith("TimeService")) {
0679: // matches TimeService.APPLICATION_ID
0680: key = "sakai:time";
0681: } else if (key.endsWith("sitenav")) {
0682: // matches Charon portal's value
0683: key = "sakai:portal:sitenav";
0684: } else if (key.endsWith("ResourceLoader")) {
0685: // matches ResourceLoader.APPLICATION_ID
0686: key = "sakai:resourceloader";
0687: }
0688:
0689: BaseResourcePropertiesEdit props = null;
0690:
0691: // the children (properties)
0692: NodeList kids = element.getChildNodes();
0693: final int len = kids.getLength();
0694: for (int i2 = 0; i2 < len; i2++) {
0695: Node kid = kids.item(i2);
0696: if (kid.getNodeType() != Node.ELEMENT_NODE)
0697: continue;
0698: Element k = (Element) kid;
0699:
0700: // look for properties
0701: if (k.getTagName().equals("properties")) {
0702: props = new BaseResourcePropertiesEdit(k);
0703: }
0704: }
0705:
0706: if (props != null) {
0707: m_props.put(key, props);
0708: }
0709: }
0710: }
0711: }
0712:
0713: /**
0714: * Take all values from this object.
0715: *
0716: * @param user
0717: * The user object to take values from.
0718: */
0719: protected void setAll(Preferences prefs) {
0720: m_id = prefs.getId();
0721:
0722: m_properties = new BaseResourcePropertiesEdit();
0723: m_properties.addAll(prefs.getProperties());
0724:
0725: // %%% is this deep enough? -ggolden
0726: m_props = new Hashtable();
0727: m_props.putAll(((BasePreferences) prefs).m_props);
0728: }
0729:
0730: /**
0731: * @inheritDoc
0732: */
0733: public Element toXml(Document doc, Stack stack) {
0734: Element prefs = doc.createElement("preferences");
0735:
0736: if (stack.isEmpty()) {
0737: doc.appendChild(prefs);
0738: } else {
0739: ((Element) stack.peek()).appendChild(prefs);
0740: }
0741:
0742: stack.push(prefs);
0743:
0744: prefs.setAttribute("id", getId());
0745:
0746: // properties
0747: m_properties.toXml(doc, stack);
0748:
0749: // for each keyed property
0750: for (Iterator it = m_props.entrySet().iterator(); it
0751: .hasNext();) {
0752: Map.Entry entry = (Map.Entry) it.next();
0753: String key = (String) entry.getKey();
0754: ResourceProperties properties = (ResourceProperties) entry
0755: .getValue();
0756:
0757: // if the props are empty, skip it
0758: if (properties.getPropertyNames().next() == null)
0759: continue;
0760:
0761: Element props = doc.createElement("prefs");
0762: prefs.appendChild(props);
0763: props.setAttribute("key", key);
0764: stack.push(props);
0765: properties.toXml(doc, stack);
0766: stack.pop();
0767: }
0768: stack.pop();
0769:
0770: return prefs;
0771: }
0772:
0773: /**
0774: * @inheritDoc
0775: */
0776: public String getId() {
0777: if (m_id == null)
0778: return "";
0779: return m_id;
0780: }
0781:
0782: /**
0783: * @inheritDoc
0784: */
0785: public String getUrl() {
0786: return getAccessPoint(false) + m_id;
0787: }
0788:
0789: /**
0790: * @inheritDoc
0791: */
0792: public String getReference() {
0793: return preferencesReference(m_id);
0794: }
0795:
0796: /**
0797: * @inheritDoc
0798: */
0799: public String getReference(String rootProperty) {
0800: return getReference();
0801: }
0802:
0803: /**
0804: * @inheritDoc
0805: */
0806: public String getUrl(String rootProperty) {
0807: return getUrl();
0808: }
0809:
0810: /**
0811: * @inheritDoc
0812: */
0813: public ResourceProperties getProperties() {
0814: return m_properties;
0815: }
0816:
0817: /**
0818: * @inheritDoc
0819: */
0820: public ResourceProperties getProperties(String key) {
0821: ResourceProperties rv = (ResourceProperties) m_props
0822: .get(key);
0823: if (rv == null) {
0824: // new, throwaway empty one
0825: rv = new BaseResourceProperties();
0826: }
0827:
0828: return rv;
0829: }
0830:
0831: /**
0832: * @inheritDoc
0833: */
0834: public Collection getKeys() {
0835: return m_props.keySet();
0836: }
0837:
0838: /**
0839: * @inheritDoc
0840: */
0841: public boolean equals(Object obj) {
0842: if (!(obj instanceof Preferences))
0843: return false;
0844: return ((Preferences) obj).getId().equals(getId());
0845: }
0846:
0847: /**
0848: * @inheritDoc
0849: */
0850: public int hashCode() {
0851: return getId().hashCode();
0852: }
0853:
0854: /**
0855: * @inheritDoc
0856: */
0857: public int compareTo(Object obj) {
0858: if (!(obj instanceof Preferences))
0859: throw new ClassCastException();
0860:
0861: // if the object are the same, say so
0862: if (obj == this )
0863: return 0;
0864:
0865: // sort based on (unique) id
0866: int compare = getId()
0867: .compareTo(((Preferences) obj).getId());
0868:
0869: return compare;
0870: }
0871:
0872: /******************************************************************************************************************************************************************************************************************************************************
0873: * Edit implementation
0874: *****************************************************************************************************************************************************************************************************************************************************/
0875:
0876: /** The event code for this edit. */
0877: protected String m_event = null;
0878:
0879: /** Active flag. */
0880: protected boolean m_active = false;
0881:
0882: /**
0883: * @inheritDoc
0884: */
0885: public ResourcePropertiesEdit getPropertiesEdit(String key) {
0886: synchronized (m_props) {
0887: ResourcePropertiesEdit rv = (ResourcePropertiesEdit) m_props
0888: .get(key);
0889: if (rv == null) {
0890: // new one saved in the map
0891: rv = new BaseResourcePropertiesEdit();
0892: m_props.put(key, rv);
0893: }
0894:
0895: return rv;
0896: }
0897: }
0898:
0899: /**
0900: * Clean up.
0901: */
0902: protected void finalize() {
0903: // catch the case where an edit was made but never resolved
0904: if (m_active) {
0905: cancel(this );
0906: }
0907: }
0908:
0909: /**
0910: * Take all values from this object.
0911: *
0912: * @param user
0913: * The user object to take values from.
0914: */
0915: protected void set(Preferences prefs) {
0916: setAll(prefs);
0917: }
0918:
0919: /**
0920: * Access the event code for this edit.
0921: *
0922: * @return The event code for this edit.
0923: */
0924: protected String getEvent() {
0925: return m_event;
0926: }
0927:
0928: /**
0929: * Set the event code for this edit.
0930: *
0931: * @param event
0932: * The event code for this edit.
0933: */
0934: protected void setEvent(String event) {
0935: m_event = event;
0936: }
0937:
0938: /**
0939: * @inheritDoc
0940: */
0941: public ResourcePropertiesEdit getPropertiesEdit() {
0942: return m_properties;
0943: }
0944:
0945: /**
0946: * Enable editing.
0947: */
0948: protected void activate() {
0949: m_active = true;
0950: }
0951:
0952: /**
0953: * @inheritDoc
0954: */
0955: public boolean isActiveEdit() {
0956: return m_active;
0957: }
0958:
0959: /**
0960: * Close the edit object - it cannot be used after this.
0961: */
0962: protected void closeEdit() {
0963: m_active = false;
0964: }
0965:
0966: /******************************************************************************************************************************************************************************************************************************************************
0967: * SessionBindingListener implementation
0968: *****************************************************************************************************************************************************************************************************************************************************/
0969:
0970: /**
0971: * @inheritDoc
0972: */
0973: public void valueBound(SessionBindingEvent event) {
0974: }
0975:
0976: /**
0977: * @inheritDoc
0978: */
0979: public void valueUnbound(SessionBindingEvent event) {
0980: if (M_log.isDebugEnabled())
0981: M_log.debug("valueUnbound()");
0982:
0983: // catch the case where an edit was made but never resolved
0984: if (m_active) {
0985: cancel(this );
0986: }
0987: }
0988: }
0989:
0990: /**********************************************************************************************************************************************************************************************************************************************************
0991: * Storage
0992: *********************************************************************************************************************************************************************************************************************************************************/
0993:
0994: protected interface Storage {
0995: /**
0996: * Open.
0997: */
0998: public void open();
0999:
1000: /**
1001: * Close.
1002: */
1003: public void close();
1004:
1005: /**
1006: * Check if a preferences by this id exists.
1007: *
1008: * @param id
1009: * The user id.
1010: * @return true if a preferences for this id exists, false if not.
1011: */
1012: public boolean check(String id);
1013:
1014: /**
1015: * Get the preferences with this id, or null if not found.
1016: *
1017: * @param id
1018: * The preferences id.
1019: * @return The preferences with this id, or null if not found.
1020: */
1021: public Preferences get(String id);
1022:
1023: /**
1024: * Add a new preferences with this id.
1025: *
1026: * @param id
1027: * The preferences id.
1028: * @return The locked Preferences object with this id, or null if the id is in use.
1029: */
1030: public PreferencesEdit put(String id);
1031:
1032: /**
1033: * Get a lock on the preferences with this id, or null if a lock cannot be gotten.
1034: *
1035: * @param id
1036: * The preferences id.
1037: * @return The locked Preferences with this id, or null if this records cannot be locked.
1038: */
1039: public PreferencesEdit edit(String id);
1040:
1041: /**
1042: * Commit the changes and release the lock.
1043: *
1044: * @param user
1045: * The edit to commit.
1046: */
1047: public void commit(PreferencesEdit edit);
1048:
1049: /**
1050: * Cancel the changes and release the lock.
1051: *
1052: * @param user
1053: * The edit to commit.
1054: */
1055: public void cancel(PreferencesEdit edit);
1056:
1057: /**
1058: * Remove this edit and release the lock.
1059: *
1060: * @param user
1061: * The edit to remove.
1062: */
1063: public void remove(PreferencesEdit edit);
1064: }
1065:
1066: /**********************************************************************************************************************************************************************************************************************************************************
1067: * StorageUser implementation (no container)
1068: *********************************************************************************************************************************************************************************************************************************************************/
1069:
1070: /**
1071: * @inheritDoc
1072: */
1073: public Entity newContainer(String ref) {
1074: return null;
1075: }
1076:
1077: /**
1078: * @inheritDoc
1079: */
1080: public Entity newContainer(Element element) {
1081: return null;
1082: }
1083:
1084: /**
1085: * @inheritDoc
1086: */
1087: public Entity newContainer(Entity other) {
1088: return null;
1089: }
1090:
1091: /**
1092: * @inheritDoc
1093: */
1094: public Entity newResource(Entity container, String id,
1095: Object[] others) {
1096: return new BasePreferences(id);
1097: }
1098:
1099: /**
1100: * @inheritDoc
1101: */
1102: public Entity newResource(Entity container, Element element) {
1103: return new BasePreferences(element);
1104: }
1105:
1106: /**
1107: * @inheritDoc
1108: */
1109: public Entity newResource(Entity container, Entity other) {
1110: return new BasePreferences((Preferences) other);
1111: }
1112:
1113: /**
1114: * @inheritDoc
1115: */
1116: public Edit newContainerEdit(String ref) {
1117: return null;
1118: }
1119:
1120: /**
1121: * @inheritDoc
1122: */
1123: public Edit newContainerEdit(Element element) {
1124: return null;
1125: }
1126:
1127: /**
1128: * @inheritDoc
1129: */
1130: public Edit newContainerEdit(Entity other) {
1131: return null;
1132: }
1133:
1134: /**
1135: * @inheritDoc
1136: */
1137: public Edit newResourceEdit(Entity container, String id,
1138: Object[] others) {
1139: BasePreferences e = new BasePreferences(id);
1140: e.activate();
1141: return e;
1142: }
1143:
1144: /**
1145: * @inheritDoc
1146: */
1147: public Edit newResourceEdit(Entity container, Element element) {
1148: BasePreferences e = new BasePreferences(element);
1149: e.activate();
1150: return e;
1151: }
1152:
1153: /**
1154: * @inheritDoc
1155: */
1156: public Edit newResourceEdit(Entity container, Entity other) {
1157: BasePreferences e = new BasePreferences((Preferences) other);
1158: e.activate();
1159: return e;
1160: }
1161:
1162: /**
1163: * @inheritDoc
1164: */
1165: public Object[] storageFields(Entity r) {
1166: return null;
1167: }
1168:
1169: /**
1170: * @inheritDoc
1171: */
1172: public boolean isDraft(Entity r) {
1173: return false;
1174: }
1175:
1176: /**
1177: * @inheritDoc
1178: */
1179: public String getOwnerId(Entity r) {
1180: return null;
1181: }
1182:
1183: /**
1184: * @inheritDoc
1185: */
1186: public Time getDate(Entity r) {
1187: return null;
1188: }
1189: }
|