/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-impl/src/main/java/org/sakaiproject/alias/impl/BaseAliasService.java $ * $Id: BaseAliasService.java 105669 2012-03-12 11:56:47Z matthew.buckett@oucs.ox.ac.uk $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 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.alias.impl; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.Arrays; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.alias.api.Alias; import org.sakaiproject.alias.api.AliasEdit; import org.sakaiproject.alias.api.AliasService; import org.sakaiproject.authz.api.FunctionManager; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.entity.api.Edit; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.HttpAccess; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; import org.sakaiproject.event.api.EventTrackingService; import org.sakaiproject.exception.IdInvalidException; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.IdUsedException; import org.sakaiproject.exception.InUseException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.memory.api.Cache; import org.sakaiproject.memory.api.MemoryService; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.time.api.Time; import org.sakaiproject.time.api.TimeService; import org.sakaiproject.tool.api.SessionBindingEvent; import org.sakaiproject.tool.api.SessionBindingListener; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.util.BaseResourceProperties; import org.sakaiproject.util.BaseResourcePropertiesEdit; import org.sakaiproject.util.SingleStorageUser; import org.sakaiproject.util.StringUtil; import org.sakaiproject.util.Validator; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * <p> * BaseAliasService is ... * </p> */ public abstract class BaseAliasService implements AliasService, SingleStorageUser { /** Our logger. */ private static Log M_log = LogFactory.getLog(BaseAliasService.class); /** Storage manager for this service. */ protected Storage m_storage = null; /** The initial portion of a relative access point URL. */ protected String m_relativeAccessPoint = null; /** A cache of calls to the service and the results. */ protected Cache m_callCache = null; private List<String> prohibited_aliases = null; /********************************************************************************************************************************************************************************************************************************************************** * Abstractions, etc. *********************************************************************************************************************************************************************************************************************************************************/ /** * Construct storage for this service. */ protected abstract Storage newStorage(); /** * 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; } // getAccessPoint /** * Access the internal reference which can be used to access the resource from within the system. * * @param id * The alias id string. * @return The the internal reference which can be used to access the resource from within the system. */ public String aliasReference(String id) { return getAccessPoint(true) + Entity.SEPARATOR + id; } // aliasReference /** * Access the alias id extracted from a alias reference. * * @param ref * The alias reference string. * @return The the alias id extracted from a alias reference. */ protected String aliasId(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; } // aliasId /** * Check security permission. * * @param lock * The lock id string. * @param resource * The resource reference string, or null if no resource is involved. * @return true if allowed, false if not */ protected boolean unlockCheck(String lock, String resource) { if (!securityService().unlock(lock, resource)) { return false; } return true; } // unlockCheck /** * Check security permission. * * @param lock * The lock id string. * @param resource * The resource reference string, or null if no resource is involved. * @exception PermissionException * Thrown if the user does not have access */ protected void unlock(String lock, String resource) throws PermissionException { if (!unlockCheck(lock, resource)) { throw new PermissionException(sessionManager().getCurrentSessionUserId(), lock, resource); } } // unlock /** * Check security permission, target modify based. * * @param target * The target resource reference string. * @return true if allowed, false if not */ protected boolean unlockTargetCheck(String target) { // check the target for modify access. // TODO: this is setup only for sites and mail archive channels, we need an Entity Model based generic "allowModify()" -ggolden. Reference ref = entityManager().newReference(target); if (ref.getType().equals(SiteService.APPLICATION_ID)) { return siteService().allowUpdateSite(ref.getId()); } // TODO: fake this dependency (MailArchiveService.APPLICATION_ID) to keep the mailarchive dependencies away -ggolden else if (ref.getType().equals("sakai:mailarchive")) { // base this on site update, too M_log.debug("checing allow update on " + ref.getContext()); //due to a bug in the mailarchive entity manager the context may be the strign null if (ref.getContext() != null && !ref.getContext().equals("null")) { M_log.debug("Checking allow update on " + ref.getContext() + " with lenght: " + ref.getContext().length()); return siteService().allowUpdateSite(ref.getContext()); } else { boolean ret = siteService().allowAddSite(null); M_log.debug("Cheking site.add permission returning: " + ret); return ret; } } // TODO: fake this dependency (CalendarService.APPLICATION_ID) to keep the calendar dependencies away else if (ref.getType().equals("sakai:calendar")) { // base this on site update, too return siteService().allowUpdateSite(ref.getContext()); } // TODO: fake this dependency (AnnouncementService.APPLICATION_ID) to keep the announcement dependencies away else if (ref.getType().equals("sakai:announcement")) { // base this on site update, too return siteService().allowUpdateSite(ref.getContext()); } return false; } // unlockTargetCheck /** * Create the live properties for the user. */ protected void addLiveProperties(ResourcePropertiesEdit props) { String current = sessionManager().getCurrentSessionUserId(); props.addProperty(ResourceProperties.PROP_CREATOR, current); props.addProperty(ResourceProperties.PROP_MODIFIED_BY, current); String now = timeService().newTime().toString(); props.addProperty(ResourceProperties.PROP_CREATION_DATE, now); props.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now); } // addLiveProperties /** * Update the live properties for a user for when modified. */ protected void addLiveUpdateProperties(ResourcePropertiesEdit props) { String current = sessionManager().getCurrentSessionUserId(); props.addProperty(ResourceProperties.PROP_MODIFIED_BY, current); props.addProperty(ResourceProperties.PROP_MODIFIED_DATE, timeService().newTime().toString()); } // addLiveUpdateProperties /********************************************************************************************************************************************************************************************************************************************************** * Dependencies *********************************************************************************************************************************************************************************************************************************************************/ /** * @return the MemoryService collaborator. */ protected abstract MemoryService memoryService(); /** * @return the ServerConfigurationService collaborator. */ protected abstract ServerConfigurationService serverConfigurationService(); /** * @return the EntityManager collaborator. */ protected abstract EntityManager entityManager(); /** * @return the SecurityService collaborator. */ protected abstract SecurityService securityService(); /** * @return the SessionManager collaborator. */ protected abstract SessionManager sessionManager(); /** * @return the SiteService collaborator. */ protected abstract SiteService siteService(); /** * @return the TimeService collaborator. */ protected abstract TimeService timeService(); /** * @return the FunctionManager collaborator. */ protected abstract FunctionManager functionManager(); /** * @return the EventTrackingService collaborator. */ protected abstract EventTrackingService eventTrackingService(); /** * @return the UserDirectoryService collaborator. */ protected abstract UserDirectoryService userDirectoryService(); /********************************************************************************************************************************************************************************************************************************************************** * Configuration *********************************************************************************************************************************************************************************************************************************************************/ /** The # seconds to cache gets. 0 disables the cache. */ protected int m_cacheSeconds = 0; /** * Set the # minutes to cache a get. * * @param time * The # minutes to cache a get (as an integer string). */ public void setCacheMinutes(String time) { m_cacheSeconds = Integer.parseInt(time) * 60; } /** The # seconds to cache gets. 0 disables the cache. */ protected int m_cacheCleanerSeconds = 0; /** * Set the # minutes between cache cleanings. * * @param time * The # minutes between cache cleanings. (as an integer string). */ public void setCacheCleanerMinutes(String time) { m_cacheCleanerSeconds = Integer.parseInt(time) * 60; } /********************************************************************************************************************************************************************************************************************************************************** * Init and Destroy *********************************************************************************************************************************************************************************************************************************************************/ /** * Final initialization, once all dependencies are set. */ public void init() { try { m_relativeAccessPoint = REFERENCE_ROOT; // construct storage and read m_storage = newStorage(); m_storage.open(); // <= 0 indicates no caching desired if ((m_cacheSeconds > 0) && (m_cacheCleanerSeconds > 0)) { // build a synchronized map for the call cache, automatically checking for expiration every 15 mins, expire on user events, too. m_callCache = memoryService().newCache( "org.sakaiproject.alias.api.AliasService.callCache", aliasReference("")); } // register as an entity producer entityManager().registerEntityProducer(this, REFERENCE_ROOT); // register functions functionManager().registerFunction(SECURE_ADD_ALIAS); functionManager().registerFunction(SECURE_UPDATE_ALIAS); functionManager().registerFunction(SECURE_REMOVE_ALIAS); prohibited_aliases = Arrays.asList(serverConfigurationService().getString("mail.prohibitedaliases", "postmaster").trim().toLowerCase().split("\\s*,\\s*")); } catch (Exception t) { M_log.warn("init(): ", t); } } // init /** * Returns to uninitialized state. */ public void destroy() { m_storage.close(); m_storage = null; M_log.info("destroy()"); } /********************************************************************************************************************************************************************************************************************************************************** * AliasService implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * Check if the current user has permission to set this alias. * * @param alias * The alias. * @param target * The resource reference string alias target. * @return true if the current user has permission to set this alias, false if not. */ public boolean allowSetAlias(String alias, String target) { if (!securityService().isSuperUser() && prohibited_aliases.contains(alias.toLowerCase())) return false; return unlockTargetCheck(target); } // allowSetAlias /** * Allocate an alias for a resource * * @param alias * The alias. * @param target * The resource reference string alias target. * @throws IdUsedException * if the alias is already used. * @throws IdInvalidException * if the alias id is invalid. * @throws PermissionException * if the current user does not have permission to set this alias. */ public void setAlias(String alias, String target) throws IdUsedException, IdInvalidException, PermissionException { if (alias!=null && alias.length()>99){ //KNL-454 M_log.warn("The length of the alias: \""+alias+"\" cannot be greater than 99 characters"); throw new IdInvalidException(alias); } // check for a valid alias name Validator.checkResourceId(alias); if ((!securityService().isSuperUser() && prohibited_aliases.contains(alias.toLowerCase())) || !unlockTargetCheck(target)) { throw new PermissionException(sessionManager().getCurrentSessionUserId(), SECURE_ADD_ALIAS, target); } // attempt to register this alias with storage - if it's in use, this will return null AliasEdit a = m_storage.put(alias); if (a == null) { throw new IdUsedException(alias); } a.setTarget(target); // update the properties addLiveProperties(a.getPropertiesEdit()); // complete the edit m_storage.commit(a); // track it eventTrackingService().post(eventTrackingService().newEvent(SECURE_ADD_ALIAS, aliasReference(alias), true)); } // setAlias /** * Check to see if the current user can remove this alias. * * @param alias * The alias. * @return true if the current user can remove this alias, false if not. */ public boolean allowRemoveAlias(String alias) { return unlockCheck(SECURE_REMOVE_ALIAS, aliasReference(alias)); } // allowRemoveAlias /** * Remove an alias. * * @param alias * The alias. * @exception IdUnusedException * if not found. * @exception PermissionException * if the current user does not have permission to remove this alias. * @exception InUseException * if the Alias object is locked by someone else. */ public void removeAlias(String alias) throws IdUnusedException, PermissionException, InUseException { AliasEdit a = edit(alias); remove(a); } // removeAlias /** * Check to see if the current user can remove these aliasese for this target resource reference. * * @param target * The target resource reference string. * @return true if the current user can remove these aliasese for this target resource reference, false if not. */ public boolean allowRemoveTargetAliases(String target) { return unlockTargetCheck(target); } // allowRemoveTargetAliases /** * Remove all aliases for this target resource reference, if any. * * @param target * The target resource reference string. * @throws PermissionException * if the current user does not have permission to remove these aliases. */ public void removeTargetAliases(String target) throws PermissionException { if (!unlockTargetCheck(target)) { throw new PermissionException(sessionManager().getCurrentSessionUserId(), SECURE_REMOVE_ALIAS, target); } List<Alias> all = getAliases(target); for (Iterator<Alias> iAll = all.iterator(); iAll.hasNext();) { Alias alias = (Alias) iAll.next(); try { AliasEdit a = m_storage.edit(alias.getId()); if (a != null) { // complete the edit m_storage.remove(a); // track it eventTrackingService().post(eventTrackingService().newEvent(SECURE_REMOVE_ALIAS, a.getReference(), true)); } } catch (Exception ignore) { } } } // removeTargetAliases /** * Find the target resource reference string associated with this alias. * * @param alias * The alias. * @return The target resource reference string associated with this alias. * @throws IdUnusedException * if the alias is not defined. */ public String getTarget(String alias) throws IdUnusedException { // check the cache String ref = aliasReference(alias); if ((m_callCache != null) && (m_callCache.containsKey(ref))) { String t = (String) m_callCache.get(ref); if ( t != null ) { return t; } M_log.warn("Null Cache Entry found, should not happen, please nottify Ian Boston, thanks, SAK-12447, line 547 BaseAliasService ref="+ref); } BaseAliasEdit a = (BaseAliasEdit) m_storage.get(alias); if (a == null) throw new IdUnusedException(alias); // cache if (m_callCache != null) { if ( a.getTarget() != null ) { m_callCache.put(ref, a.getTarget(), m_cacheSeconds); } } return a.getTarget(); } // getTarget /** * Find all the aliases defined for this target. * * @param target * The target resource reference string. * @return A list (Alias) of all the aliases defined for this target. */ public List<Alias> getAliases(String target) { List<Alias> allForTarget = m_storage.getAll(target); return allForTarget; } // getAliases /** * Find all the aliases defined for this target, within the record range given (sorted by id). * * @param target * The target resource reference string. * @param first * The first record position to return. * @param last * The last record position to return. * @return A list (Alias) of all the aliases defined for this target, within the record range given (sorted by id). */ public List<Alias> getAliases(String target, int first, int last) { List<Alias> allForTarget = m_storage.getAll(target, first, last); return allForTarget; } // getAliases /** * Find all the aliases within the record range given (sorted by id). * * @param first * The first record position to return. * @param last * The last record position to return. * @return A list (Alias) of all the aliases within the record range given (sorted by id). */ public List<Alias> getAliases(int first, int last) { List<Alias> all = m_storage.getAll(first, last); return all; } // getAliases /** * {@inheritDoc} */ public int countAliases() { return m_storage.count(); } /** * {@inheritDoc} */ public List<Alias> searchAliases(String criteria, int first, int last) { return m_storage.search(criteria, first, last); } /** * {@inheritDoc} */ public int countSearchAliases(String criteria) { return m_storage.countSearch(criteria); } /** * Check to see if the current user can add an alias. * * @return true if the current user can add an alias, false if not. */ public boolean allowAdd() { return unlockCheck(SECURE_ADD_ALIAS, aliasReference("")); } // allowAdd /** * Add a new alias. Must commit() to make official, or cancel() when done! * * @param id * The alias id. * @return A locked AliasEdit object (reserving the id). * @exception IdInvalidException * if the alias id is invalid. * @exception IdUsedException * if the alias id is already used. * @exception PermissionException * if the current user does not have permission to add an alias. */ public AliasEdit add(String id) throws IdInvalidException, IdUsedException, PermissionException { if (id!=null && id.length()>99){ //KNL-454 M_log.warn("The length of the alias: \""+id+"\" cannot be greater than 99 characters"); throw new IdInvalidException(id); } // check for a valid user name Validator.checkResourceId(id); // check security (throws if not permitted) unlock(SECURE_ADD_ALIAS, aliasReference(id)); // reserve an alias with this id from the info store - if it's in use, this will return null AliasEdit a = m_storage.put(id); if (a == null) { throw new IdUsedException(id); } ((BaseAliasEdit) a).setEvent(SECURE_ADD_ALIAS); return a; } // add /** * Check to see if the current user can edit this alias. * * @param id * The alias id string. * @return true if the current user can edit this alias, false if not. */ public boolean allowEdit(String id) { return unlockCheck(SECURE_UPDATE_ALIAS, aliasReference(id)); } // allowEdit /** * Get a locked alias object for editing. Must commit() to make official, or cancel() (or remove()) when done! * * @param id * The alias id string. * @return An AliasEdit object for editing. * @exception IdUnusedException * if not found. * @exception PermissionException * if the current user does not have permission to mess with this alias. * @exception InUseException * if the Alias object is locked by someone else. */ public AliasEdit edit(String id) throws IdUnusedException, PermissionException, InUseException { if (id == null) throw new IdUnusedException("null"); // check security (throws if not permitted) unlock(SECURE_UPDATE_ALIAS, aliasReference(id)); // check for existance if (!m_storage.check(id)) { throw new IdUnusedException(id); } // ignore the cache - get the user with a lock from the info store AliasEdit a = m_storage.edit(id); if (a == null) throw new InUseException(id); ((BaseAliasEdit) a).setEvent(SECURE_UPDATE_ALIAS); return a; } // edit /** * Commit the changes made to a AliasEdit object, and release the lock. The AliasEdit is disabled, and not to be used after this call. * * @param user * The AliasEdit object to commit. */ public void commit(AliasEdit edit) { // check for closed edit if (!edit.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn("commit(): closed AliasEdit", e); } return; } // If we're doing an update just change the modification if (SECURE_UPDATE_ALIAS.equals(((BaseAliasEdit)edit).getEvent())) { addLiveUpdateProperties(edit.getPropertiesEdit()); } else { addLiveProperties(edit.getPropertiesEdit()); } // complete the edit m_storage.commit(edit); // track it eventTrackingService().post(eventTrackingService().newEvent(((BaseAliasEdit) edit).getEvent(), edit.getReference(), true)); // close the edit object ((BaseAliasEdit) edit).closeEdit(); } // commit /** * Cancel the changes made to a AliasEdit object, and release the lock. The AliasEdit is disabled, and not to be used after this call. * * @param user * The AliasEdit object to commit. */ public void cancel(AliasEdit edit) { // check for closed edit if (!edit.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn("cancel(): closed AliasEdit", e); } return; } // release the edit lock m_storage.cancel(edit); // close the edit object ((BaseAliasEdit) edit).closeEdit(); } // cancel /** * Remove this alias information - it must be a user with a lock from edit(). The AliasEdit is disabled, and not to be used after this call. * * @param edit * The locked AliasEdit object to remove. * @exception PermissionException * if the current user does not have permission to remove this alias. */ public void remove(AliasEdit edit) throws PermissionException { // check for closed edit if (!edit.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn("remove(): closed AliasEdit", e); } return; } // check security (throws if not permitted) unlock(SECURE_REMOVE_ALIAS, edit.getReference()); // complete the edit m_storage.remove(edit); // track it eventTrackingService().post(eventTrackingService().newEvent(SECURE_REMOVE_ALIAS, edit.getReference(), true)); // close the edit object ((BaseAliasEdit) edit).closeEdit(); } // remove /********************************************************************************************************************************************************************************************************************************************************** * EntityProducer implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * {@inheritDoc} */ public String getLabel() { return "alias"; } /** * {@inheritDoc} */ public boolean willArchiveMerge() { return false; } /** * {@inheritDoc} */ public HttpAccess getHttpAccess() { return null; } /** * {@inheritDoc} */ public boolean parseEntityReference(String reference, Reference ref) { // for preferences access if (reference.startsWith(REFERENCE_ROOT)) { String id = null; // we will get null, service, userId String[] parts = StringUtil.split(reference, Entity.SEPARATOR); if (parts.length > 2) { id = parts[2]; } ref.set(APPLICATION_ID, null, id, null, null); return true; } return false; } /** * {@inheritDoc} */ public String getEntityDescription(Reference ref) { return null; } /** * {@inheritDoc} */ public ResourceProperties getEntityResourceProperties(Reference ref) { return null; } /** * {@inheritDoc} */ public Entity getEntity(Reference ref) { return null; } /** * {@inheritDoc} */ public Collection getEntityAuthzGroups(Reference ref, String userId) { // for alias access %%% ? what realm? -ggolden return null; } /** * {@inheritDoc} */ public String getEntityUrl(Reference ref) { return null; } /** * {@inheritDoc} */ public String archive(String siteId, Document doc, Stack stack, String archivePath, List attachments) { return ""; } /** * {@inheritDoc} */ public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames, Map userIdTrans, Set userListAllowImport) { return ""; } /********************************************************************************************************************************************************************************************************************************************************** * Alias implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * <p> * BaseAlias is an implementation of the CHEF Alias object. * </p> * */ public class BaseAliasEdit implements AliasEdit, SessionBindingListener { /** The alias id. */ protected String m_id = null; /** The alias target. */ protected String m_target = null; /** The properties. */ protected ResourcePropertiesEdit m_properties = null; /** The created user id. */ protected String m_createdUserId = null; /** The last modified user id. */ protected String m_lastModifiedUserId = null; /** The time created. */ protected Time m_createdTime = null; /** The time last modified. */ protected Time m_lastModifiedTime = null; /** The event code for this edit. */ protected String m_event = null; /** Active flag. */ protected boolean m_active = false; /** * Clean up. */ protected void finalize() { // catch the case where an edit was made but never resolved if (m_active) { cancel(this); } } // finalize /** * Construct. * * @param id * The id. */ public BaseAliasEdit(String id) { m_id = id; // setup for properties ResourcePropertiesEdit props = new BaseResourcePropertiesEdit(); m_properties = props; // if not a reconstruction, add properties if ((m_id != null) && (m_id.length() > 0)) addLiveProperties(props); } // BaseAlias /** * ReConstruct. * * @param id * The id. * @param target * The target. * @param createdBy * The createdBy property. * @param createdOn * The createdOn property. * @param modifiedBy * The modified by property. * @param modifiedOn * The modified on property. */ public BaseAliasEdit(String id, String target, String createdBy, Time createdOn, String modifiedBy, Time modifiedOn) { m_id = id; m_target = target; m_createdUserId = createdBy; m_lastModifiedUserId = modifiedBy; m_createdTime = createdOn; m_lastModifiedTime = modifiedOn; // setup for properties, but mark them lazy since we have not yet established them from data BaseResourcePropertiesEdit props = new BaseResourcePropertiesEdit(); props.setLazy(true); m_properties = props; } // BaseAlias /** * Construct from another Alias object. * * @param alias * The alias object to use for values. */ public BaseAliasEdit(BaseAliasEdit alias) { setAll(alias); } // BaseAlias /** * Construct from information in XML. * * @param el * The XML DOM Element definining the alias. */ public BaseAliasEdit(Element el) { // setup for properties m_properties = new BaseResourcePropertiesEdit(); m_id = el.getAttribute("id"); m_target = el.getAttribute("target"); m_createdUserId = StringUtils.trimToNull(el.getAttribute("created-id")); m_lastModifiedUserId = StringUtils.trimToNull(el.getAttribute("modified-id")); String time = StringUtils.trimToNull(el.getAttribute("created-time")); if (time != null) { m_createdTime = timeService().newTimeGmt(time); } time = StringUtils.trimToNull(el.getAttribute("modified-time")); if (time != null) { m_lastModifiedTime = timeService().newTimeGmt(time); } // the children (properties) 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); // pull out some properties into fields to convert old (pre 1.18) versions if (m_createdUserId == null) { m_createdUserId = m_properties.getProperty("CHEF:creator"); } if (m_lastModifiedUserId == null) { m_lastModifiedUserId = m_properties.getProperty("CHEF:modifiedby"); } if (m_createdTime == null) { try { m_createdTime = m_properties.getTimeProperty("DAV:creationdate"); } catch (Exception ignore) { } } if (m_lastModifiedTime == null) { try { m_lastModifiedTime = m_properties.getTimeProperty("DAV:getlastmodified"); } catch (Exception ignore) { } } m_properties.removeProperty("CHEF:creator"); m_properties.removeProperty("CHEF:modifiedby"); m_properties.removeProperty("DAV:creationdate"); m_properties.removeProperty("DAV:getlastmodified"); } } } // BaseAlias /** * Take all values from this object. * * @param alias * The alias object to take values from. */ protected void setAll(BaseAliasEdit alias) { m_id = alias.m_id; m_target = alias.m_target; m_createdUserId = ((BaseAliasEdit) alias).m_createdUserId; m_lastModifiedUserId = ((BaseAliasEdit) alias).m_lastModifiedUserId; if (((BaseAliasEdit) alias).m_createdTime != null) m_createdTime = (Time) ((BaseAliasEdit) alias).m_createdTime.clone(); if (((BaseAliasEdit) alias).m_lastModifiedTime != null) m_lastModifiedTime = (Time) ((BaseAliasEdit) alias).m_lastModifiedTime.clone(); m_properties = new BaseResourcePropertiesEdit(); m_properties.addAll(alias.getProperties()); ((BaseResourcePropertiesEdit) m_properties).setLazy(((BaseResourceProperties) alias.getProperties()).isLazy()); } // setAll /** * 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 alias = doc.createElement("alias"); if (stack.isEmpty()) { doc.appendChild(alias); } else { ((Element) stack.peek()).appendChild(alias); } stack.push(alias); alias.setAttribute("id", m_id); alias.setAttribute("target", m_target); alias.setAttribute("created-id", m_createdUserId); alias.setAttribute("modified-id", m_lastModifiedUserId); alias.setAttribute("created-time", m_createdTime.toString()); alias.setAttribute("modified-time", m_lastModifiedTime.toString()); // properties getProperties().toXml(doc, stack); stack.pop(); return alias; } // toXml /** * Access the alias id. * * @return The alias id string. */ public String getId() { return m_id; } // getId /** * {@inheritDoc} */ public User getCreatedBy() { try { return userDirectoryService().getUser(m_createdUserId); } catch (Exception e) { return userDirectoryService().getAnonymousUser(); } } /** * {@inheritDoc} */ public User getModifiedBy() { try { return userDirectoryService().getUser(m_lastModifiedUserId); } catch (Exception e) { return userDirectoryService().getAnonymousUser(); } } /** * {@inheritDoc} */ public Time getCreatedTime() { return m_createdTime; } /** * {@inheritDoc} */ public Time getModifiedTime() { return m_lastModifiedTime; } public Date getDateCreated() { return new Date(m_createdTime.getTime()); } public Date getDateModified() { return new Date(m_lastModifiedTime.getTime()); } /** * Access the alias target. * * @return The alias target. */ public String getTarget() { return m_target; } // getTarget /** * Set the alias target. * * @param target * The alias target. */ public void setTarget(String target) { m_target = target; } // setTarget /** * Access the URL which can be used to access the resource. * * @return The URL which can be used to access the resource. */ public String getUrl() { return getAccessPoint(false) + m_id; } // getUrl /** * Access the internal reference which can be used to access the resource from within the system. * * @return The the internal reference which can be used to access the resource from within the system. */ public String getReference() { return aliasReference(m_id); } // getReference /** * @inheritDoc */ public String getReference(String rootProperty) { return getReference(); } /** * @inheritDoc */ public String getUrl(String rootProperty) { return getUrl(); } /** * Access the resources's properties. * * @return The resources's properties. */ public ResourceProperties getProperties() { // if lazy, resolve if (((BaseResourceProperties) m_properties).isLazy()) { ((BaseResourcePropertiesEdit) m_properties).setLazy(false); m_storage.readProperties(this, m_properties); } return m_properties; } // getProperties /** * Are these objects equal? If they are both Alias objects, and they have matching id's, they are. * * @return true if they are equal, false if not. */ public boolean equals(Object obj) { if (!(obj instanceof BaseAliasEdit)) return false; return ((BaseAliasEdit) obj).getId().equals(getId()); } // equals /** * Make a hash code that reflects the equals() logic as well. We want two objects, even if different instances, if they have the same id to hash the same. */ public int hashCode() { return getId().hashCode(); } // hashCode /** * Compare this object with the specified object for order. * * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. */ public int compareTo(Object obj) { if (!(obj instanceof BaseAliasEdit)) throw new ClassCastException(); // if the object are the same, say so if (obj == this) return 0; // sort based on (unique) id int compare = getId().compareTo(((BaseAliasEdit) obj).getId()); return compare; } // compareTo /** * 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() { // if lazy, resolve if (((BaseResourceProperties) m_properties).isLazy()) { ((BaseResourcePropertiesEdit) m_properties).setLazy(false); m_storage.readProperties(this, m_properties); } 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 /** * @return a description of the item this alias's target applies to. */ public String getDescription() { try { // the rest are references to some resource Reference ref = entityManager().newReference(getTarget()); return ref.getDescription(); } catch (Exception any) { return "unknown"; } } // getDescription /****************************************************************************************************************************************************************************************************************************************************** * SessionBindingListener implementation *****************************************************************************************************************************************************************************************************************************************************/ public void valueBound(SessionBindingEvent event) { } public void valueUnbound(SessionBindingEvent event) { if (M_log.isDebugEnabled()) M_log.debug("valueUnbound()"); // catch the case where an edit was made but never resolved if (m_active) { cancel(this); } } // valueUnbound } // BaseAliasEdit /********************************************************************************************************************************************************************************************************************************************************** * Storage *********************************************************************************************************************************************************************************************************************************************************/ protected interface Storage { /** * Open. */ public void open(); /** * Close. */ public void close(); /** * Check if an alias with this id exists. * * @param id * The alias id (case insensitive). * @return true if an alias by this id exists, false if not. */ public boolean check(String id); /** * Get the alias with this id, or null if not found. * * @param id * The alias id (case insensitive). * @return The alias with this id, or null if not found. */ public AliasEdit get(String id); /** * Get all the alias. * * @return The List (BaseAliasEdit) of all alias. */ public List getAll(); /** * Get all the alias in record range. * * @param first * The first record position to return. * @param last * The last record position to return. * @return The List (BaseAliasEdit) of all alias. */ public List getAll(int first, int last); /** * Count all the aliases. * * @return The count of all aliases. */ public int count(); /** * Search for aliases with id or target matching criteria, in range. * * @param criteria * The search criteria. * @param first * The first record position to return. * @param last * The last record position to return. * @return The List (BaseAliasEdit) of all alias. */ public List search(String criteria, int first, int last); /** * Count all the aliases with id or target matching criteria. * * @param criteria * The search criteria. * @return The count of all aliases with id or target matching criteria. */ public int countSearch(String criteria); /** * Get all the alias that point at this target. * * @return The List (BaseAliasEdit) of all alias that point at this target */ public List getAll(String target); /** * Get all the alias that point at this target, in record range. * * @param first * The first record position to return. * @param last * The last record position to return. * @return The List (BaseAliasEdit) of all alias that point at this target, in record range. */ public List getAll(String target, int first, int last); /** * Add a new alias with this id. * * @param id * The alias id. * @return The locked Alias object with this id, or null if the id is in use. */ public AliasEdit put(String id); /** * Get a lock on the alias with this id, or null if a lock cannot be gotten. * * @param id * The alias id (case insensitive). * @return The locked Alias with this id, or null if this records cannot be locked. */ public AliasEdit edit(String id); /** * Commit the changes and release the lock. * * @param user * The alias to commit. */ public void commit(AliasEdit alias); /** * Cancel the changes and release the lock. * * @param user * The alias to commit. */ public void cancel(AliasEdit alias); /** * Remove this alias. * * @param user * The alias to remove. */ public void remove(AliasEdit alias); /** * Read properties from storage into the edit's properties. * * @param edit * The user to read properties for. */ public void readProperties(AliasEdit edit, ResourcePropertiesEdit props); } // Storage /********************************************************************************************************************************************************************************************************************************************************** * StorageUser implementation (no container) *********************************************************************************************************************************************************************************************************************************************************/ /** * Construct a new resource 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 BaseAliasEdit(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 BaseAliasEdit(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 BaseAliasEdit((BaseAliasEdit) other); } /** * Construct a new resource 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) { BaseAliasEdit e = new BaseAliasEdit(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) { BaseAliasEdit e = new BaseAliasEdit(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) { BaseAliasEdit e = new BaseAliasEdit((BaseAliasEdit) 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; } } // BaseAliasService