Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2014 Jeanfrancois Arcand
*
* Licensed 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.atmosphere.cpr;
import org.atmosphere.cache.BroadcastMessage;
import org.atmosphere.cache.CacheMessage;
import org.atmosphere.cpr.BroadcastFilter.BroadcastAction;
import org.atmosphere.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
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.ConcurrentHashMap;
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.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_WAIT_TIME;
import static org.atmosphere.cpr.ApplicationConfig.MAX_INACTIVE;
import static org.atmosphere.cpr.ApplicationConfig.OUT_OF_ORDER_BROADCAST;
import static org.atmosphere.cpr.ApplicationConfig.SUSPENDED_ATMOSPHERE_RESOURCE_UUID;
import static org.atmosphere.cpr.BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.EMPTY;
import static org.atmosphere.cpr.BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.EMPTY_DESTROY;
import static org.atmosphere.cpr.BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.IDLE;
import static org.atmosphere.cpr.BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.IDLE_DESTROY;
import static org.atmosphere.cpr.BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.IDLE_RESUME;
import static org.atmosphere.cpr.BroadcasterLifeCyclePolicy.ATMOSPHERE_RESOURCE_POLICY.NEVER;
import static org.atmosphere.cpr.FrameworkConfig.INJECTED_ATMOSPHERE_RESOURCE;
/**
* The default {@link Broadcaster} implementation.
*
* Broadcast messages to suspended responses using the caller's Thread.
* This basic {@link Broadcaster} use an {@link java.util.concurrent.ExecutorService}
* to broadcast messages, 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 {
public static final int POLLING_DEFAULT = 100;
public static final String CACHED = DefaultBroadcaster.class.getName() + ".messagesCached";
public static final String ASYNC_TOKEN = DefaultBroadcaster.class.getName() + ".token";
private static final Logger logger = LoggerFactory.getLogger(DefaultBroadcaster.class);
private static final String DESTROYED = "This Broadcaster has been destroyed and cannot be used {} by invoking {}";
private static final List EMPTY_LISTENERS = new ArrayList();
protected final ConcurrentLinkedQueue resources =
new ConcurrentLinkedQueue();
protected BroadcasterConfig bc;
protected final BlockingQueue messages = new LinkedBlockingQueue();
protected final ConcurrentLinkedQueue broadcasterListeners = new ConcurrentLinkedQueue();
protected final AtomicBoolean started = new AtomicBoolean(false);
protected final AtomicBoolean initialized = new AtomicBoolean(false);
protected final AtomicBoolean destroyed = new AtomicBoolean(false);
protected SCOPE scope = SCOPE.APPLICATION;
protected String name = DefaultBroadcaster.class.getSimpleName();
protected final ConcurrentLinkedQueue delayedBroadcast = new ConcurrentLinkedQueue();
protected final ConcurrentLinkedQueue broadcastOnResume = new ConcurrentLinkedQueue();
protected final ConcurrentLinkedQueue lifeCycleListeners = new ConcurrentLinkedQueue();
protected final ConcurrentHashMap writeQueues = new ConcurrentHashMap();
protected final WriteQueue uniqueWriteQueue = new WriteQueue("-1");
protected final AtomicInteger dispatchThread = new AtomicInteger();
protected Future>[] notifierFuture;
protected Future>[] asyncWriteFuture;
private POLICY policy = POLICY.FIFO;
private final AtomicLong maxSuspendResource = new AtomicLong(-1);
private final AtomicBoolean requestScoped = new AtomicBoolean(false);
private final AtomicBoolean recentActivity = new AtomicBoolean(false);
private BroadcasterLifeCyclePolicy lifeCyclePolicy = new BroadcasterLifeCyclePolicy.Builder()
.policy(NEVER).build();
private Future> currentLifecycleTask;
protected URI uri;
protected AtmosphereConfig config;
private final Object[] awaitBarrier = new Object[0];
private final AtomicBoolean outOfOrderBroadcastSupported = new AtomicBoolean(false);
protected int writeTimeoutInSecond = -1;
protected int waitTime = POLLING_DEFAULT;
private boolean backwardCompatible = false;
public DefaultBroadcaster() {
}
public Broadcaster initialize(String name, URI uri, AtmosphereConfig config) {
this.name = name;
this.uri = uri;
this.config = config;
bc = createBroadcasterConfig(config);
String s = config.getInitParameter(ApplicationConfig.BROADCASTER_CACHE_STRATEGY);
if (s != null) {
logger.warn("{} is no longer supported. Use BroadcastInterceptor instead. By default the original message will be cached.", ApplicationConfig.BROADCASTER_CACHE_STRATEGY);
}
s = config.getInitParameter(OUT_OF_ORDER_BROADCAST);
if (s != null) {
outOfOrderBroadcastSupported.set(Boolean.valueOf(s));
}
s = config.getInitParameter(BROADCASTER_WAIT_TIME);
if (s != null) {
waitTime = Integer.valueOf(s);
}
s = config.getInitParameter(ApplicationConfig.WRITE_TIMEOUT);
if (s != null) {
writeTimeoutInSecond = Integer.valueOf(s);
}
if (outOfOrderBroadcastSupported.get()) {
logger.trace("{} supports Out Of Order Broadcast: {}", name, outOfOrderBroadcastSupported.get());
}
initialized.set(true);
backwardCompatible = Boolean.parseBoolean(config.getInitParameter(ApplicationConfig.BACKWARD_COMPATIBLE_WEBSOCKET_BEHAVIOR));
return this;
}
public Broadcaster initialize(String name, AtmosphereConfig config) {
return initialize(name, URI.create("http://localhost"), config);
}
/**
* Create {@link BroadcasterConfig}.
*
* @param config the {@link AtmosphereConfig}
* @return an instance of {@link BroadcasterConfig}
*/
protected BroadcasterConfig createBroadcasterConfig(AtmosphereConfig config) {
return new BroadcasterConfig(config.framework().broadcasterFilters, config, getID()).init();
}
@Override
public synchronized void destroy() {
if (notifyOnPreDestroy()) return;
if (destroyed.getAndSet(true)) return;
notifyDestroyListener();
try {
logger.trace("Broadcaster {} is being destroyed and cannot be re-used. Policy was {}", getID(), policy);
logger.trace("Broadcaster {} is being destroyed and cannot be re-used. Resources are {}", getID(), resources);
if (config.getBroadcasterFactory() != null) {
config.getBroadcasterFactory().remove(this, this.getID());
}
if (currentLifecycleTask != null) {
currentLifecycleTask.cancel(true);
}
started.set(false);
releaseExternalResources();
killReactiveThreads();
if (bc != null) {
bc.destroy();
}
resources.clear();
broadcastOnResume.clear();
messages.clear();
delayedBroadcast.clear();
broadcasterListeners.clear();
writeQueues.clear();
} catch (Throwable t) {
logger.error("Unexpected exception during Broadcaster destroy {}", getID(), t);
}
}
@Override
public Collection getAtmosphereResources() {
return Collections.unmodifiableCollection(resources);
}
@Override
public void setScope(SCOPE scope) {
if (destroyed.get()) {
logger.debug(DESTROYED, getID(), "setScope");
return;
}
this.scope = scope;
if (scope != SCOPE.REQUEST) {
return;
}
logger.debug("Changing broadcaster scope for {}. This broadcaster will be destroyed.", getID());
synchronized (resources) {
try {
// Next, we need to create a new broadcaster per resource.
for (AtmosphereResource resource : resources) {
Broadcaster b = config.getBroadcasterFactory()
.get(getClass(), getClass().getSimpleName() + "/" + UUID.randomUUID());
/**
* REQUEST_SCOPE means one BroadcasterCache per Broadcaster,
*/
if (DefaultBroadcaster.class.isAssignableFrom(this.getClass())) {
BroadcasterCache cache = config.framework().newClassInstance(BroadcasterCache.class, bc.getBroadcasterCache().getClass());
b.getBroadcasterConfig().setBroadcasterCache(cache);
}
resource.setBroadcaster(b);
b.setScope(SCOPE.REQUEST);
if (resource.getAtmosphereResourceEvent().isSuspended()) {
b.addAtmosphereResource(resource);
}
logger.debug("Resource {} not using broadcaster {}", resource, b.getID());
}
// Do not destroy because this is a new Broadcaster
if (resources.isEmpty()) {
return;
}
destroy();
} catch (Exception e) {
logger.error("Failed to set request scope for current resources", e);
}
}
}
@Override
public SCOPE getScope() {
return scope;
}
@Override
public synchronized void setID(String id) {
if (id == null) {
id = getClass().getSimpleName() + "/" + UUID.randomUUID();
}
if (config.getBroadcasterFactory() == null)
return; // we are shutdown or destroyed, but someone still reference
Broadcaster b = config.getBroadcasterFactory().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");
} else if (b != null) {
return;
}
config.getBroadcasterFactory().remove(this, name);
this.name = id;
config.getBroadcasterFactory().add(this, name);
bc.broadcasterID(name);
}
@Override
public String getID() {
return name;
}
@Override
public void resumeAll() {
synchronized (resources) {
for (AtmosphereResource r : resources) {
try {
r.resume();
} catch (Throwable t) {
logger.trace("resumeAll", t);
} finally {
removeAtmosphereResource(r);
}
}
}
}
@Override
public void releaseExternalResources() {
}
@Override
public void setBroadcasterLifeCyclePolicy(final BroadcasterLifeCyclePolicy lifeCyclePolicy) {
this.lifeCyclePolicy = lifeCyclePolicy;
if (logger.isTraceEnabled()) {
logger.trace("{} new lifecycle policy: {}", name, lifeCyclePolicy.getLifeCyclePolicy().name());
}
if (currentLifecycleTask != null) {
currentLifecycleTask.cancel(false);
}
if (bc != null && bc.getScheduledExecutorService() == null) {
logger.error("No Broadcaster's SchedulerExecutorService has been configured on {}. BroadcasterLifeCyclePolicy won't work.", getID());
return;
}
if (lifeCyclePolicy.getLifeCyclePolicy() == IDLE
|| lifeCyclePolicy.getLifeCyclePolicy() == IDLE_RESUME
|| lifeCyclePolicy.getLifeCyclePolicy() == IDLE_DESTROY) {
recentActivity.set(false);
int time = lifeCyclePolicy.getTimeout();
if (time == -1) {
throw new IllegalStateException("BroadcasterLifeCyclePolicy time is not set");
}
final AtomicReference> ref = new AtomicReference>();
currentLifecycleTask = bc.getScheduledExecutorService().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
// Check for activity since the last execution.
if (recentActivity.getAndSet(false)) {
return;
} else if (resources.isEmpty()) {
if (lifeCyclePolicy.getLifeCyclePolicy() == IDLE) {
notifyEmptyListener();
notifyIdleListener();
releaseExternalResources();
logger.debug("Applying BroadcasterLifeCyclePolicy IDLE policy to Broadcaster {}", getID());
} else if (lifeCyclePolicy.getLifeCyclePolicy() == IDLE_DESTROY) {
notifyEmptyListener();
notifyIdleListener();
destroy(false);
logger.debug("Applying BroadcasterLifeCyclePolicy IDLE_DESTROY policy to Broadcaster {}", getID());
}
} else if (lifeCyclePolicy.getLifeCyclePolicy() == IDLE_RESUME) {
notifyIdleListener();
destroy(true);
logger.debug("Applying BroadcasterLifeCyclePolicy IDLE_RESUME policy to Broadcaster {}", getID());
}
} catch (Throwable t) {
if (destroyed.get()) {
logger.trace("Scheduled BroadcasterLifeCyclePolicy exception", t);
} else {
logger.warn("Scheduled BroadcasterLifeCyclePolicy exception", t);
}
}
}
void destroy(boolean resume) {
if (resume) {
logger.info("All AtmosphereResource will now be resumed from Broadcaster {}", getID());
resumeAll();
}
DefaultBroadcaster.this.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);
}
}
}, 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);
}
@Override
public boolean isDestroyed() {
return destroyed.get();
}
@Override
public Future