Package org.apache.sling.jcr.prefs.impl

Source Code of org.apache.sling.jcr.prefs.impl.JcrBackingStore

/*
* 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.
*/
package org.apache.sling.jcr.prefs.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jcr.NamespaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.felix.prefs.BackingStore;
import org.apache.felix.prefs.BackingStoreManager;
import org.apache.felix.prefs.PreferencesDescription;
import org.apache.felix.prefs.PreferencesImpl;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.prefs.BackingStoreException;

@Component
@Service(value=BackingStore.class)
@Properties({
    @org.apache.felix.scr.annotations.Property(name="sling.preferences.jcr.path", value="/preferences"),
    @org.apache.felix.scr.annotations.Property(name="sling.preferences.jcr.namespace",value="http://osgi.org/service/prefs/Preferences/1.1"),
    @org.apache.felix.scr.annotations.Property(name="sling.preferences.jcr.prefix", value="osgipref")
})
public class JcrBackingStore implements BackingStore {

    protected static final String NS_URI = "http://osgi.org/service/prefs/Preferences/1.1";

    protected static final String PATH_PROPERTY = "sling.preferences.jcr.path";
    protected static final String NAMESPACE_PROPERTY = "sling.preferences.jcr.namespace";
    protected static final String NAMESPACE_PREFIX_PROPERTY = "sling.preferences.jcr.prefix";

    @Reference
    protected SlingRepository repository;

    protected boolean initialized = false;

    protected String rootNodePath = "/preferences";

    protected String namespacePrefix = "osgipref";

    protected String namespacePrefixSep = this.namespacePrefix + ':';

    protected String namespace = NS_URI;

    protected Session getSession() throws RepositoryException {
        return this.repository.loginAdministrative(null);
    }

    /**
     * Activate this component and get the property configuration.
     * @param componentContext
     */
    protected void activate(ComponentContext componentContext) {
        Object value = componentContext.getProperties().get(PATH_PROPERTY);
        if ( value != null ) {
            this.rootNodePath = value.toString();
        }
        value = componentContext.getProperties().get(NAMESPACE_PROPERTY);
        if ( value != null ) {
            this.namespace = value.toString();
        }
        value = componentContext.getProperties().get(NAMESPACE_PREFIX_PROPERTY);
        if ( value != null ) {
            this.namespacePrefix = value.toString();
            this.namespacePrefixSep = this.namespacePrefix + ':';
        }
    }

    /**
     * Check if the repository is already initialized and return a fresh user session
     * This method should be called by every service method before anything else.
     */
    protected Session checkInitialized() {
        if ( !this.initialized ) {
            this.initRepository();
        }
        try {
            return this.repository.loginAdministrative(null);
        } catch (RepositoryException e) {
            throw new RuntimeException("Unable to login and create a new session.", e);
        }
    }

    protected void createNodePath(Node parent, String relPath) throws RepositoryException{
        int pos = relPath.indexOf('/');
        String nodeName = (pos == -1 ? relPath : relPath.substring(0, pos));
        if ( !parent.hasNode(nodeName) ) {
            parent.addNode(nodeName, "nt:unstructured");
        }
        if ( pos != -1 ) {
            this.createNodePath(parent.getNode(nodeName), relPath.substring(pos+1));
        }
    }

    /**
     * Initialize the repository:
     * - register this component as an event listener
     * - create the root node
     */
    protected synchronized void initRepository() {
        if ( !this.initialized ) {
            Session session = null;
            try {
                session = this.repository.loginAdministrative(null);
                if ( !session.itemExists(this.rootNodePath) ) {
                    this.createNodePath(session.getRootNode(), this.rootNodePath.substring(1));
                    session.save();
                }
                try {
                    String prefix = session.getWorkspace().getNamespaceRegistry().getPrefix(this.namespace);
                    this.namespacePrefix = prefix;
                    this.namespacePrefixSep = prefix + ':';
                } catch (NamespaceException ne) {
                    // mapping does not exist
                    session.getWorkspace().getNamespaceRegistry().registerNamespace(this.namespacePrefix, this.namespace);
                }
            } catch (RepositoryException e) {
                throw new RuntimeException("Unable to get root node or to create new root node.", e);
            } finally {
                if ( session != null ) {
                    session.logout();
                }
            }
            this.initialized = true;
        }
    }

    /**
     * Get the node path for the preferences tree.
     * @param desc
     * @return
     */
    protected String getNodePath(PreferencesImpl prefs) {
        final PreferencesDescription desc = prefs.getDescription();
        final StringBuilder buffer = new StringBuilder(this.rootNodePath);
        buffer.append('/');
        buffer.append(desc.getBundleId());
        buffer.append('/');
        if ( desc.getIdentifier() != null ) {
            buffer.append("users");
            buffer.append('/');
            buffer.append(desc.getIdentifier());
        } else {
            buffer.append("system");
        }
        buffer.append('/');
        buffer.append("preferences");
        final String absPath = prefs.absolutePath();
        if ( absPath.length() > 1 ) {
            buffer.append(absPath);
        }
        return buffer.toString();
    }

    /**
     * Get the node path for the preferences tree.
     * @param desc
     * @return
     */
    protected String getNodePath(PreferencesDescription desc) {
        final StringBuilder buffer = new StringBuilder(this.rootNodePath);
        buffer.append('/');
        buffer.append(desc.getBundleId());
        buffer.append('/');
        if ( desc.getIdentifier() != null ) {
            buffer.append("users");
            buffer.append('/');
            buffer.append(desc.getIdentifier());
        } else {
            buffer.append("system");
        }
        buffer.append('/');
        buffer.append("preferences");
        return buffer.toString();
    }

    /**
     * Load this preferences from a node.
     */
    @SuppressWarnings("unchecked")
    protected void readPreferences(PreferencesImpl prefs, Session session, Node node) throws RepositoryException {
        final PropertyIterator iterator = node.getProperties();
        while ( iterator.hasNext() ) {
            final Property prop = iterator.nextProperty();
            if ( prop.getName().startsWith(this.namespacePrefixSep) ) {
                prefs.getProperties().put(prop.getName(), prop.getString());
            }
        }
    }

    /**
     * Save this preferences to a node.
     */
    @SuppressWarnings("unchecked")
    protected void writePreferences(PreferencesImpl prefs, Session session) throws RepositoryException {
        final String path = this.getNodePath(prefs);
        if ( !session.itemExists(path) ) {
            this.createNodePath(session.getRootNode(), path.substring(1));
        }

        final Node node = (Node)session.getItem(path);
        // process the change set
        final Map properties = prefs.getProperties();
        Iterator<String> i;

        // remove properties
        i = prefs.getChangeSet().getRemovedProperties().iterator();
        while ( i.hasNext() ) {
            final String name = i.next();
            if ( node.hasProperty(this.namespacePrefixSep + name) ) {
                node.getProperty(this.namespacePrefixSep + name).remove();
            }
        }
        // update properties
        i = prefs.getChangeSet().getChangedProperties().iterator();
        while ( i.hasNext() ) {
            final String name = i.next();
            node.setProperty(this.namespacePrefixSep + name, (String)properties.get(name));
        }
        // remove children
        i = prefs.getChangeSet().getRemovedChildren().iterator();
        while ( i.hasNext() ) {
            final String name = i.next();
            if ( node.hasNode(name) ) {
                node.getNode(name).remove();
            }
        }
        // adding children is done by writing the child preferences!

        session.save();
    }

    /**
     * Read the preferences from the repository.
     */
    protected void readTree(PreferencesImpl prefs, Session session, Node node)
    throws RepositoryException {
        this.readPreferences(prefs, session, node);
        final NodeIterator iterator = node.getNodes();
        while ( iterator.hasNext() ) {
            final Node current = iterator.nextNode();
            final PreferencesImpl impl = (PreferencesImpl)prefs.node(current.getName());
            this.readTree(impl, session, current);
        }
    }

    /**
     * @see org.apache.felix.prefs.BackingStore#availableBundles()
     */
    public Long[] availableBundles() {
        final Session session = this.checkInitialized();
        Long[] result = new Long[0];
        try {
            try {
                final List<Long> bundleIds = new ArrayList<Long>();
                final NodeIterator iterator = session.getRootNode().getNodes(this.rootNodePath);
                while ( iterator.hasNext() ) {
                    final Node current = iterator.nextNode();
                    try {
                        final Long id = Long.valueOf(current.getName());
                        bundleIds.add(id);
                    } catch (NumberFormatException nfe) {
                        // we ignore this as this just indicates a wrong node in the tree
                    }
                }
                result = bundleIds.toArray(new Long[bundleIds.size()]);
            } catch (RepositoryException e) {
                // we ignore this for now
            }
        } finally {
            session.logout();
        }
        return result;
    }

    /**
     * @see org.apache.felix.prefs.BackingStore#load(org.apache.felix.prefs.BackingStoreManager, org.apache.felix.prefs.PreferencesDescription)
     */
    public PreferencesImpl load(BackingStoreManager manager, PreferencesDescription desc) throws BackingStoreException {
        final Session session = this.checkInitialized();
        try {
            final String path = this.getNodePath(desc);
            if ( session.itemExists(path) ) {
                final PreferencesImpl root = new PreferencesImpl(desc, manager);
                this.readTree(root, session, (Node)session.getItem(path));

                return root;
            }
        } catch (RepositoryException re) {
            throw new BackingStoreException("Unable to load preferences.", re);
        } finally {
            session.logout();
        }
        return null;
    }

    /**
     * @see org.apache.felix.prefs.BackingStore#loadAll(org.apache.felix.prefs.BackingStoreManager, java.lang.Long)
     */
    public PreferencesImpl[] loadAll(BackingStoreManager manager, Long bundleId) throws BackingStoreException {
        final Session session = this.checkInitialized();
        try {
            final List<PreferencesImpl> list = new ArrayList<PreferencesImpl>();
            // check for system preferences
            final PreferencesDescription systemDesc = new PreferencesDescription(bundleId, null);
            final String systemPath = this.getNodePath(systemDesc);
            if ( session.itemExists(systemPath) ) {
                final Node rootNode = (Node)session.getItem(systemPath);
                final PreferencesImpl root = new PreferencesImpl(systemDesc, manager);
                this.readTree(root, session, rootNode);
            }
            // user preferences
            final String userPath = this.rootNodePath + '/' + bundleId + '/' + "users";
            if ( session.itemExists(userPath) ) {
                final NodeIterator iterator = ((Node)session.getItem(userPath)).getNodes();
                while ( iterator.hasNext() ) {
                    final Node current = iterator.nextNode();
                    final PreferencesDescription desc = new PreferencesDescription(bundleId, current.getName());
                    final PreferencesImpl root = new PreferencesImpl(desc, manager);

                    this.readTree(root, session, current);

                    list.add(root);
                }
            }
            return list.toArray(new PreferencesImpl[list.size()]);
        } catch (RepositoryException re) {
            throw new BackingStoreException("Unable to load preferences.", re);
        } finally {
            session.logout();
        }
    }

    /**
     * @see org.apache.felix.prefs.BackingStore#remove(java.lang.Long)
     */
    public void remove(Long bundleId) throws BackingStoreException {
        final Session session = this.checkInitialized();
        try {
            final String nodePath = this.rootNodePath + '/' + bundleId;
            if ( session.itemExists(nodePath) ) {
                final Node node = ((Node)session).getNode(nodePath);
                final Node parent = node.getParent();
                node.remove();
                parent.save();
            }
        } catch (RepositoryException re) {
            throw new BackingStoreException("Unable to remove preferences.", re);
        } finally {
            session.logout();
        }
    }

    /**
     * @see org.apache.felix.prefs.BackingStore#store(org.apache.felix.prefs.PreferencesImpl)
     */
    public void store(PreferencesImpl prefs) throws BackingStoreException {
        final Session session = this.checkInitialized();
        try {
            this.store(prefs, session);
        } catch (RepositoryException re) {
            throw new BackingStoreException("Unable to store preferences.", re);
        } finally {
            session.logout();
        }
    }

    protected void store(PreferencesImpl prefs, Session session)
    throws BackingStoreException, RepositoryException {
        // do we need to store at all?
        if ( prefs.getChangeSet().hasChanges() ) {
            // load existing data
            final PreferencesImpl savedData = this.load(prefs.getBackingStoreManager(), prefs.getDescription());
            if ( savedData != null ) {
                // merge with saved version
                final PreferencesImpl n = savedData.getOrCreateNode(prefs.absolutePath());
                n.applyChanges(prefs);
                prefs = n;
            }
            this.writePreferences(prefs, session);
        }
        // now process children
        @SuppressWarnings("unchecked")
        final Iterator<PreferencesImpl> i = prefs.getChildren().iterator();
        while ( i.hasNext() ) {
            final PreferencesImpl p = i.next();
            this.store(p, session);
        }

    }

    /**
     * @see org.apache.felix.prefs.BackingStore#update(org.apache.felix.prefs.PreferencesImpl)
     */
    public void update(PreferencesImpl prefs) throws BackingStoreException {
        final PreferencesImpl root = this.load(prefs.getBackingStoreManager(), prefs.getDescription());
        if ( root != null ) {
            // and now update
            if ( root.nodeExists(prefs.absolutePath()) ) {
                final PreferencesImpl updated = (PreferencesImpl)root.node(prefs.absolutePath());
                prefs.update(updated);
            }
        }
    }
}
TOP

Related Classes of org.apache.sling.jcr.prefs.impl.JcrBackingStore

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.