All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.cq.social.activitystreams.listener.api.AbstractActivityStreamProvider Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2014 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.cq.social.activitystreams.listener.api;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.References;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.jcr.resource.JcrResourceConstants;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.cq.social.activitystreams.api.SocialActivityManager;
import com.adobe.cq.social.activitystreams.impl.Activator;
import com.adobe.cq.social.activitystreams.listener.impl.ComparableStreamProviderExtension;
import com.adobe.cq.social.scf.core.SocialEvent;
import com.adobe.granite.activitystreams.Activity;
import com.adobe.granite.activitystreams.ActivityException;
import com.adobe.granite.activitystreams.ActivityStream;

/**
 * Base class of the ActivityStreamProvider.
 */
@Component(componentAbstract = true, label = "%abstractactivitystreamprovider.name")
@References({
    @Reference(name = "extensionProvider", referenceInterface = ActivityStreamProviderExtension.class,
            bind = "bindExtension", unbind = "unbindExtension", cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
            policy = ReferencePolicy.DYNAMIC),
    @Reference(name = "listenerExtension", referenceInterface = EventListenerExtension.class,
            bind = "bindListenerExtension", unbind = "unbindListenerExtension",
            cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)})
public abstract class AbstractActivityStreamProvider implements ActivityStreamProvider {
    protected static final String UGC_WRITER = "ugc-writer";

    /**
     * Eventually remove SLING_SERVICE_SUBSERVICE once we can update the dependencies to get this from
     * ResourceResolverFactory and not make the unit tests fail.
     */
    protected static final String SLING_SERVICE_SUBSERVICE = "sling.service.subservice";

    /**
     * The logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractActivityStreamProvider.class);

    /**
     * Reference to ResourceResolverFactory. Can't remove this it is API, but internally it is not used.
     */
    @Reference
    protected ResourceResolverFactory resourceResolverFactory;
    @Reference
    protected SocialActivityManager activityManager;

    @Reference
    protected final SlingRepository repository = null;

    String servicePid;

    /** The context. */
    private volatile ComponentContext context;

    /** The bound extension. */
    private final List boundExtensions =
        new LinkedList();

    /**
     * The list of extensions
     */
    private final CopyOnWriteArrayList extensions =
        new CopyOnWriteArrayList();
    /**
     * Because this is an abstract component we can't just add @Reference without bumping the package API major
     * version. So we avoid using Declarative Services and just use the next layer down directly.
     */
    private com.adobe.cq.social.serviceusers.internal.ServiceUserWrapper serviceUserWrapper;
    /**
     * Using this one lets us configure the service user with the symbolic name of *this* bundle, not the symbolic
     * name of each bundle implementing a concrete version of this component.
     */
    private ResourceResolverFactory privateResourceResolverFactory;

    public com.adobe.cq.social.serviceusers.internal.ServiceUserWrapper getServiceUserWrapper() {
        if (serviceUserWrapper == null) {
            serviceUserWrapper =
                Activator.getService(com.adobe.cq.social.serviceusers.internal.ServiceUserWrapper.class);
        }
        return serviceUserWrapper;
    }

    public ResourceResolverFactory getPrivateResourceResolverFactory() {
        if (privateResourceResolverFactory == null) {
            privateResourceResolverFactory = Activator.getService(ResourceResolverFactory.class);
        }
        return privateResourceResolverFactory;
    }

    @Override
    public void append(final Activity activity) throws ActivityException {

        if (activityManager == null) {
            return;
        }
        ResourceResolver ugcResolver = null;
        try {
            ugcResolver =
                getServiceUserWrapper().getServiceResourceResolver(getPrivateResourceResolverFactory(),
                    Collections.singletonMap(SLING_SERVICE_SUBSERVICE, (Object) UGC_WRITER));
            final ActivityStream stream = getStream(activity, ugcResolver);
            if (stream != null) {
                stream.append(activity);
            }
        } catch (final LoginException le) {
            LOGGER.error("Can't obtain resolver for service user", le);
            return;
        } finally {
            if (ugcResolver != null && ugcResolver.isLive()) {
                ugcResolver.close();
            }
        }
    }

    protected abstract ActivityStream getStream(Activity activity, ResourceResolver resolver)
        throws ActivityException;

    /**
     * Get the session to be used by the stream
     * @return Session the admin session or null if there is an error.
     * @throws RepositoryException RepositoryException
     */
    protected abstract Session getAdminSession() throws RepositoryException;

    /**
     * Activate this service. Subclass component needs to call this method in its activate method.
     * @param ctx ComponentContext
     * @throws LoginException LoginException
     */
    protected void activate(final ComponentContext ctx) throws LoginException {
        LOGGER.debug("Activating " + this.getClass().getName());
        servicePid = (String) ctx.getProperties().get("service.pid");
        this.context = ctx;
        final List extensionsCopy;
        synchronized (this.boundExtensions) {
            extensionsCopy = new ArrayList(this.boundExtensions);
            this.boundExtensions.clear();
        }
        for (final ActivityStreamProviderExtension extension : extensionsCopy) {
            this.addExtension(extension);
        }
    }

