Package org.apache.sling.event.dea.impl

Source Code of org.apache.sling.event.dea.impl.DistributedEventSender

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

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.sling.api.SlingConstants;
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.api.resource.ValueMap;
import org.apache.sling.event.dea.DEAConstants;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This event handler distributes events from other application nodes in
* the cluster on the current instance.
* <p>
* It's listening for resource added events in the resource tree storing the
* distributed events. If a new resource is added, the resource is read,
* converted to an event and then send using the local event admin.
* <p>
*/
public class DistributedEventSender
    implements EventHandler {

    /** Default logger. */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /** Is the background task still running? */
    private volatile boolean running;

    /** A local queue for serializing the event processing. */
    private final BlockingQueue<String> queue = new LinkedBlockingQueue<String>();

    private final ResourceResolverFactory resourceResolverFactory;

    private final EventAdmin eventAdmin;

    private final String ownRootPathWithSlash;

    private volatile ServiceRegistration<?> serviceRegistration;

    public DistributedEventSender(final BundleContext bundleContext,
            final String rootPath,
            final String ownRootPath,
            final ResourceResolverFactory rrFactory,
            final EventAdmin eventAdmin) {
        this.eventAdmin = eventAdmin;
        this.resourceResolverFactory = rrFactory;
        this.ownRootPathWithSlash = ownRootPath + "/";

        this.running = true;
        final Thread backgroundThread = new Thread(new Runnable() {
            @Override
            public void run() {
                // create service registration properties
                final Dictionary<String, Object> props = new Hashtable<String, Object>();
                props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");

                // listen for all resource added OSGi events in the DEA area
                props.put(EventConstants.EVENT_TOPIC, SlingConstants.TOPIC_RESOURCE_ADDED);
                props.put(EventConstants.EVENT_FILTER, "(path=" + rootPath + "/*)");

                final ServiceRegistration<?> reg =
                        bundleContext.registerService(new String[] {EventHandler.class.getName()},
                        DistributedEventSender.this, props);

                DistributedEventSender.this.serviceRegistration = reg;

                try {
                    runInBackground();
                } catch (Throwable t) { //NOSONAR
                    logger.error("Background thread stopped with exception: " + t.getMessage(), t);
                    running = false;
                }
            }
        });
        backgroundThread.start();
    }

    /**
     * Deactivate this component.
     */
    public void stop() {
        if ( this.serviceRegistration != null ) {
            this.serviceRegistration.unregister();
            this.serviceRegistration = null;
        }
        // stop background threads by putting empty objects into the queue
        this.running = false;
        try {
            this.queue.put("");
        } catch (final InterruptedException e) {
            this.ignoreException(e);
            Thread.currentThread().interrupt();
        }
    }

    /**
     * Read an event from the resource
     * @return The event object or <code>null</code>
     */
    private Event readEvent(final Resource eventResource) {
        try {
            final ValueMap vm = ResourceHelper.getValueMap(eventResource);
            final String topic = vm.get(EventConstants.EVENT_TOPIC, String.class);
            if ( topic == null ) {
                // no topic should never happen as we check the resource type before
                logger.error("Unable to read distributed event from " + eventResource.getPath() + " : no topic property available.");
            } else {
                final Map<String, Object> properties = ResourceHelper.cloneValueMap(vm);
                // only send event if there are no read errors, otherwise discard it
                @SuppressWarnings("unchecked")
                final List<Exception> readErrorList = (List<Exception>) properties.remove(ResourceHelper.PROPERTY_MARKER_READ_ERROR_LIST);
                if ( readErrorList == null ) {
                    properties.remove(EventConstants.EVENT_TOPIC);
                    properties.remove(DEAConstants.PROPERTY_DISTRIBUTE);
                    final Object oldRT = properties.remove("event.dea." + ResourceResolver.PROPERTY_RESOURCE_TYPE);
                    if ( oldRT != null ) {
                        properties.put(ResourceResolver.PROPERTY_RESOURCE_TYPE, oldRT);
                    } else {
                        properties.remove(ResourceResolver.PROPERTY_RESOURCE_TYPE);
                    }
                    try {
                        final Event event = new Event(topic, properties);
                        return event;
                    } catch (final IllegalArgumentException iae) {
                        // this exception occurs if the topic is not correct (it should never happen,
                        // but you never know)
                        logger.error("Unable to read event: " + iae.getMessage(), iae);
                    }
                } else {
                    for(final Exception e : readErrorList) {
                        logger.warn("Unable to read distributed event from " + eventResource.getPath(), e);
                    }
                }
            }
        } catch (final InstantiationException ie) {
            // something happened with the resource in the meantime
            this.ignoreException(ie);
        }
        return null;
    }

    /**
     * Background thread
     */
    private void runInBackground() {
        while ( this.running ) {
            // so let's wait/get the next event from the queue
            String path = null;
            try {
                path = this.queue.take();
            } catch (final InterruptedException e) {
                this.ignoreException(e);
                Thread.currentThread().interrupt();
                this.running = false;
            }
            if ( path != null && path.length() > 0 && this.running ) {
                ResourceResolver resolver = null;
                try {
                    resolver = this.resourceResolverFactory.getAdministrativeResourceResolver(null);
                    final Resource eventResource = resolver.getResource(path);
                    if ( DistributedEventAdminImpl.RESOURCE_TYPE_EVENT.equals(eventResource.getResourceType())) {
                        final Event e = this.readEvent(eventResource);
                        if ( e != null ) {
                            // we check event admin as processing is async
                            final EventAdmin localEA = this.eventAdmin;
                            if ( localEA != null ) {
                                localEA.postEvent(e);
                            } else {
                                this.logger.error("Unable to post event as no event admin is available.");
                            }
                        }
                    }
                } catch (final LoginException ex) {
                    this.logger.error("Exception during creation of resource resolver.", ex);
                } finally {
                    if ( resolver != null ) {
                        resolver.close();
                    }
                }
            }
        }
    }

    /**
     * @see org.osgi.service.event.EventHandler#handleEvent(org.osgi.service.event.Event)
     */
    @Override
    public void handleEvent(final Event event) {
        final String path = (String) event.getProperty(SlingConstants.PROPERTY_PATH);
        if ( !path.startsWith(this.ownRootPathWithSlash) ) {
            try {
                this.queue.put(path);
            } catch (final InterruptedException ex) {
                this.ignoreException(ex);
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * Helper method which just logs the exception in debug mode.
     * @param e
     */
    private void ignoreException(final Exception e) {
        if ( this.logger.isDebugEnabled() ) {
            this.logger.debug("Ignored exception " + e.getMessage(), e);
        }
    }
}
TOP

Related Classes of org.apache.sling.event.dea.impl.DistributedEventSender

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.