Package org.apache.lenya.cms.repository

Source Code of org.apache.lenya.cms.repository.SourceNodeRCML

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-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.
*
*/

/* $Id: RCML.java 473861 2006-11-12 03:51:14Z gregor $  */

package org.apache.lenya.cms.repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.avalon.framework.service.ServiceManager;
import org.apache.excalibur.source.SourceResolver;
import org.apache.lenya.cms.cocoon.source.SourceUtil;
import org.apache.lenya.cms.rc.CheckInEntry;
import org.apache.lenya.cms.rc.CheckOutEntry;
import org.apache.lenya.cms.rc.RCML;
import org.apache.lenya.cms.rc.RCMLEntry;
import org.apache.lenya.cms.rc.RevisionControlException;
import org.apache.lenya.util.Assert;
import org.apache.lenya.xml.DocumentHelper;
import org.apache.lenya.xml.NamespaceHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* Handle with the RCML file
*/
public class SourceNodeRCML implements RCML {

    protected static final String NAMESPACE = "";

    private boolean dirty = false;
    private int maximalNumberOfEntries = 5;
    private Vector entries;

    private ServiceManager manager;

    private String contentSourceUri;
    private String metaSourceUri;

    private static Map ELEMENTS = new HashMap();
    protected static final String ELEMENT_CHECKIN = "CheckIn";
    protected static final String ELEMENT_CHECKOUT = "CheckOut";
    protected static final String ELEMENT_BACKUP = "Backup";
    protected static final String ELEMENT_TIME = "Time";
    protected static final String ELEMENT_VERSION = "Version";
    protected static final String ELEMENT_IDENTITY = "Identity";
    protected static final String ELEMENT_XPSREVISIONCONTROL = "XPSRevisionControl";
    protected static final String ELEMENT_SESSION = "session";

    protected static final String ATTR_BACKUP = "backup";
    protected static final String ATTR_TIME = "time";
    protected static final String ATTR_VERSION = "version";
    protected static final String ATTR_IDENTITY = "identity";
    protected static final String ATTR_SESSION = "session";

    {
        ELEMENTS.put(new Short(ci), ELEMENT_CHECKIN);
        ELEMENTS.put(new Short(co), ELEMENT_CHECKOUT);
    }

    /**
     * @param contentSourceUri The content source URI.
     * @param metaSourceUri The meta source URI.
     * @param manager The service manager.
     */
    public SourceNodeRCML(String contentSourceUri, String metaSourceUri, ServiceManager manager) {
        this.maximalNumberOfEntries = 200;
        this.maximalNumberOfEntries = (2 * this.maximalNumberOfEntries) + 1;
        this.manager = manager;
        this.contentSourceUri = contentSourceUri;
        this.metaSourceUri = metaSourceUri;
    }

    protected static final String RCML_EXTENSION = ".rcml";

    private static final String ALL_SESSIONS = "unrestricted";

    protected String getRcmlSourceUri() {
        return this.contentSourceUri + RCML_EXTENSION;
    }

    /**
     * Call the method write, if the document is dirty
     *
     * @throws IOException if an error occurs
     * @throws Exception if an error occurs
     */
    protected void finalize() throws IOException, Exception {
        if (this.isDirty()) {
            write();
        }
    }

    /**
     * Write the XML RCML-document in the RCML-file.
     * @throws RevisionControlException if an error occurs
     */
    public synchronized void write() throws RevisionControlException {
        NamespaceHelper helper = saveToXml();
        Assert.notNull("XML document", helper);
        try {
            SourceUtil.writeDOM(helper.getDocument(), getRcmlSourceUri(), this.manager);
        } catch (Exception e) {
            throw new RevisionControlException(e);
        }
        clearDirty();
    }

