package com.idega.slide.business; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.Serializable; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.codec.digest.DigestUtils; import org.apache.slide.authenticate.CredentialsToken; import org.apache.slide.authenticate.SecurityToken; import org.apache.slide.common.Domain; import org.apache.slide.common.NamespaceAccessToken; import org.apache.slide.common.ServiceAccessException; import org.apache.slide.common.SlideToken; import org.apache.slide.common.SlideTokenImpl; import org.apache.slide.content.Content; import org.apache.slide.content.NodeProperty; import org.apache.slide.content.NodeRevisionContent; import org.apache.slide.content.NodeRevisionDescriptor; import org.apache.slide.content.NodeRevisionDescriptors; import org.apache.slide.content.NodeRevisionNumber; import org.apache.slide.content.RevisionDescriptorNotFoundException; import org.apache.slide.event.AbstractEventMethod; import org.apache.slide.event.ContentEvent; import org.apache.slide.event.VetoException; import org.apache.slide.lock.ObjectLockedException; import org.apache.slide.security.AccessDeniedException; import org.apache.slide.security.NodePermission; import org.apache.slide.security.Security; import org.apache.slide.structure.LinkedObjectNotFoundException; import org.apache.slide.structure.ObjectAlreadyExistsException; import org.apache.slide.structure.ObjectNode; import org.apache.slide.structure.ObjectNotFoundException; import org.apache.slide.structure.Structure; import org.apache.slide.structure.SubjectNode; import org.apache.webdav.lib.Ace; import org.apache.webdav.lib.Privilege; import org.apache.webdav.lib.WebdavResources; import org.jdom.Attribute; import org.jdom.Document; import org.jdom.Element; import org.jdom.Namespace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import com.idega.core.business.DefaultSpringBean; import com.idega.slide.authentication.AuthenticationBusiness; import com.idega.slide.bean.SlideAction; import com.idega.slide.util.IWSlideConstants; import com.idega.slide.webdavservlet.DomainConfig; import com.idega.user.data.User; import com.idega.util.ArrayUtil; import com.idega.util.CoreConstants; import com.idega.util.FileUtil; import com.idega.util.IOUtil; import com.idega.util.IWTimestamp; import com.idega.util.ListUtil; import com.idega.util.StringHandler; import com.idega.util.StringUtil; import com.idega.util.xml.XmlUtil; /** * Simple API of Slide implementation. It improves performance without breaking business logic. * * @author <a href="mailto:valdas@idega.com">Valdas Žemaitis</a> * @version $Revision: 1.2 $ * * Last modified: $Date: 2009/05/08 08:10:02 $ by: $Author: valdas $ */ @Service @Scope(BeanDefinition.SCOPE_SINGLETON) public class IWSimpleSlideServiceImp extends DefaultSpringBean implements IWSimpleSlideService, IWSlideChangeListener { private static final long serialVersionUID = 8065146986117553218L; private static final Logger LOGGER = Logger.getLogger(IWSimpleSlideServiceImp.class.getName()); private static final int CACHE_SIZE = 100000; static final String CACHE_RESOURCE_EXISTANCE_NAME = "slide_resource_existance_cache", CACHE_RESOURCE_DESCRIPTOR_NAME = "slide_resource_descriptor_cache", CACHE_RESOURCE_DESCRIPTORS_NAME = "slide_resource_descriptors_cache"; private static final String DEFINITION_XML_FILE_ENDING = ".def.xml"; static final long THREE_MINUTES = 60 * 3; private Map<SlideAction, List<NamespaceAccessToken>> activeTransactions = new HashMap<SlideAction, List<NamespaceAccessToken>>(); @Autowired private DomainConfig domainConfig; private Structure structure; private Content content; private Security security; private AuthenticationBusiness authenticationBusiness; private boolean initialized; private void initializeSimpleSlideServiceBean() { if (initialized) { initialized = !(structure == null || content == null || security == null); } if (initialized) { return; } initialized = true; try { if (!Domain.isInitialized()) { domainConfig.initialize(); } NamespaceAccessToken namespace = getNamespace(); structure = structure == null ? namespace.getStructureHelper() : structure; content = content == null ? namespace.getContentHelper(): content; security = security == null ? namespace.getSecurityHelper() : security; } catch(Throwable e) { initialized = false; LOGGER.warning("Error while initializing Simple Slide API, will try again on next request"); } } private AuthenticationBusiness getAuthenticationBusiness() { if (authenticationBusiness == null) { authenticationBusiness = getServiceInstance(AuthenticationBusiness.class); } return authenticationBusiness; } SlideToken getContentToken() { initializeSimpleSlideServiceBean(); String userPrincipals = null; try { AuthenticationBusiness ab = getAuthenticationBusiness(); userPrincipals = ab.getRootUserCredentials().getUserName(); } catch(Exception e) { LOGGER.log(Level.WARNING, "Error getting user's principals", e); } SlideToken token = new SlideTokenImpl(new CredentialsToken(userPrincipals)); token.setForceStoreEnlistment(true); return token; } private String getAuthorsXML(User user) { String firstName = null; String lastName = null; String unknown = "Unknown"; if (user != null) { firstName = user.getFirstName(); lastName = user.getLastName(); } String authors = new StringBuilder("<authors><author><firstname>").append(firstName == null ? unknown : firstName).append("</firstname><lastname>") .append(lastName == null ? unknown : lastName).append("</lastname></author></authors>").toString(); return authors; } private String computeEtag(String uri, NodeRevisionDescriptor nrd) throws Exception { StringBuffer result = new StringBuffer(String.valueOf(System.currentTimeMillis())).append(CoreConstants.UNDER).append(uri.hashCode()) .append(CoreConstants.UNDER).append(nrd.getLastModified()).append(CoreConstants.UNDER).append(nrd.getContentLength()); return DigestUtils.md5Hex(result.toString()); } @Override public boolean upload(InputStream stream, String uploadPath, String fileName, String contentType, User user, boolean closeStream) throws Exception { if (structure == null) { getLogger().warning("Structure (" + Structure.class.getName() + ") is not initialized"); return false; } if (content == null) { getLogger().warning("Content (" + Content.class.getName() + ") is not initialized"); return false; } if (security == null) { getLogger().warning("Security (" + Security.class.getName() + ") is not initialized"); return false; } if (stream == null || uploadPath == null || fileName == null) { return false; } uploadPath = uploadPath.concat(fileName); uploadPath = getNormalizedPath(uploadPath); NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return false; } boolean error = false; SlideToken token = getContentToken(); try { NodeRevisionNumber lastRevision = null; try { structure.retrieve(token, uploadPath); lastRevision = content.retrieve(token, uploadPath).getLatestRevision(); } catch (ObjectNotFoundException e) { SubjectNode subjectNode = new SubjectNode(); structure.create(token, subjectNode, uploadPath); } if (lastRevision == null) { lastRevision = new NodeRevisionNumber(); } else { lastRevision = new NodeRevisionNumber(lastRevision, false); } // Node revision descriptor IWTimestamp now = IWTimestamp.RightNow(); @SuppressWarnings("rawtypes") NodeRevisionDescriptor revisionDescriptor = new NodeRevisionDescriptor(lastRevision, NodeRevisionDescriptors.MAIN_BRANCH, new Vector(), new ArrayList()); revisionDescriptor.setResourceType(CoreConstants.EMPTY); revisionDescriptor.setSource(CoreConstants.EMPTY); revisionDescriptor.setContentLanguage(Locale.ENGLISH.getLanguage()); revisionDescriptor.setLastModified(now.getDate()); revisionDescriptor.setETag(computeEtag(uploadPath, revisionDescriptor)); revisionDescriptor.setCreationDate(now.getDate()); // Owner String creator = ((SubjectNode) security.getPrincipal(token)).getPath().lastSegment(); revisionDescriptor.setCreationUser(creator); revisionDescriptor.setOwner(creator); if (contentType != null) { revisionDescriptor.setContentType(contentType); } // Properties (for now - just owner) NodeProperty newProperty = new NodeProperty("authors", getAuthorsXML(user)); revisionDescriptor.setProperty(newProperty); // Create content NodeRevisionContent revisionContent = new NodeRevisionContent(); revisionContent.setContent(stream); // Important to create NodeRevisionDescriptors separately to be able to tell it to use versioning if (lastRevision.toString().equals("1.0")) { content.create(token, uploadPath, true); } content.create(token, uploadPath, revisionDescriptor, revisionContent); putValueIntoCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, uploadPath, Boolean.TRUE); putValueIntoCache(CACHE_RESOURCE_DESCRIPTOR_NAME, THREE_MINUTES, uploadPath, revisionDescriptor); return true; } catch (Throwable t) { error = true; if (isNeededToPrintSlideExceptions()) { LOGGER.log(Level.WARNING, "Error while uploading: " + uploadPath, t); } else { LOGGER.warning("Error while uploading: " + uploadPath); } rollbackTransaction(namespace); } finally { if (closeStream) { IOUtil.closeInputStream(stream); } if (error) { rollbackTransaction(namespace); } else { commitTransaction(namespace); } } return false; } private boolean isNeededToPrintSlideExceptions() { return getApplication().getSettings().getBoolean("slide.print_errors", Boolean.FALSE); } @Override public boolean upload(InputStream stream, String uploadPath, String fileName, String contentType, User user) throws Exception { return upload(stream, uploadPath, fileName, contentType, user, true); } private NodeRevisionDescriptors getNodeRevisionDescriptors(String pathToNode) { if (StringUtil.isEmpty(pathToNode)) { return null; } if (content == null) { getLogger().warning("Content (" + Content.class.getName() + ") is not initialized"); return null; } NodeRevisionDescriptors descriptors = getValueFromCache(CACHE_RESOURCE_DESCRIPTORS_NAME, THREE_MINUTES, pathToNode); if (descriptors != null) { return descriptors; } SlideToken rootToken = getContentToken(); if (rootToken == null) { return null; } NamespaceAccessToken namespace = startTransaction(SlideAction.ROLLBACK); if (namespace == null) { return null; } pathToNode = getNormalizedPath(pathToNode); try { descriptors = content.retrieve(rootToken, pathToNode); putValueIntoCache(CACHE_RESOURCE_DESCRIPTORS_NAME, THREE_MINUTES, pathToNode, descriptors); return descriptors; } catch (ObjectNotFoundException e) { deletetDefinitionFile(e.getObjectUri()); } catch (Throwable e) { } finally { rollbackTransaction(namespace); } return null; } private NodeRevisionDescriptor getNodeRevisionDescriptor(String path) throws AccessDeniedException, LinkedObjectNotFoundException, RevisionDescriptorNotFoundException, ObjectLockedException, ServiceAccessException, VetoException { NodeRevisionDescriptors revisionDescriptors = getNodeRevisionDescriptors(path); return getNodeRevisionDescriptor(revisionDescriptors); } @Override public NodeRevisionDescriptor getRevisionDescriptor(String path) { path = getNormalizedPath(path); if (StringUtil.isEmpty(path)) { return null; } try { return getNodeRevisionDescriptor(path); } catch (Throwable t) { LOGGER.warning("Error getting node revision descriptor for: " + path); } return null; } private NodeRevisionDescriptor getRevisionDescriptor(NodeRevisionDescriptors revisionDescriptors) { try { return getNodeRevisionDescriptor(revisionDescriptors); } catch (Throwable t) { LOGGER.warning("Error getting node revision descriptor!"); } return null; } private NodeRevisionDescriptor getNodeRevisionDescriptor(NodeRevisionDescriptors revisionDescriptors) throws AccessDeniedException, LinkedObjectNotFoundException, RevisionDescriptorNotFoundException, ObjectLockedException, ServiceAccessException, VetoException { if (content == null) { getLogger().warning("Content (" + Content.class.getName() + ") is not initialized"); return null; } String path = getNormalizedPath(revisionDescriptors.getUri()); NodeRevisionDescriptor descriptor = getValueFromCache(CACHE_RESOURCE_DESCRIPTOR_NAME, THREE_MINUTES, path); if (descriptor != null) { return descriptor; } SlideToken rootToken = getContentToken(); if (rootToken == null) { return null; } NamespaceAccessToken namespace = startTransaction(SlideAction.ROLLBACK); if (namespace == null) { return null; } try { descriptor = content.retrieve(rootToken, revisionDescriptors, revisionDescriptors.getLatestRevision()); putValueIntoCache(CACHE_RESOURCE_DESCRIPTOR_NAME, THREE_MINUTES, path, descriptor); return descriptor; } catch (ObjectNotFoundException e) { deletetDefinitionFile(e.getObjectUri()); return null; } finally { rollbackTransaction(namespace); } } @Override public boolean checkExistance(String pathToFile) { pathToFile = getNormalizedPath(pathToFile); if (structure == null) { getLogger().warning("Structure (" + Structure.class.getName() + ") is not initialized"); return false; } Boolean cachedAnswer = getValueFromCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, pathToFile); if (cachedAnswer != null) { return cachedAnswer; } SlideToken rootToken = getContentToken(); if (rootToken == null) { return false; } NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return false; } boolean rollback = true; ObjectNode node = null; try { node = structure.retrieve(rootToken, pathToFile); commitTransaction(namespace); rollback = false; Boolean exists = node == null ? false : true; putValueIntoCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, pathToFile, exists); return exists; } catch (ObjectNotFoundException e) { deletetDefinitionFile(e.getObjectUri()); } catch (LinkedObjectNotFoundException e) { } catch (AccessDeniedException e) { LOGGER.warning("Current user can not access " + pathToFile + " - access denied!"); } catch (ServiceAccessException e) { LOGGER.log(Level.WARNING, "Error accessing " + pathToFile, e); } catch (Throwable t) { LOGGER.log(Level.WARNING, "Some error occurred while trying to retrieve " + pathToFile, t); } finally { if (rollback) { rollbackTransaction(namespace); } } return false; } private NodeRevisionContent getNodeContent(String pathToFile) { if (content == null) { getLogger().warning("Content (" + Content.class.getName() + ") is not initialized"); return null; } NodeRevisionDescriptors revisionDescriptors = getNodeRevisionDescriptors(pathToFile); if (revisionDescriptors == null || !revisionDescriptors.hasRevisions()) { return null; } NodeRevisionDescriptor revisionDescriptor = getRevisionDescriptor(pathToFile); if (revisionDescriptor == null) { return null; } SlideToken rootToken = getContentToken(); if (rootToken == null) { return null; } NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return null; } boolean rollback = false; try { return content.retrieve(rootToken, revisionDescriptors, revisionDescriptor); } catch (ObjectNotFoundException e) { LOGGER.log(Level.WARNING, "Error getting InputStream for: " + pathToFile, e); deletetDefinitionFile(e.getObjectUri()); rollback = true; } catch (Throwable e) { LOGGER.log(Level.WARNING, "Error getting InputStream for: " + pathToFile, e); rollback = true; } finally { if (rollback) { rollbackTransaction(namespace); } else { commitTransaction(namespace); } } return null; } @Override public InputStream getInputStream(String pathToFile) { pathToFile = getNormalizedPath(pathToFile); NodeRevisionContent nodeContent = getNodeContent(pathToFile); if (nodeContent == null) { return null; } InputStream stream = null; try { stream = nodeContent.streamContent(); } catch (Throwable e) { LOGGER.log(Level.WARNING, "Error getting InputStream for: " + pathToFile, e); } return stream; } @Override public boolean setContent(String pathToFile, InputStream contentStream) { if (content == null) { getLogger().warning("Content (" + Content.class.getName() + ") is not initialized"); return false; } pathToFile = getNormalizedPath(pathToFile); NodeRevisionContent nodeContent = getNodeContent(pathToFile); if (nodeContent == null) { return Boolean.FALSE; } SlideToken rootToken = getContentToken(); if (rootToken == null) { return Boolean.FALSE; } NodeRevisionDescriptors descriptors = getNodeRevisionDescriptors(pathToFile); NodeRevisionDescriptor descriptor = getRevisionDescriptor(descriptors); NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return Boolean.FALSE; } boolean rollback = false; try { descriptor.setContentLength(contentStream.available()); descriptor.setLastModified(new Date(System.currentTimeMillis())); nodeContent.setContent(contentStream); content.store(rootToken, pathToFile, descriptor, nodeContent); } catch (Throwable e) { rollback = Boolean.TRUE; LOGGER.log(Level.WARNING, "Error setting content InputStream for: " + pathToFile, e); if (e instanceof ObjectNotFoundException) { deletetDefinitionFile(((ObjectNotFoundException) e).getObjectUri()); } else if (e instanceof RevisionDescriptorNotFoundException) { deletetDefinitionFile(((RevisionDescriptorNotFoundException) e).getObjectUri()); } return Boolean.FALSE; } finally { if (rollback) { rollbackTransaction(namespace); } else { commitTransaction(namespace); } } putValueIntoCache(CACHE_RESOURCE_DESCRIPTORS_NAME, THREE_MINUTES, pathToFile, descriptors); putValueIntoCache(CACHE_RESOURCE_DESCRIPTOR_NAME, THREE_MINUTES, pathToFile, descriptor); putValueIntoCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, pathToFile, Boolean.TRUE); return Boolean.TRUE; } @Override public Enumeration<NodePermission> getPermissions(String path) { path = getNormalizedPath(path); if (StringUtil.isEmpty(path)) { return null; } List<NodePermission> permissions = new ArrayList<NodePermission>(); permissions = getPermissions(permissions, path, path, getContentToken()); return Collections.enumeration(permissions); } @SuppressWarnings("unchecked") private List<NodePermission> getPermissions(List<NodePermission> allPermissions, String path, String originalPath, SlideToken content) { if (path == null) { return allPermissions; } if (security == null) { getLogger().warning("Security (" + Security.class.getName() + ") is not initialized"); return null; } NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return allPermissions; } Enumeration<NodePermission> permissions = null; try { permissions = security.enumeratePermissions(content, path); } catch (Throwable t) { LOGGER.log(Level.WARNING, "Error getting permissions for: " + path, t); rollbackTransaction(namespace); if (t instanceof ObjectNotFoundException) { deletetDefinitionFile(((ObjectNotFoundException) t).getObjectUri()); } return allPermissions; } if (!commitTransaction(namespace)) { return allPermissions; } if (permissions != null && permissions.hasMoreElements()) { List<NodePermission> currentPermissions = Collections.list(permissions); if (path.equals(originalPath)) { allPermissions.addAll(currentPermissions); } else { for (NodePermission permission: currentPermissions) { if (permission.isInheritable() && !allPermissions.contains(permission)) { allPermissions.add(permission); } } } } return getPermissions(allPermissions, getParentPath(path), path, content); } private String getParentPath(String path) { if (StringUtil.isEmpty(path)) { return null; } if (path.endsWith(CoreConstants.SLASH)) { path = path.substring(0, path.length() - 1); } if (path.equals(CoreConstants.WEBDAV_SERVLET_URI)) { return path; } int lastSlash = path.lastIndexOf(CoreConstants.SLASH); if (lastSlash < 0) { return null; } return path.substring(0, lastSlash); } @Override @SuppressWarnings("unchecked") public boolean setPermissions(String path, Ace[] aces) { if (ArrayUtil.isEmpty(aces)) { return false; } if (!createStructure(path)) { return false; } path = getNormalizedPath(path); if (security == null) { getLogger().warning("Security (" + Security.class.getName() + ") is not initialized"); return false; } NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return false; } Collection<NodePermission> permissions = new ArrayList<NodePermission>(aces.length); for (Ace ace: aces) { List<String> actions = new ArrayList<String>(); Enumeration<Privilege> privileges = ace.enumeratePrivileges(); if (privileges != null) { while (privileges.hasMoreElements()) { Privilege p = privileges.nextElement(); actions.add(p.getName()); } } for (String action: actions) { String subjectUri = ace.getPrincipal(); String actionUri = action; if (!Privilege.ALL.getName().equals(action)) { actionUri = action.startsWith(IWSlideConstants.PATH_ACTIONS) ? action : IWSlideConstants.PATH_ACTIONS.concat(CoreConstants.SLASH).concat(action); } NodePermission permission = new NodePermission(path, subjectUri, actionUri); permission.setInheritable(ace.isInheritable()); String inheritedFrom = ace.getInheritedFrom(); permission.setInheritedFrom(inheritedFrom); permission.setNegative(ace.isNegative()); permission.setProtected(ace.isProtected()); permissions.add(permission); } } try { security.setPermissions(getContentToken(), path, Collections.enumeration(permissions)); } catch (Throwable t) { LOGGER.log(Level.WARNING, "Error setting ACLs " + aces + " for " + path, t); rollbackTransaction(namespace); if (t instanceof ObjectNotFoundException) { deletetDefinitionFile(((ObjectNotFoundException) t).getObjectUri()); } return false; } commitTransaction(namespace); return true; } private String getNormalizedPath(String path) { if (StringUtil.isEmpty(path)) { return path; } if (path.startsWith(CoreConstants.WEBDAV_SERVLET_URI)) { path = StringHandler.replace(path, CoreConstants.WEBDAV_SERVLET_URI, CoreConstants.EMPTY); } try { path = URLDecoder.decode(path, CoreConstants.ENCODING_UTF8); } catch (Exception e) { LOGGER.log(Level.WARNING, "Error decoding: " + path, e); } return path; } NamespaceAccessToken startTransaction(SlideAction action) { initializeSimpleSlideServiceBean(); NamespaceAccessToken namespace = getNamespace(); if (namespace == null) { return null; } try { if (namespace.getStatus() == 0) { // Transaction was begun already! LOGGER.warning("TRANSACTION " + namespace + " already has started!"); return namespace; } namespace.begin(); } catch(Throwable e) { LOGGER.log(Level.WARNING, "Cannot start user transaction", e); return null; } finally { addTransaction(action, namespace); } return namespace; } private List<NamespaceAccessToken> getTransactions(SlideAction action) { return activeTransactions.get(action); } private void addTransaction(SlideAction action, NamespaceAccessToken namespace) { synchronized (activeTransactions) { List<NamespaceAccessToken> transactions = getTransactions(action); if (transactions == null) { transactions = new ArrayList<NamespaceAccessToken>(); activeTransactions.put(action, transactions); } synchronized (transactions) { transactions.add(namespace); } } } private boolean canRollback() { return Boolean.TRUE; } boolean rollbackTransaction(NamespaceAccessToken namespace) { if (namespace == null) { return false; } try { if (canRollback()) { namespace.rollback(); } } catch (Throwable e) { if (isNeededToPrintSlideExceptions()) { LOGGER.log(Level.WARNING, "Cannot rollback user transaction", e); } return false; } finally { finishTransaction(namespace, SlideAction.ROLLBACK); } return true; } private boolean canCommit(NamespaceAccessToken namespace) { return Boolean.TRUE; } boolean commitTransaction(NamespaceAccessToken namespace) { if (namespace == null) { return false; } try { if (canCommit(namespace)) { namespace.commit(); } } catch (Throwable e) { if (isNeededToPrintSlideExceptions()) { LOGGER.log(Level.WARNING, "Cannot finish user transaction", e); } return false; } finally { finishTransaction(namespace, SlideAction.COMMIT); } return true; } private void finishTransaction(NamespaceAccessToken namespace, SlideAction action) { if (!removeTransaction(namespace, action)) { // Transaction was started for commit but now trying to rollback! SlideAction intendedAction = SlideAction.COMMIT == action ? SlideAction.ROLLBACK : SlideAction.COMMIT; removeTransaction(namespace, intendedAction); } } private boolean removeTransaction(NamespaceAccessToken namespace, SlideAction action) { synchronized (activeTransactions) { List<NamespaceAccessToken> transactions = getTransactions(action); if (transactions == null) { return Boolean.FALSE; } synchronized (transactions) { if (transactions.contains(namespace)) { LOGGER.fine("REMOVING transaction " + namespace + " from " + transactions + ", action: " + action); transactions.remove(namespace); return Boolean.TRUE; } else { LOGGER.fine("Transactions " + transactions + " does not contain this: " + namespace + ", try oposite action?"); } } } return Boolean.FALSE; } @Override public boolean createStructure(String path) { return createStructure(getContentToken(), path, Boolean.TRUE); } private boolean createStructure(SlideToken contentToken, String path, boolean checkParents) { path = getNormalizedPath(path); if (path == null) { return false; } if (structure == null) { getLogger().warning("Structure (" + Structure.class.getName() + ") is not initialized"); return false; } if (content == null) { getLogger().warning("Content (" + Content.class.getName() + ") is not initialized"); return false; } if (checkParents) { String[] paths = path.split(CoreConstants.SLASH); String parentPath = CoreConstants.SLASH; for (String pathPart: paths) { if (!StringUtil.isEmpty(pathPart)) { parentPath = parentPath.concat(pathPart).concat(CoreConstants.SLASH); if (!createStructure(contentToken, parentPath, Boolean.FALSE)) { return false; } } } } if (checkExistance(path)) { return true; } NamespaceAccessToken namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return false; } boolean error = false; ObjectNode node = new SubjectNode(path); NodeRevisionDescriptor descriptor = null; try { structure.create(contentToken, node, path); commitTransaction(namespace); } catch (ObjectAlreadyExistsException e) { descriptor = getRevisionDescriptor(path); if (descriptor != null) { putValueIntoCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, path, Boolean.TRUE); return true; } } catch (Throwable t) { if (t instanceof ObjectNotFoundException) { deletetDefinitionFile(((ObjectNotFoundException) t).getObjectUri()); } error = true; LOGGER.log(Level.WARNING, "Error creating structure: " + path, t); return false; } finally { if (error) { rollbackTransaction(namespace); } } try { namespace = startTransaction(SlideAction.COMMIT); if (namespace == null) { return false; } descriptor = new NodeRevisionDescriptor(); NodeRevisionContent nodeContent = new NodeRevisionContent(); nodeContent.setContent(path.getBytes()); content.create(contentToken, path, descriptor, nodeContent); commitTransaction(namespace); putValueIntoCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, path, Boolean.TRUE); putValueIntoCache(CACHE_RESOURCE_DESCRIPTOR_NAME, THREE_MINUTES, path, descriptor); return true; } catch (Throwable t) { if (t instanceof ObjectNotFoundException) { deletetDefinitionFile(((ObjectNotFoundException) t).getObjectUri()); } error = true; LOGGER.log(Level.WARNING, "Error creating descriptor: " + path + " - " + t.getMessage(), t); } finally { if (error) { rollbackTransaction(namespace); } } return false; } @Override public boolean delete(String path) { path = getNormalizedPath(path); if (StringUtil.isEmpty(path)) { return false; } IWSlideServiceBean repositoryService = (IWSlideServiceBean) getServiceInstance(IWSlideService.class); if (repositoryService == null) { return false; } DeleteWorker deleter = new DeleteWorker(repositoryService, this, path); try { deleter.run(); return deleter.isWorkFinishedSuccessfully(); } catch (Throwable t) { LOGGER.log(Level.WARNING, "Error while deleting: " + path, t); } return false; } @Override public void deletetDefinitionFile(String path) { String workingDir = System.getProperty("user.dir"); if (workingDir == null) { LOGGER.warning("Unknown directory for Slide store!"); return; } workingDir = workingDir.concat("/store/metadata"); String realPath = workingDir.concat(path).concat(DEFINITION_XML_FILE_ENDING); String parentFile = null; try { File xml = new File(realPath); if (xml != null && xml.exists()) { parentFile = xml.getParent(); xml.delete(); } if (parentFile == null) { int lastSlash = realPath.lastIndexOf(CoreConstants.SLASH); if (lastSlash != -1) { parentFile = realPath.substring(0, lastSlash); } } if (StringUtil.isEmpty(parentFile)) { LOGGER.warning("Parent file can not be found for: " + realPath); return; } String parentFileXMLURI = parentFile.concat(DEFINITION_XML_FILE_ENDING); File parentXMLFile = new File(parentFileXMLURI); if (parentXMLFile == null || !parentXMLFile.exists()) { LOGGER.warning("File " + parentFileXMLURI + " does not exist!"); return; } Document parentXML = XmlUtil.getJDOMXMLDocument(new FileInputStream(parentXMLFile)); if (parentXML == null) { LOGGER.warning("XML document was not loaded from: " + parentFileXMLURI); return; } Namespace n = Namespace.getNamespace(CoreConstants.EMPTY, CoreConstants.EMPTY); List<Element> children = XmlUtil.getElementsByXPath(parentXML.getRootElement(), "child", n); if (ListUtil.isEmpty(children)) { return; } List<Element> toRemove = new ArrayList<Element>(); for (Element child: children) { Attribute uuri = child.getAttribute("uuri"); if (path.equals(uuri.getValue())) { toRemove.add(child); } } if (toRemove.size() > 0) { for (Iterator<Element> toRemoveIter = toRemove.iterator(); toRemoveIter.hasNext();) { toRemoveIter.next().detach(); } InputStream stream = StringHandler.getStreamFromString(XmlUtil.getPrettyJDOMDocument(parentXML)); FileUtil.streamToFile(stream, parentXMLFile); } } catch (Exception e) { LOGGER.log(Level.WARNING, "Error deleting XML file (from metadata) for: " + path, e); } } @Override public void onSlideChange(IWContentEvent contentEvent) { AbstractEventMethod method = contentEvent.getMethod(); String path = getNormalizedPath(contentEvent.getContentEvent().getUri()); if (ContentEvent.REMOVE.equals(method)) { removeValueFromCache(CACHE_RESOURCE_DESCRIPTORS_NAME, THREE_MINUTES, path); removeValueFromCache(CACHE_RESOURCE_DESCRIPTOR_NAME, THREE_MINUTES, path); removeValueFromCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, path); } else if (ContentEvent.STORE.equals(method)) { putValueIntoCache(CACHE_RESOURCE_EXISTANCE_NAME, -1, path, Boolean.TRUE); } } private <K extends Serializable, V> void putValueIntoCache(String cacheName, long ttl, K key, V value) { Map<K, V> cache = getCache(cacheName, ttl, CACHE_SIZE); if (cache != null) { cache.put(key, value); } } private <K extends Serializable, V> V getValueFromCache(String cacheName, long ttl, K key) { Map<K, V> cache = getCache(cacheName, ttl, CACHE_SIZE); if (cache != null) { return cache.get(key); } return null; } <K extends Serializable, V> V removeValueFromCache(String cacheName, long ttl, K key) { Map<K, V> cache = getCache(cacheName, ttl, CACHE_SIZE); if (cache != null) { return cache.remove(key); } return null; } @Override @SuppressWarnings("unchecked") public WebdavResources getResources(String path) { WebdavResources resources = new WebdavResources(); path = getNormalizedPath(path); if (path == null) { return resources; } if (structure == null) { getLogger().warning("Structure (" + Structure.class.getName() + ") is not initialized"); return null; } NamespaceAccessToken namespace = startTransaction(SlideAction.ROLLBACK); if (namespace == null) { return resources; } ObjectNode node = null; try { node = structure.retrieve(getContentToken(), path); } catch (Exception e) { LOGGER.log(Level.WARNING, "Error while trying to retrieve: " + path, e); if (e instanceof ObjectNotFoundException) { deletetDefinitionFile(((ObjectNotFoundException) e).getObjectUri()); } } finally { rollbackTransaction(namespace); } if (node == null) { return resources; } Vector<String> children = node.getChildren(); if (ListUtil.isEmpty(children)) { return resources; } IWSlideService slideService = getServiceInstance(IWSlideService.class); for (String child: children) { try { String name = child; if (child.indexOf(CoreConstants.SLASH) != -1) { name = child.substring(child.lastIndexOf(CoreConstants.SLASH)); } if (checkExistance(child)) { resources.addResource(name, slideService.getWebdavResourceAuthenticatedAsRoot(child)); } } catch (Exception e) { LOGGER.log(Level.WARNING, "Error while adding resource: " + child + " to the " + path, e); } } return resources; } private NamespaceAccessToken getNamespace() { try { return Domain.accessNamespace(new SecurityToken(CoreConstants.EMPTY), Domain.getDefaultNamespace()); } catch (Throwable t) { LOGGER.log(Level.SEVERE, "Error getting namespace (instanceof "+NamespaceAccessToken.class+")", t); } return null; } Security getSecurity() { return security; } Content getContent() { return content; } Structure getStructure() { return structure; } }