Package org.atmosphere.cpr

Source Code of org.atmosphere.cpr.DefaultBroadcaster$AsyncWriteToken

/*
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*/
package org.atmosphere.cpr;

import org.atmosphere.cpr.BroadcastFilter.BroadcastAction;
import org.atmosphere.cpr.BroadcasterConfig.DefaultBroadcasterCache;
import org.atmosphere.di.InjectorProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
* {@link Broadcaster} implementation.
* <p/>
* Broadcast messages to suspended response using the caller's Thread.
* This basic {@link Broadcaster} use an {@link java.util.concurrent.ExecutorService}
* to broadcast message, hence the broadcast operation is asynchronous. Make sure
* you block on {@link #broadcast(Object)}.get()} if you need synchronous operations.
*
* @author Jeanfrancois Arcand
*/
public class DefaultBroadcaster implements Broadcaster {

    private static final Logger logger = LoggerFactory.getLogger(DefaultBroadcaster.class);

    protected final ConcurrentLinkedQueue<AtmosphereResource<?, ?>> resources =
            new ConcurrentLinkedQueue<AtmosphereResource<?, ?>>();
    protected BroadcasterConfig bc;
    protected final BlockingQueue<Entry> messages = new LinkedBlockingQueue<Entry>();
    protected final BlockingQueue<AsyncWriteToken> asyncWriteQueue = new LinkedBlockingQueue<AsyncWriteToken>();

    protected final AtomicBoolean started = new AtomicBoolean(false);
    protected final AtomicBoolean destroyed = new AtomicBoolean(false);

    protected SCOPE scope = SCOPE.APPLICATION;
    protected String name = DefaultBroadcaster.class.getSimpleName();
    protected final ConcurrentLinkedQueue<Entry> delayedBroadcast = new ConcurrentLinkedQueue<Entry>();
    protected final ConcurrentLinkedQueue<Entry> broadcastOnResume = new ConcurrentLinkedQueue<Entry>();
    protected final ConcurrentLinkedQueue<BroadcasterLifeCyclePolicyListener> lifeCycleListeners = new ConcurrentLinkedQueue<BroadcasterLifeCyclePolicyListener>();

    protected Future<?> notifierFuture;
    protected Future<?> asyncWriteFuture;
    protected BroadcasterCache broadcasterCache;

    private POLICY policy = POLICY.FIFO;
    private long maxSuspendResource = -1;
    private final AtomicBoolean requestScoped = new AtomicBoolean(false);
    private BroadcasterLifeCyclePolicy lifeCyclePolicy = new BroadcasterLifeCyclePolicy.Builder()
            .policy(BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.NEVER).build();
    private Future<?> currentLifecycleTask;

    public DefaultBroadcaster() {
        this(DefaultBroadcaster.class.getSimpleName());
    }

    public DefaultBroadcaster(String name) {
        this.name = name;
        broadcasterCache = new DefaultBroadcasterCache();
        bc = new BroadcasterConfig(AtmosphereServlet.broadcasterFilters, null);
        setID(name);
    }

    /**
     * {@inheritDoc}
     */
    public void destroy() {
        started.set(false);
        destroyed.set(true);
        releaseExternalResources();
        if (notifierFuture != null) {
            notifierFuture.cancel(true);
        }

        if (asyncWriteFuture != null) {
            asyncWriteFuture.cancel(true);
        }

        if (bc != null) {
            bc.destroy();
        }

        if (broadcasterCache != null) {
            broadcasterCache.stop();
        }
        resources.clear();
        broadcastOnResume.clear();
        messages.clear();
        asyncWriteQueue.clear();
        delayedBroadcast.clear();
        broadcasterCache = null;

        if (BroadcasterFactory.getDefault() != null) {
            BroadcasterFactory.getDefault().remove(this, name);
        }

        if (currentLifecycleTask != null) {
            currentLifecycleTask.cancel(true);
        }
    }

    /**
     * {@inheritDoc}
     */
    public Collection<AtmosphereResource<?, ?>> getAtmosphereResources() {
        return Collections.unmodifiableCollection(resources);
    }

    /**
     * {@inheritDoc}
     */
    public void setScope(SCOPE scope) {
        this.scope = scope;
        if (scope != SCOPE.REQUEST) {
            return;
        }

        try {
            for (AtmosphereResource<?, ?> resource : resources) {
                Broadcaster b = BroadcasterFactory.getDefault()
                        .get(getClass(), getClass().getSimpleName() + "/" + UUID.randomUUID());

                if (DefaultBroadcaster.class.isAssignableFrom(this.getClass())) {
                    BroadcasterCache cache = bc.getBroadcasterCache().getClass().newInstance();
                    InjectorProvider.getInjector().inject(cache);
                    DefaultBroadcaster.class.cast(b).broadcasterCache = cache;
                }
                resource.setBroadcaster(b);
                if (resource.getAtmosphereResourceEvent().isSuspended()) {
                    b.addAtmosphereResource(resource);
                }
            }

            if (!resources.isEmpty()) {
                destroy();
            }
        }
        catch (Exception e) {
            logger.error("failed to set request scope for current resources", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public SCOPE getScope() {
        return scope;
    }

    /**
     * {@inheritDoc}
     */
    public void setID(String id) {
        if(id == null) {
            id = getClass().getSimpleName() + "/" + UUID.randomUUID();
        }
               
        Broadcaster b = BroadcasterFactory.getDefault().lookup(this.getClass(), id);
        if (b != null && b.getScope() == SCOPE.REQUEST) {
            throw new IllegalStateException("Broadcaster ID already assigned to SCOPE.REQUEST. Cannot change the id");
        }

        BroadcasterFactory.getDefault().remove(this, name);
        this.name = id;
        BroadcasterFactory.getDefault().add(this, name);
    }

    /**
     * {@inheritDoc}
     */
    public String getID() {
        return name;
    }

    /**
     * {@inheritDoc}
     */
    public void resumeAll() {
        for (AtmosphereResource<?, ?> r : resources) {
            r.resume();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void releaseExternalResources() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setBroadcasterLifeCyclePolicy(final BroadcasterLifeCyclePolicy lifeCyclePolicy) {
        this.lifeCyclePolicy = lifeCyclePolicy;
        if (currentLifecycleTask != null) {
            currentLifecycleTask.cancel(false);
        }

        if ( lifeCyclePolicy.getLifeCyclePolicy() == BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.IDLE
                || lifeCyclePolicy.getLifeCyclePolicy() == BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.IDLE_DESTROY ) {

            int time = lifeCyclePolicy.getTimeout();
            if (time == -1) {
                throw new IllegalStateException("BroadcasterLifeCyclePolicy time is not set");
            }

            final AtomicReference<Future<?>> ref = new AtomicReference<Future<?>>();
            currentLifecycleTask = bc.getScheduledExecutorService().scheduleAtFixedRate(new Runnable(){

                @Override
                public void run() {
                    try {
                        if (resources.isEmpty()) {
                            notifyEmptyListener();
                            notifyIdleListener();

                            if (lifeCyclePolicy.getLifeCyclePolicy() == BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.IDLE) {
                                releaseExternalResources();
                                logger.debug("Applying BroadcasterLifeCyclePolicy IDLE policy");
                            } else {
                                notifyDestroyListener();

                                destroy();
                                /**
                                 * The value may be null if the timeout is too low. Hopefully next execution will
                                 * cancel the task properly.
                                 */
                                if (ref.get() != null) {
                                    currentLifecycleTask.cancel(true);
                                }

                                logger.debug("Applying BroadcasterLifeCyclePolicy IDLE_DESTROY policy");
                            }
                        }
                    } catch (Throwable t) {
                        logger.warn("Scheduled BroadcasterLifeCyclePolicy exception", t);
                    }
                }

            }, time, time, lifeCyclePolicy.getTimeUnit());
            ref.set(currentLifecycleTask);
        }
    }

    @Override
    public void addBroadcasterLifeCyclePolicyListener(BroadcasterLifeCyclePolicyListener b) {
        lifeCycleListeners.add(b);
    }

    @Override
    public void removeBroadcasterLifeCyclePolicyListener(BroadcasterLifeCyclePolicyListener b) {
         lifeCycleListeners.remove(b);
    }

    public class Entry {

        public Object message;
        public Object multipleAtmoResources;
        public BroadcasterFuture<?> future;
        public boolean writeLocally;
        public Object originalMessage;

        public Entry(Object message, Object multipleAtmoResources, BroadcasterFuture<?> future, Object originalMessage) {
            this.message = message;
            this.multipleAtmoResources = multipleAtmoResources;
            this.future = future;
            this.writeLocally = true;
            this.originalMessage = originalMessage;
        }

        public Entry(Object message, Object multipleAtmoResources, BroadcasterFuture<?> future, boolean writeLocally) {
            this.message = message;
            this.multipleAtmoResources = multipleAtmoResources;
            this.future = future;
            this.writeLocally = writeLocally;
        }

        @Override
        public String toString() {
            return "Entry{" +
                    "message=" + message +
                    ", multipleAtmoResources=" + multipleAtmoResources +
                    ", future=" + future +
                    '}';
        }
    }

    protected Runnable getBroadcastHandler() {
        return new Runnable() {
            public void run() {
                Entry msg;
                while (started.get()) {
                    try {
                        msg = messages.take();
                        push(msg);
                    } catch (Throwable ex) {
                        if (!started.get()) {
                            logger.trace("failed to submit broadcast handler runnable to broadcast executor service on shutdown", ex);
                        } else {
                            logger.debug("failed to submit broadcast handler runnable to broadcast executor service", ex);
                        }
                    }
                }
            }
        };
    }

    protected void start() {
        if (!started.getAndSet(true)) {

            broadcasterCache = bc.getBroadcasterCache();
            broadcasterCache.start();

            notifierFuture = bc.getExecutorService().submit(getBroadcastHandler());
            asyncWriteFuture = bc.getAsyncWriteService().submit(getAsyncWriteHandler());
        }
    }

    protected void push(Entry entry) {

        if (destroyed.get()) {
            return;
        }

        String prevMessage = entry.message.toString();
        if (!delayedBroadcast.isEmpty()) {
            Iterator<Entry> i = delayedBroadcast.iterator();
            StringBuilder b = new StringBuilder();
            while (i.hasNext()) {
                Entry e = i.next();
                e.future.cancel(true);
                try {
                    // Append so we do a single flush
                    if (e.message instanceof String
                            && entry.message instanceof String) {
                        b.append(e.message);
                    } else {
                        push(e);
                    }
                } finally {
                    i.remove();
                }
            }

            if (b.length() > 0) {
                entry.message = b.append(entry.message).toString();
            }
        }

        if (resources.isEmpty()) {
            trackBroadcastMessage(null, entry.message);
            if (entry.future != null) {
                entry.future.done();
            }
            return;
        }

        Object finalMsg = translate(entry.message);
        entry.message = finalMsg;
        try {
            if (entry.multipleAtmoResources == null) {
                for (AtmosphereResource<?, ?> r : resources) {
                    finalMsg = perRequestFilter(r, entry);
                    if (entry.writeLocally) {
                        asyncWriteQueue.put(new AsyncWriteToken(r, finalMsg, entry.future));
                    }
                }
            } else if (entry.multipleAtmoResources instanceof AtmosphereResource<?, ?>) {
                finalMsg = perRequestFilter((AtmosphereResource<?, ?>) entry.multipleAtmoResources, entry);

                if (entry.writeLocally) {
                    asyncWriteQueue.put(new AsyncWriteToken((AtmosphereResource<?, ?>) entry.multipleAtmoResources, finalMsg, entry.future));
                }
            } else if (entry.multipleAtmoResources instanceof Set) {
                Set<AtmosphereResource<?, ?>> sub = (Set<AtmosphereResource<?, ?>>) entry.multipleAtmoResources;
                for (AtmosphereResource<?, ?> r : sub) {
                    finalMsg = perRequestFilter(r, entry);
                    if (entry.writeLocally) {
                        asyncWriteQueue.put(new AsyncWriteToken(r, finalMsg, entry.future));
                    }
                }
            }
            entry.message = prevMessage;
        } catch(InterruptedException ex) {
            logger.debug(ex.getMessage(), ex);
        }
    }

    protected Object perRequestFilter(AtmosphereResource<?, ?> r, Entry msg) {
        Object finalMsg = msg.message;

        if (AtmosphereResourceImpl.class.isAssignableFrom(r.getClass()))  {
            if (AtmosphereResourceImpl.class.cast(r).isInScope()) {
                if (r.getRequest() instanceof HttpServletRequest && bc.hasPerRequestFilters()) {
                    Object message = msg.originalMessage;
                    BroadcastAction a  = bc.filter( (HttpServletRequest) r.getRequest(), (HttpServletResponse) r.getResponse(), message);
                    if (a.action() == BroadcastAction.ACTION.ABORT
                            || a.message() != msg.originalMessage) {
                       finalMsg = a.message();
                    }
                }
                trackBroadcastMessage(r, finalMsg);
            } else {
                // The resource is no longer valid.
                removeAtmosphereResource(r);
                BroadcasterFactory.getDefault().removeAllAtmosphereResource(r);
            }

        }
        return finalMsg;
    }

    private Object translate(Object msg) {
        if (Callable.class.isAssignableFrom(msg.getClass())) {
            try {
                return  Callable.class.cast(msg).call();
            } catch (Exception e) {
                logger.error("failed to cast message: " + msg, e);
            }
        }
        return msg;
    }

    protected void executeAsyncWrite(final AtmosphereResource<?, ?> resource, final Object msg, final BroadcasterFuture future) {
        if (resource.getAtmosphereResourceEvent().isCancelled()) {
            return;
        }

        final AtmosphereResourceEvent event = resource.getAtmosphereResourceEvent();
        event.setMessage(msg);

        try {
            if (resource.getAtmosphereResourceEvent() != null && !resource.getAtmosphereResourceEvent().isCancelled()
                    && HttpServletRequest.class.isAssignableFrom(resource.getRequest().getClass())) {
                HttpServletRequest.class.cast(resource.getRequest())
                        .setAttribute(CometSupport.MAX_INACTIVE, System.currentTimeMillis());
            }
        } catch (Exception t) {
            // Shield us from any corrupted Request
            logger.debug("Preventing corruption of a recycled request: resource" + resource, event);
            removeAtmosphereResource(resource);
            BroadcasterFactory.getDefault().removeAllAtmosphereResource(resource);
            return;
        }

        broadcast(resource, event);
        if (resource instanceof AtmosphereEventLifecycle) {
            ((AtmosphereEventLifecycle) resource).notifyListeners();
        }

        if (future != null) {
            future.done();
        }
    }

    protected Runnable getAsyncWriteHandler() {
        return new Runnable() {
            public void run() {
                AsyncWriteToken token;
                try {
                    token = asyncWriteQueue.take();
                    // Leader/follower
                    synchronized(token.resource) {
                        bc.getAsyncWriteService().submit(this);
                        executeAsyncWrite(token.resource, token.msg, token.future);
                    }
                } catch (Throwable ex) {
                    if (!started.get()) {
                        logger.trace("failed to submit async write task on shutdown", ex);
                    } else {
                        logger.debug("failed to submit async write task", ex);
                    }
                }
            }
        };
    }

    protected void checkCachedAndPush(final AtmosphereResource<?, ?> r, final AtmosphereResourceEvent e) {
        retrieveTrackedBroadcast(r, e);
        if (e.getMessage() instanceof List && !((List) e.getMessage()).isEmpty()) {
            broadcast(r, e);
        }
    }

    protected boolean retrieveTrackedBroadcast(final AtmosphereResource<?, ?> r, final AtmosphereResourceEvent e) {
        List<?> missedMsg = broadcasterCache.retrieveFromCache(r);
        if (!missedMsg.isEmpty()) {
            e.setMessage(missedMsg);
            return true;
        }
        return false;
    }

    protected void trackBroadcastMessage(final AtmosphereResource<?, ?> r, Object msg) {
        broadcasterCache.addToCache(r, msg);
    }
                                                                                                            
    protected void broadcast(final AtmosphereResource<?, ?> r, final AtmosphereResourceEvent e) {
        try {
            r.getAtmosphereHandler().onStateChange(e);
        } catch (IOException ex) {
            if (AtmosphereResourceImpl.class.isAssignableFrom(r.getClass())) {
                AtmosphereResourceImpl.class.cast(r).notifyListeners(e);
            }
            onException(ex, r);
        } catch (RuntimeException ex) {
            if (AtmosphereResourceImpl.class.isAssignableFrom(r.getClass())) {
                AtmosphereResourceImpl.class.cast(r).notifyListeners(e);
            }
            onException(ex, r);
        }
    }

    protected void onException(Throwable t, final AtmosphereResource<?, ?> r) {
        logger.debug("onException()", t);

        if (r instanceof AtmosphereEventLifecycle) {
            ((AtmosphereEventLifecycle) r)
                    .notifyListeners(new AtmosphereResourceEventImpl((AtmosphereResourceImpl) r, true, false, t));
            ((AtmosphereEventLifecycle) r).removeEventListeners();
        }

        /**
         * Make sure we resume the connection on every IOException.
         */
        bc.getAsyncWriteService().execute( new Runnable()
        {
            @Override
            public void run()
            {
                r.resume();
            }
        } );

    }

    @Override
    public void setSuspendPolicy(long maxSuspendResource, POLICY policy) {
        this.maxSuspendResource = maxSuspendResource;
        this.policy = policy;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Future<T> broadcast(T msg) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        start();
        Object newMsg = filter(msg);
        if (newMsg == null) return null;

        BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg);
        messages.offer(new Entry(newMsg, null, f, msg));
        return f;
    }

    /**
     * Invoke the {@link BroadcastFilter}
     *
     * @param msg
     * @return
     */
    protected Object filter(Object msg) {
        BroadcastAction a = bc.filter(msg);
        if (a.action() == BroadcastAction.ACTION.ABORT || msg == null)
            return null;
        else
            return a.message();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Future<T> broadcast(T msg, AtmosphereResource<?, ?> r) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        start();
        Object newMsg = filter(msg);
        if (newMsg == null) return null;

        BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg);
        messages.offer(new Entry(newMsg, r, f, msg));
        return f;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Future<T> broadcastOnResume(T msg) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        start();
        Object newMsg = filter(msg);
        if (newMsg == null) return null;

        BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg);
        broadcastOnResume.offer(new Entry(newMsg, null, f, msg));
        return f;
    }

    protected void broadcastOnResume(AtmosphereResource<?,?> r){
        Iterator<Entry> i = broadcastOnResume.iterator();
        while (i.hasNext()) {
            Entry e = i.next();
            e.multipleAtmoResources = r;
            push(e);
        }
       
        if (resources.isEmpty()) {
            broadcastOnResume.clear();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Future<T> broadcast(T msg, Set<AtmosphereResource<?, ?>> subset) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        start();
        Object newMsg = filter(msg);
        if (newMsg == null) return null;

        BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg);
        messages.offer(new Entry(newMsg, subset, f, msg));
        return f;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AtmosphereResource<?, ?> addAtmosphereResource(AtmosphereResource<?, ?> r) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        if (scope == SCOPE.REQUEST && requestScoped.getAndSet(true)) {
            throw new IllegalStateException("Broadcaster " + this
                    + " cannot be used as its scope is set to REQUEST");
        }

        if (maxSuspendResource > 0 && resources.size() == maxSuspendResource) {
            // Resume the first in.
            if (policy == POLICY.FIFO) {
                // TODO handle null return from poll()
                AtmosphereResource<?, ?> resource = resources.poll();
                try {
                    resource.resume();
                }
                catch (Throwable t) {
                    logger.warn("failed to resume resource: " + resource, t);
                }
            } else if (policy == POLICY.REJECT) {
                throw new RejectedExecutionException(String.format("Maximum suspended AtmosphereResources %s", maxSuspendResource));
            }
        }

        if (resources.contains(r)) {
            return r;
        }

        // Re-add yourself
        if (resources.isEmpty()) {
            BroadcasterFactory.getDefault().add(this, name);
        }

        resources.add(r);
        checkCachedAndPush(r, r.getAtmosphereResourceEvent());
        return r;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AtmosphereResource<?, ?> removeAtmosphereResource(AtmosphereResource r) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        if (!resources.contains(r)) {
            return null;
        }
        resources.remove(r);

        // Will help preventing OOM.
        if (resources.isEmpty()) {
            notifyEmptyListener();
            if (lifeCyclePolicy.getLifeCyclePolicy() == BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.EMPTY) {
                releaseExternalResources();
            } else if (lifeCyclePolicy.getLifeCyclePolicy() == BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.EMPTY_DESTROY) {
                notifyDestroyListener();
                BroadcasterFactory.getDefault().remove(this, name);               
                destroy();
            }
        }     
        return r;
    }

    private void notifyIdleListener() {
        for (BroadcasterLifeCyclePolicyListener b: lifeCycleListeners) {
            b.onIdle();
        }
    }

    private void notifyDestroyListener() {
        for (BroadcasterLifeCyclePolicyListener b: lifeCycleListeners) {
            b.onDestroy();
        }
    }

    private void notifyEmptyListener() {
        for (BroadcasterLifeCyclePolicyListener b: lifeCycleListeners) {
            b.onEmpty();
        }
    }
    /**
     * Set the {@link BroadcasterConfig} instance.
     *
     * @param bc
     */
    @Override
    public void setBroadcasterConfig(BroadcasterConfig bc) {
        this.bc = bc;
    }

    /**
     * Return the current {@link BroadcasterConfig}
     *
     * @return the current {@link BroadcasterConfig}
     */
    public BroadcasterConfig getBroadcasterConfig() {
        return bc;
    }

    /**
     * {@inheritDoc}
     */
    public <T> Future<T> delayBroadcast(T o) {
        return delayBroadcast(o, 0, null);
    }

    /**
     * {@inheritDoc}
     */
    public <T> Future<T> delayBroadcast(final T o, long delay, TimeUnit t) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        start();
        final Object msg = filter(o);
        if (msg == null) return null;

        final BroadcasterFuture<Object> future = new BroadcasterFuture<Object>(msg);
        final Entry e = new Entry(msg, null, future, o);
        Future<T> f;
        if (delay > 0) {
            f = bc.getScheduledExecutorService().schedule(new Callable<T>() {

                public T call() throws Exception {
                    delayedBroadcast.remove(e);
                    if (Callable.class.isAssignableFrom(o.getClass())) {
                        try {
                            Object r = Callable.class.cast(o).call();
                            final Object msg = filter(r);
                            if (msg != null) {
                                Entry entry = new Entry(msg, null, null, r);
                                push(entry);
                            }
                            return (T) msg;
                        } catch (Exception e1) {
                            logger.error("", e);
                        }
                    }
                   
                    final Object msg = filter(o);
                    final Entry e = new Entry(msg, null, null, o);
                    push(e);
                    return (T) msg;
                }
            }, delay, t);

            e.future = new BroadcasterFuture<Object>(f, msg);
        }
        delayedBroadcast.offer(e);
        return future;
    }

    /**
     * {@inheritDoc}
     */
    public Future<?> scheduleFixedBroadcast(final Object o, long period, TimeUnit t) {
        return scheduleFixedBroadcast(o, 0, period, t);
    }

    /**
     * {@inheritDoc}
     */
    public Future<?> scheduleFixedBroadcast(final Object o, long waitFor, long period, TimeUnit t) {

        if (destroyed.get()) throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

        start();
        if (period == 0 || t == null) {
            return null;
        }

        final Object msg = filter(o);
        if (msg == null) return null;

        return bc.getScheduledExecutorService().scheduleWithFixedDelay(new Runnable() {
            public void run() {
                if (Callable.class.isAssignableFrom(o.getClass())) {
                    try {
                        Object r = Callable.class.cast(o).call();
                        final Object msg = filter(r);
                        if (msg != null) {
                            Entry entry = new Entry(msg, null, null, r);
                            push(entry);
                        }
                        return;
                    } catch (Exception e) {
                        logger.error("", e);
                    }
                }
                final Object msg = filter(o);
                final Entry e = new Entry(msg, null, null, o);
                push(e);
            }
        }, waitFor, period, t);
    }

    public String toString() {
        return new StringBuilder(this.getClass().getName()).append("@").append(this.hashCode()).append("\n")
                .append("\tName: ").append(name).append("\n")
                .append("\tScope: ").append(scope).append("\n")
                .append("\tBroasdcasterCache ").append(broadcasterCache).append("\n")
                .append("\tAtmosphereResource: ").append(resources.size()).append("\n")
                .toString();
    }

    private static class AsyncWriteToken {

        final AtmosphereResource<?, ?> resource;
        final Object msg;
        final BroadcasterFuture future;

        public AsyncWriteToken(AtmosphereResource<?, ?> resource, Object msg, BroadcasterFuture future) {
            this.resource = resource;
            this.msg = msg;
            this.future = future;
        }
    }

}
TOP

Related Classes of org.atmosphere.cpr.DefaultBroadcaster$AsyncWriteToken

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.