    /**
     * Write a new entry for a check out or a check in the RCML-File made by the user with identity
     * at time
     * @param node The node.
     * @param type co for a check out, ci for a check in
     * @param time
     * @param backup Create backup element (only considered for check-in entries).
     * @param newVersion If the revision number shall be increased (only considered for check-in
     *        entries).
     * @param restrictedToSession If the check-out is restricted to the session (only considered for
     *        check-out entries).
     * @throws RevisionControlException if an error occurs
     */
    public synchronized void checkOutIn(Node node, short type, long time, boolean backup,
            boolean newVersion, boolean restrictedToSession) throws RevisionControlException {

        String identity = node.getSession().getIdentity().getUser().getId();

        Vector entries = getEntries();
        if (entries.size() == 0) {
            if (type == ci) {
                throw new IllegalStateException("Can't check in - not checked out.");
            }
        } else {
            RCMLEntry latestEntry = getLatestEntry();
            if (type == latestEntry.getType()) {
                String elementName = (String) ELEMENTS.get(new Short(type));
                throw new IllegalStateException("RCML entry type <" + elementName
                        + "> not allowed twice in a row. Before: [" + latestEntry.getIdentity()
                        + "], now: [" + identity + "], node: [" + this.contentSourceUri + "]");
            }
        }

        String sessionId;
        if (type == RCML.co && !restrictedToSession) {
            sessionId = ALL_SESSIONS;
        }
        else {
            sessionId = node.getSession().getId();
        }

        RCMLEntry entry;
        switch (type) {
        case RCML.ci:
            int version = 0;
            CheckInEntry latestEntry = getLatestCheckInEntry();
            if (latestEntry != null) {
                version = latestEntry.getVersion();
            }
            if (newVersion) {
                version++;
            }
            entry = new CheckInEntry(sessionId, identity, time, version, backup);
            break;
        case RCML.co:
            entry = new CheckOutEntry(sessionId, identity, time);
            break;
        default:
            throw new IllegalArgumentException("No such type: [" + type + "]");
        }

        entries.add(0, entry);
        setDirty();
    }

    protected Element saveToXml(NamespaceHelper helper, RCMLEntry entry)
            throws RevisionControlException {
        String elementName = (String) ELEMENTS.get(new Short(entry.getType()));
        Element entryElement = helper.createElement(elementName);

        entryElement.setAttribute(ATTR_IDENTITY, entry.getIdentity());
        entryElement.setAttribute(ATTR_SESSION, entry.getSessionId());
        entryElement.setAttribute(ATTR_TIME, Long.toString(entry.getTime()));

        if (entry.getType() == ci) {
            CheckInEntry checkInEntry = (CheckInEntry) entry;
            entryElement.setAttribute(ATTR_VERSION, Integer.toString(checkInEntry.getVersion()));
            if (checkInEntry.hasBackup()) {
                entryElement.setAttribute(ATTR_BACKUP, "true");
            }
        }

        return entryElement;
    }

    protected NamespaceHelper saveToXml() throws RevisionControlException {
        try {
            NamespaceHelper helper = new NamespaceHelper(NAMESPACE, "", ELEMENT_XPSREVISIONCONTROL);
            Element root = helper.getDocument().getDocumentElement();
            Vector entries = getEntries();
            for (Iterator i = entries.iterator(); i.hasNext();) {
                RCMLEntry entry = (RCMLEntry) i.next();
                Element element = saveToXml(helper, entry);
                root.appendChild(element);
            }
            return helper;
        } catch (Exception e) {
            throw new RevisionControlException("Could create revision control XML ["
                    + getRcmlSourceUri() + "]", e);
        }
    }

    protected Element getLatestElement(NamespaceHelper helper, String type)
            throws RevisionControlException {
        Element parent = helper.getDocument().getDocumentElement();
        return helper.getFirstChild(parent, type);
    }

    /**
     * get the latest check out
     * @return CheckOutEntry The entry of the check out
     * @throws RevisionControlException if an error occurs
     */
    public CheckOutEntry getLatestCheckOutEntry() throws RevisionControlException {
        return (CheckOutEntry) getLatestEntry(RCML.co);
    }

    /**
     * get the latest check in
     * @return CheckInEntry The entry of the check in
     * @throws RevisionControlException if an error occurs
     */
    public CheckInEntry getLatestCheckInEntry() throws RevisionControlException {
        return (CheckInEntry) getLatestEntry(RCML.ci);
    }

    /**
     * get the latest entry (a check out or check in)
     * @param type The type.
     * @return RCMLEntry The entry of the check out/in
     * @throws RevisionControlException if an error occurs
     */
    public RCMLEntry getLatestEntry(short type) throws RevisionControlException {
        Vector entries = getEntries();
        for (Iterator i = entries.iterator(); i.hasNext();) {
            RCMLEntry entry = (RCMLEntry) i.next();
            if (entry.getType() == type) {
                return entry;
            }
        }
        return null;
    }