    /**
     * Deactivate this service. Subclass component needs to call this method in its deactivate method.
     * @param context OSGi component context
     */
    protected void deactivate(final ComponentContext context) {
        LOGGER.debug("Deactivating " + this.getClass().getName());
        activityManager = null;
        this.context = null;
    }

    protected ResourceResolver getServiceResourceResolver(final Session session) throws LoginException {
        final Map authInfo = new HashMap();
        authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, session);
        return resourceResolverFactory.getResourceResolver(authInfo);
    }

    public void bindExtension(final ActivityStreamProviderExtension extension) {
        LOGGER.debug("Binding : {} activated: {}", extension.getName(), (context == null));
        if (this.context == null) {
            synchronized (this.boundExtensions) {
                if (this.context == null) {
                    this.boundExtensions.add(extension);
                } else {
                    addExtension(extension);
                }
            }
        } else {
            addExtension(extension);
        }

    }

    private synchronized void addExtension(final ActivityStreamProviderExtension extension) {
        if (!hasExtension(extension) && validateExtension(extension)) {
            final int ranking = extension.getRanking();
            final ComparableStreamProviderExtension cextension =
                new ComparableStreamProviderExtension(extension, ranking);
            final int index = Collections.binarySearch(extensions, cextension);
            final int insertAt;
            if (index < 0) {
                insertAt = -(index + 1);
            } else {
                // this should never happen in our use case since we don't have duplicate item in the list.
                insertAt = index + 1;
            }

            extensions.add(insertAt, cextension);
        }
    }

    public void bindListenerExtension(final EventListenerExtension extension) {
        LOGGER.debug("Binding : {} activated:{}", extension.getName(), (this.context == null));
        if (this.context == null) {
            synchronized (this.boundExtensions) {
                if (this.context == null) {
                    this.boundExtensions.add(new ActivityStreamProviderExtensionWrapper(extension));
                } else {
                    addExtension(new ActivityStreamProviderExtensionWrapper(extension));
                }
            }
        } else {
            addExtension(new ActivityStreamProviderExtensionWrapper(extension));
        }
    }

    public void unbindListenerExtension(final EventListenerExtension extension) {
        LOGGER.debug("Unbinding {} activated:{}", extension.getName(), (this.context == null));
        unbindExtension(extension.getName());
    }

    private boolean validateExtension(final ActivityStreamProviderExtension extension) {
        String streams[] = extension.getStreamProviderPid();

        if (streams.length == 1 && StringUtils.equals(streams[0], "*")) {
            return true;
        }
        for (String name : streams) {
            if (StringUtils.equals(name, this.servicePid)) {
                return true;
            }
        }
        return false;
    }

    private boolean hasExtension(final ActivityStreamProviderExtension extension) {
        for (ActivityStreamProviderExtension e : extensions) {
            if (extension.getName().equals(e.getName())) {
                return true;
            }
        }
        return false;
    }

    public void unbindExtension(final ActivityStreamProviderExtension extension) {
        LOGGER.debug("Unbind extension {} activated {}", extension.getName(), (context == null));
        unbindExtension(extension.getName());
    }

    private synchronized void unbindExtension(final String name) {
        int index = 0;
        for (final ActivityStreamProviderExtension ext : extensions) {
            if (ext.getName().equals(name)) {
                extensions.remove(index);
                break;
            } else {
                index++;
            }
        }
    }

    @Override
    public List getExtensions() {
        return new CopyOnWriteArrayList(extensions);
    }

    /**
     * Wrapping old EventListenerExtension to support existing implementation.
     * @author thuynh
     */
    @SuppressWarnings("deprecation")
    protected class ActivityStreamProviderExtensionWrapper implements ActivityStreamProviderExtension {
        private EventListenerExtension eventListenerExtension;
        private final String[] STREAM_PROVIDER_PID = {"*"};

        public ActivityStreamProviderExtensionWrapper(final EventListenerExtension eventListenerExtension) {
            this.eventListenerExtension = eventListenerExtension;
        }

        @Override
        public String getName() {
            return eventListenerExtension.getName();
        }

        @Override
        public String[] getStreamProviderPid() {
            return STREAM_PROVIDER_PID;
        }

        @Override
        public boolean evaluate(SocialEvent event, Resource resource) {
            return eventListenerExtension.evaluate(event, resource);
        }

        @Override
        public int getRanking() {
            return eventListenerExtension.getRanking();
        }

        @Override
        public Map getActivityProperties(SocialEvent event, Resource resource) {
            return eventListenerExtension.getActivityProperties(event, resource);
        }

        @Override
        public Map getObjectProperties(SocialEvent event, Resource resource) {
            return eventListenerExtension.getObjectProperties(event, resource);
        }

        @Override
        public Map getActorProperties(SocialEvent event, Resource resource) {
            return eventListenerExtension.getActorProperties(event, resource);
        }

        @Override
        public Map getTargetProperties(SocialEvent event, Resource resource) {
            return eventListenerExtension.getTargetProperties(event, resource);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy