org.apache.sling.superimposing.impl.SuperimposingResourceProviderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sling.superimposing.impl.SuperimposingResourceProviderImpl.java

Source

/*
 * 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.superimposing.impl;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Iterator;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceWrapper;
import org.apache.sling.superimposing.SuperimposingResourceProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Superimposing resource provider.
 * Maps a single source path to the target root path, with or without overlay depending on configuration.
 */
public class SuperimposingResourceProviderImpl implements SuperimposingResourceProvider {

    private static final Logger log = LoggerFactory.getLogger(SuperimposingResourceProviderImpl.class);

    private final String rootPath;
    private final String rootPrefix;
    private final String sourcePath;
    private final String sourcePathPrefix;
    private final boolean overlayable;
    private final String toString;
    private ServiceRegistration registration;

    SuperimposingResourceProviderImpl(String rootPath, String sourcePath, boolean overlayable) {
        this.rootPath = rootPath;
        this.rootPrefix = rootPath.concat("/");
        this.sourcePath = sourcePath;
        this.sourcePathPrefix = sourcePath.concat("/");
        this.overlayable = overlayable;
        StringBuilder sb = new StringBuilder(getClass().getSimpleName());
        sb.append(" [path=").append(rootPath).append(", ");
        sb.append("sourcePath=").append(sourcePath).append(", ");
        sb.append("overlayable=").append(overlayable).append("]");
        this.toString = sb.toString();
    }

    /**
     * {@inheritDoc}
     */
    public Resource getResource(ResourceResolver resolver, HttpServletRequest httpServletRequest, String path) {
        return getResource(resolver, path);
    }

    /**
     * {@inheritDoc}
     */
    public Resource getResource(ResourceResolver resolver, String path) {
        final String mappedPath = mapPath(this, resolver, path);
        if (null != mappedPath) {
            // the existing resource where the superimposed content is retrieved from
            final Resource mappedResource = resolver.getResource(mappedPath);
            if (null != mappedResource) {
                return new SuperimposingResource(mappedResource, path);
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Iterator<Resource> listChildren(Resource resource) {

        // unwrap resource if it is a wrapped resource
        final Resource currentResource;
        if (resource instanceof ResourceWrapper) {
            currentResource = ((ResourceWrapper) resource).getResource();
        } else {
            currentResource = resource;
        }

        // delegate resource listing to resource resolver
        if (currentResource instanceof SuperimposingResource) {
            final SuperimposingResource res = (SuperimposingResource) currentResource;
            final ResourceResolver resolver = res.getResource().getResourceResolver();
            final Iterator<Resource> children = resolver.listChildren(res.getResource());
            return new SuperimposingResourceIterator(this, children);
        }
        return null;
    }

    /**
     * Maps a path below the superimposing root to the target resource's path.
     * @param provider Superimposing resource provider
     * @param resolver Resource resolver
     * @param path Path to map
     * @return Mapped path or null if no mapping available
     */
    static String mapPath(SuperimposingResourceProviderImpl provider, ResourceResolver resolver, String path) {
        if (provider.overlayable) {
            return mapPathWithOverlay(provider, resolver, path);
        } else {
            return mapPathWithoutOverlay(provider, resolver, path);
        }
    }

    /**
     * Maps a path below the superimposing root to the target resource's path with check for overlaying.
     * @param provider Superimposing resource provider
     * @param resolver Resource resolver
     * @param path Path to map
     * @return Mapped path or null if no mapping available
     */
    static String mapPathWithOverlay(SuperimposingResourceProviderImpl provider, ResourceResolver resolver,
            String path) {
        if (StringUtils.equals(path, provider.rootPath)) {
            // Superimposing root path cannot be overlayed
            return mapPathWithoutOverlay(provider, resolver, path);
        } else if (StringUtils.startsWith(path, provider.rootPrefix)) {
            if (hasOverlayResource(resolver, path)) {
                // overlay item exists, allow underlying resource provider to step in
                return null;
            } else {
                // overlay item does not exist, overlay cannot be applied, fallback to mapped path without overlay
                return mapPathWithoutOverlay(provider, resolver, path);
            }
        }
        return null;
    }

    static boolean hasOverlayResource(ResourceResolver resolver, String path) {
        // check for overlay resource by checking directly in underlying JCR
        final Session session = resolver.adaptTo(Session.class);
        try {
            return (null != session && session.itemExists(path));
        } catch (RepositoryException e) {
            log.error("Error accessing the repository. ", e);
        }
        return false;
    }

    /**
     * Maps a path below the superimposing root to the target resource's path without check for overlaying.
     * @param provider Superimposing resource provider
     * @param resolver Resource resolver
     * @param path Path to map
     * @return Mapped path or null if no mapping available
     */
    static String mapPathWithoutOverlay(SuperimposingResourceProviderImpl provider, ResourceResolver resolver,
            String path) {
        final String mappedPath;
        if (StringUtils.equals(path, provider.rootPath)) {
            mappedPath = provider.sourcePath;
        } else if (StringUtils.startsWith(path, provider.rootPrefix)) {
            mappedPath = StringUtils.replaceOnce(path, provider.rootPrefix, provider.sourcePathPrefix);
        } else {
            mappedPath = null;
        }
        return mappedPath;
    }

    /**
     * Maps a path below the target resource to the superimposed resource's path.
     *
     * @param provider
     * @param path
     * @return
     */
    static String reverseMapPath(SuperimposingResourceProviderImpl provider, String path) {
        final String mappedPath;
        if (path.startsWith(provider.sourcePathPrefix)) {
            mappedPath = StringUtils.replaceOnce(path, provider.sourcePathPrefix, provider.rootPrefix);
        } else if (path.equals(provider.sourcePath)) {
            mappedPath = provider.rootPath;
        } else {
            mappedPath = null;
        }
        return mappedPath;
    }

    //---------- Service Registration

    void registerService(BundleContext context) {
        final Dictionary<String, Object> props = new Hashtable<String, Object>();
        props.put(Constants.SERVICE_DESCRIPTION, "Provider of superimposed resources");
        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
        props.put(ROOTS, new String[] { rootPath });

        registration = context.registerService(SERVICE_NAME, this, props);

        log.info("Registered {}", this);
    }

    void unregisterService() {
        if (registration != null) {
            registration.unregister();
            registration = null;
            log.info("Unregistered {}", this);
        }
    }

    /**
     * @return Root path (destination for superimposing)
     */
    public String getRootPath() {
        return rootPath;
    }

    /**
     * @return Source path (source for superimposing)
     */
    public String getSourcePath() {
        return sourcePath;
    }

    /**
     * @return Overlayable yes/no
     */
    public boolean isOverlayable() {
        return overlayable;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof SuperimposingResourceProviderImpl) {
            final SuperimposingResourceProviderImpl srp = (SuperimposingResourceProviderImpl) o;
            return this.rootPath.equals(srp.rootPath) && this.sourcePath.equals(srp.sourcePath)
                    && this.overlayable == srp.overlayable;

        }
        return false;
    }

    @Override
    public String toString() {
        return toString;
    }
}