    public RCMLEntry getLatestEntry() throws RevisionControlException {
        Vector entries = getEntries();
        if (entries.isEmpty()) {
            return null;
        } else {
            return (RCMLEntry) entries.firstElement();
        }
    }

    protected RCMLEntry getEntry(NamespaceHelper helper, Element element) {
        if (element.hasAttribute(ATTR_IDENTITY)) {
            String type = element.getLocalName();
            String sessionId = element.getAttribute(ATTR_SESSION);
            String identity = element.getAttribute(ATTR_IDENTITY);
            String timeString = element.getAttribute(ATTR_TIME);
            long time = new Long(timeString).longValue();
            if (type.equals(ELEMENT_CHECKIN)) {
                String versionString = element.getAttribute(ATTR_VERSION);
                int version = new Integer(versionString).intValue();
                boolean backup = element.hasAttribute(ATTR_BACKUP);
                return new CheckInEntry(sessionId, identity, time, version, backup);
            } else if (type.equals(ELEMENT_CHECKOUT)) {
                return new CheckOutEntry(sessionId, identity, time);
            } else {
                throw new RuntimeException("Unsupported RCML entry type: [" + type + "]");
            }
        } else {
            return getLegacyEntry(helper, element);
        }
    }

    protected RCMLEntry getLegacyEntry(NamespaceHelper helper, Element element) {
        String type = element.getLocalName();
        String sessionId = getChildValue(helper, element, ELEMENT_SESSION, "");
        String identity = getChildValue(helper, element, ELEMENT_IDENTITY);
        String timeString = getChildValue(helper, element, ELEMENT_TIME);
        long time = new Long(timeString).longValue();
        if (type.equals(ELEMENT_CHECKIN)) {
            String versionString = getChildValue(helper, element, ELEMENT_VERSION);
            int version = new Integer(versionString).intValue();
            boolean backup = helper.getChildren(element, ELEMENT_BACKUP).length > 0;
            return new CheckInEntry(sessionId, identity, time, version, backup);
        } else if (type.equals(ELEMENT_CHECKOUT)) {
            return new CheckOutEntry(sessionId, identity, time);
        } else {
            throw new RuntimeException("Unsupported RCML entry type: [" + type + "]");
        }
    }

    protected String getChildValue(NamespaceHelper helper, Element element, String childName,
            String defaultValue) {
        Element child = DocumentHelper.getFirstChild(element, NAMESPACE, childName);
        if (child == null) {
            return defaultValue;
        } else {
            return DocumentHelper.getSimpleElementText(child);
        }
    }

    protected String getChildValue(NamespaceHelper helper, Element element, String childName) {
        Element child = helper.getFirstChild(element, childName);
        if (child == null) {
            throw new RuntimeException("The element <" + element.getNodeName()
                    + "> has no child element <" + childName + ">. Source URI: ["
                    + getRcmlSourceUri() + "[");
        }
        return DocumentHelper.getSimpleElementText(child);
    }

    /**
     * get all check in and check out
     * @return Vector of all check out and check in entries in this RCML-file
     * @throws RevisionControlException if an error occurs
     */
    public synchronized Vector getEntries() throws RevisionControlException {
        if (this.entries == null) {
            this.entries = new Vector();
            String uri = getRcmlSourceUri();
            try {
                if (SourceUtil.exists(uri, this.manager)) {
                    Document xml = SourceUtil.readDOM(uri, this.manager);
                    NamespaceHelper helper = new NamespaceHelper(NAMESPACE, "", xml);
                    Element parent = xml.getDocumentElement();
                    Element[] elements = helper.getChildren(parent);
                    for (int i = 0; i < elements.length; i++) {
                        RCMLEntry entry = getEntry(helper, elements[i]);
                        entries.add(entry);
                    }
                }
            } catch (Exception e) {
                throw new RevisionControlException(e);
            }
        }
        return this.entries;
    }

    /**
     * get all backup entries
     * @return Vector of all entries in this RCML-file with a backup
     * @throws Exception if an error occurs
     */
    public synchronized Vector getBackupEntries() throws Exception {
        Vector entries = getEntries();
        Vector backupEntries = new Vector();
        for (Iterator i = entries.iterator(); i.hasNext();) {
            RCMLEntry entry = (RCMLEntry) i.next();
            if (entry.getType() == RCML.ci && ((CheckInEntry) entry).hasBackup()) {
                backupEntries.add(entry);
            }
        }
        return backupEntries;
    }

    public synchronized void makeBackup(long time) throws RevisionControlException {
        makeBackup(this.contentSourceUri, time);
        makeBackup(this.metaSourceUri, time);
    }

    protected synchronized void makeBackup(String sourceUri, long time)
            throws RevisionControlException {
        String backupSourceUri = getBackupSourceUri(sourceUri, time);
        try {
            if (SourceUtil.exists(sourceUri, manager)) {
                SourceUtil.copy(this.manager, sourceUri, backupSourceUri);
            }
        } catch (Exception e) {
            throw new RevisionControlException(e);
        }
    }

    public synchronized void restoreBackup(Node node, long time) throws RevisionControlException {
        SourceNode sourceNode = (SourceNode) node;
        restoreBackup(sourceNode.getContentSource(), time);
        restoreBackup(sourceNode.getMetaSource(), time);
    }

    protected synchronized void restoreBackup(SourceWrapper wrapper, long time)
            throws RevisionControlException {
        String backupSourceUri = getBackupSourceUri(wrapper, time);
        SourceResolver resolver = null;
        try {
            resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
            SourceUtil.copy(resolver, backupSourceUri, wrapper.getOutputStream());
        } catch (Exception e) {
            throw new RevisionControlException(e);
        } finally {
            if (resolver != null) {
                this.manager.release(resolver);
            }
        }
    }

    protected String getBackupSourceUri(SourceWrapper wrapper, long time) {
        String uri = wrapper.getRealSourceUri();
        return getBackupSourceUri(uri, time);
    }

    protected String getBackupSourceUri(String uri, long time) {
        return uri + "." + time + ".bak";
    }

    /**
     * Prune the list of entries and delete the corresponding backups. Limit the number of entries
     * to the value maximalNumberOfEntries (2maxNumberOfRollbacks(configured)+1)
     * @throws RevisionControlException if an error occurs
     */
    public synchronized void pruneEntries() throws RevisionControlException {
        Vector entries = getEntries();
        RCMLEntry[] array = (RCMLEntry[]) entries.toArray(new RCMLEntry[entries.size()]);

        for (int i = this.maximalNumberOfEntries; i < entries.size(); i++) {
            // remove the backup file associated with this entry
            RCMLEntry entry = array[i];
            if (entry.getType() == ci && ((CheckInEntry) entry).hasBackup()) {
                long time = entry.getTime();
                deleteBackup(this.contentSourceUri, time);
                deleteBackup(this.metaSourceUri, time);
            }
            this.entries.remove(entry);
        }
        setDirty();
    }

    protected synchronized void deleteBackup(String sourceUri, long time)
            throws RevisionControlException {
        String uri = getBackupSourceUri(sourceUri, time);
        try {
            SourceUtil.delete(uri, this.manager);
            SourceUtil.deleteEmptyCollections(uri, this.manager);
        } catch (Exception e) {
            throw new RevisionControlException(e);
        }
    }

    /**
     * Check if the document is dirty
     * @return boolean dirty
     */
    public boolean isDirty() {
        return this.dirty;
    }

    /**
     * Set the value dirty to true
     */
    protected void setDirty() {
        this.dirty = true;
    }

    /**
     * Set the value dirty to false
     */
    protected void clearDirty() {
        this.dirty = false;
    }

    /**
     * get the time's value of the backups
     * @return String[] the times
     * @throws Exception if an error occurs
     */
    public String[] getBackupsTime() throws Exception {

        Vector entries = getEntries();
        List times = new ArrayList();
        for (Iterator i = entries.iterator(); i.hasNext();) {
            RCMLEntry entry = (RCMLEntry) i.next();
            if (entry.getType() == ci && ((CheckInEntry) entry).hasBackup()) {
                times.add(Long.toString(entry.getTime()));
            }
        }
        return (String[]) times.toArray(new String[times.size()]);

    }

    /**
     * Delete the revisions, the RCML source and the collection if the latter is empty.
     * @return boolean true, if the file was deleted
     */
    public synchronized boolean delete() {
        try {
            deleteRevisions();
            SourceUtil.delete(getRcmlSourceUri(), this.manager);
            SourceUtil.deleteEmptyCollections(getRcmlSourceUri(), this.manager);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    /**
     * delete the revisions
     * @throws RevisionControlException when somthing went wrong
     */
    public synchronized void deleteRevisions() throws RevisionControlException {
        try {
            String[] times = getBackupsTime();
            for (int i = 0; i < times.length; i++) {
                long time = new Long(times[i]).longValue();
                deleteBackup(this.contentSourceUri, time);
                deleteBackup(this.metaSourceUri, time);
            }
            this.entries.clear();
        } catch (Exception e) {
            throw new RevisionControlException(e);
        }
    }

    public synchronized void copyFrom(Node node, Node otherNode) throws RevisionControlException {

        SourceNode otherSourceNode = (SourceNode) otherNode;
        SourceNode sourceNode = (SourceNode) node;
        SourceNodeRCML otherRcml = (SourceNodeRCML) ((SourceNode) otherNode).getRcml();

        try {

            Vector backupEntries = otherRcml.getBackupEntries();
            for (Iterator i = backupEntries.iterator(); i.hasNext();) {
                RCMLEntry entry = (RCMLEntry) i.next();
                long time = entry.getTime();
                String otherContentUri = otherRcml.getBackupSourceUri(otherSourceNode
                        .getContentSource(), time);
                String thisContentUri = this
                        .getBackupSourceUri(sourceNode.getContentSource(), time);
                SourceUtil.copy(this.manager, otherContentUri, thisContentUri);

                String otherMetaUri = otherRcml.getBackupSourceUri(otherSourceNode.getMetaSource(),
                        time);
                String thisMetaUri = this.getBackupSourceUri(sourceNode.getMetaSource(), time);
                SourceUtil.copy(this.manager, otherMetaUri, thisMetaUri);
            }

            this.entries = new Vector();
            Vector otherEntries = otherRcml.getEntries();
            for (Iterator i = otherEntries.iterator(); i.hasNext();) {
                RCMLEntry entry = (RCMLEntry) i.next();
                RCMLEntry newEntry = null;
                switch (entry.getType()) {
                case co:
                    newEntry = new CheckOutEntry(entry.getSessionId(), entry.getIdentity(), entry
                            .getTime());
                    break;
                case ci:
                    CheckInEntry ciEntry = (CheckInEntry) entry;
                    newEntry = new CheckInEntry(ciEntry.getSessionId(), ciEntry.getIdentity(),
                            ciEntry.getTime(), ciEntry.getVersion(), ciEntry.hasBackup());
                    break;
                }
                this.entries.add(newEntry);
            }

            write();
        } catch (Exception e) {
            throw new RevisionControlException(e);
        }
    }

    public synchronized boolean isCheckedOut() throws RevisionControlException {
        RCMLEntry entry = getLatestEntry();
        return entry != null && entry.getType() == RCML.co;
    }

    public synchronized void checkIn(Node node, boolean backup, boolean newVersion)
            throws RevisionControlException {
        long time = new Date().getTime();

        if (backup) {
            makeBackup(time);
        }

        checkOutIn(node, RCML.ci, time, backup, newVersion, false);
        pruneEntries();
        write();
    }

    public synchronized void checkOut(Node node) throws RevisionControlException {
        checkOut(node, true);
    }

    public synchronized void checkOut(Node node, boolean restrictedToSession)
            throws RevisionControlException {
        checkOutIn(node, RCML.co, new Date().getTime(), false, false, restrictedToSession);
        write();
    }

    public boolean isCheckedOutBySession(Session session) throws RevisionControlException {
        Vector entries = getEntries();
        if (entries.size() > 0) {
            RCMLEntry entry = (RCMLEntry) entries.get(0);
            String otherSessionId = entry.getSessionId();
            if (entry.getType() == co) {
                // not restricted to session
                if (otherSessionId.equals(ALL_SESSIONS)) {
                    String otherUserId = entry.getIdentity();
                    String userId = session.getIdentity().getUser().getId();
                    return userId.equals(otherUserId);
                }
                // restricted to session
                if (otherSessionId.equals(session.getId())) {
                    return true;
                }
            }
        }
        return false;
    }
   
}
TOP

Related Classes of org.apache.lenya.cms.repository.SourceNodeRCML

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.