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 2013 The Netty Project
*
* The Netty Project 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 io.netty.channel.group;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ServerChannel;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.internal.ConcurrentSet;
import io.netty.util.internal.StringUtil;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The default {@link ChannelGroup} implementation.
*/
public class DefaultChannelGroup extends AbstractSet implements ChannelGroup {
private static final AtomicInteger nextId = new AtomicInteger();
private final String name;
private final EventExecutor executor;
private final ConcurrentSet serverChannels = new ConcurrentSet();
private final ConcurrentSet nonServerChannels = new ConcurrentSet();
private final ChannelFutureListener remover = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
remove(future.channel());
}
};
private final boolean stayClosed;
private volatile boolean closed;
/**
* Creates a new group with a generated name and the provided {@link EventExecutor} to notify the
* {@link ChannelGroupFuture}s.
*/
public DefaultChannelGroup(EventExecutor executor) {
this(executor, false);
}
/**
* Creates a new group with the specified {@code name} and {@link EventExecutor} to notify the
* {@link ChannelGroupFuture}s. Please note that different groups can have the same name, which means no
* duplicate check is done against group names.
*/
public DefaultChannelGroup(String name, EventExecutor executor) {
this(name, executor, false);
}
/**
* Creates a new group with a generated name and the provided {@link EventExecutor} to notify the
* {@link ChannelGroupFuture}s. {@code stayClosed} defines whether or not, this group can be closed
* more than once. Adding channels to a closed group will immediately close them, too. This makes it
* easy, to shutdown server and child channels at once.
*/
public DefaultChannelGroup(EventExecutor executor, boolean stayClosed) {
this("group-0x" + Integer.toHexString(nextId.incrementAndGet()), executor, stayClosed);
}
/**
* Creates a new group with the specified {@code name} and {@link EventExecutor} to notify the
* {@link ChannelGroupFuture}s. {@code stayClosed} defines whether or not, this group can be closed
* more than once. Adding channels to a closed group will immediately close them, too. This makes it
* easy, to shutdown server and child channels at once. Please note that different groups can have
* the same name, which means no duplicate check is done against group names.
*/
public DefaultChannelGroup(String name, EventExecutor executor, boolean stayClosed) {
if (name == null) {
throw new NullPointerException("name");
}
this.name = name;
this.executor = executor;
this.stayClosed = stayClosed;
}
@Override
public String name() {
return name;
}
@Override
public boolean isEmpty() {
return nonServerChannels.isEmpty() && serverChannels.isEmpty();
}
@Override
public int size() {
return nonServerChannels.size() + serverChannels.size();
}
@Override
public boolean contains(Object o) {
if (o instanceof Channel) {
Channel c = (Channel) o;
if (o instanceof ServerChannel) {
return serverChannels.contains(c);
} else {
return nonServerChannels.contains(c);
}
} else {
return false;
}
}
@Override
public boolean add(Channel channel) {
ConcurrentSet set =
channel instanceof ServerChannel? serverChannels : nonServerChannels;
boolean added = set.add(channel);
if (added) {
channel.closeFuture().addListener(remover);
}
if (stayClosed && closed) {
// First add channel, than check if closed.
// Seems inefficient at first, but this way a volatile
// gives us enough synchronization to be thread-safe.
//
// If true: Close right away.
// (Might be closed a second time by ChannelGroup.close(), but this is ok)
//
// If false: Channel will definitely be closed by the ChannelGroup.
// (Because closed=true always happens-before ChannelGroup.close())
//
// See https://github.com/netty/netty/issues/4020
channel.close();
}
return added;
}
@Override
public boolean remove(Object o) {
if (!(o instanceof Channel)) {
return false;
}
boolean removed;
Channel c = (Channel) o;
if (c instanceof ServerChannel) {
removed = serverChannels.remove(c);
} else {
removed = nonServerChannels.remove(c);
}
if (!removed) {
return false;
}
c.closeFuture().removeListener(remover);
return true;
}
@Override
public void clear() {
nonServerChannels.clear();
serverChannels.clear();
}
@Override
public Iterator iterator() {
return new CombinedIterator(
serverChannels.iterator(),
nonServerChannels.iterator());
}
@Override
public Object[] toArray() {
Collection channels = new ArrayList(size());
channels.addAll(serverChannels);
channels.addAll(nonServerChannels);
return channels.toArray();
}
@Override
public T[] toArray(T[] a) {
Collection channels = new ArrayList(size());
channels.addAll(serverChannels);
channels.addAll(nonServerChannels);
return channels.toArray(a);
}
@Override
public ChannelGroupFuture close() {
return close(ChannelMatchers.all());
}
@Override
public ChannelGroupFuture disconnect() {
return disconnect(ChannelMatchers.all());
}
@Override
public ChannelGroupFuture deregister() {
return deregister(ChannelMatchers.all());
}
@Override
public ChannelGroupFuture write(Object message) {
return write(message, ChannelMatchers.all());
}
// Create a safe duplicate of the message to write it to a channel but not affect other writes.
// See https://github.com/netty/netty/issues/1461
private static Object safeDuplicate(Object message) {
if (message instanceof ByteBuf) {
return ((ByteBuf) message).duplicate().retain();
} else if (message instanceof ByteBufHolder) {
return ((ByteBufHolder) message).duplicate().retain();
} else {
return ReferenceCountUtil.retain(message);
}
}
@Override
public ChannelGroupFuture write(Object message, ChannelMatcher matcher) {
if (message == null) {
throw new NullPointerException("message");
}
if (matcher == null) {
throw new NullPointerException("matcher");
}
Map futures = new LinkedHashMap(size());
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
futures.put(c, c.write(safeDuplicate(message)));
}
}
ReferenceCountUtil.release(message);
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
public ChannelGroup flush() {
return flush(ChannelMatchers.all());
}
@Override
public ChannelGroupFuture flushAndWrite(Object message) {
return writeAndFlush(message);
}
@Override
public ChannelGroupFuture writeAndFlush(Object message) {
return writeAndFlush(message, ChannelMatchers.all());
}
@Override
public ChannelGroupFuture disconnect(ChannelMatcher matcher) {
if (matcher == null) {
throw new NullPointerException("matcher");
}
Map futures =
new LinkedHashMap(size());
for (Channel c: serverChannels) {
if (matcher.matches(c)) {
futures.put(c, c.disconnect());
}
}
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
futures.put(c, c.disconnect());
}
}
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
public ChannelGroupFuture close(ChannelMatcher matcher) {
if (matcher == null) {
throw new NullPointerException("matcher");
}
Map futures =
new LinkedHashMap(size());
if (stayClosed) {
// It is important to set the closed to true, before closing channels.
// Our invariants are:
// closed=true happens-before ChannelGroup.close()
// ChannelGroup.add() happens-before checking closed==true
//
// See https://github.com/netty/netty/issues/4020
closed = true;
}
for (Channel c: serverChannels) {
if (matcher.matches(c)) {
futures.put(c, c.close());
}
}
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
futures.put(c, c.close());
}
}
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
public ChannelGroupFuture deregister(ChannelMatcher matcher) {
if (matcher == null) {
throw new NullPointerException("matcher");
}
Map futures =
new LinkedHashMap(size());
for (Channel c: serverChannels) {
if (matcher.matches(c)) {
futures.put(c, c.deregister());
}
}
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
futures.put(c, c.deregister());
}
}
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
public ChannelGroup flush(ChannelMatcher matcher) {
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
c.flush();
}
}
return this;
}
@Override
public ChannelGroupFuture flushAndWrite(Object message, ChannelMatcher matcher) {
return writeAndFlush(message, matcher);
}
@Override
public ChannelGroupFuture writeAndFlush(Object message, ChannelMatcher matcher) {
if (message == null) {
throw new NullPointerException("message");
}
Map futures = new LinkedHashMap(size());
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
futures.put(c, c.writeAndFlush(safeDuplicate(message)));
}
}
ReferenceCountUtil.release(message);
return new DefaultChannelGroupFuture(this, futures, executor);
}
/**
* Returns the {@link ChannelGroupFuture} which will be notified when all {@link Channel}s that are part of this
* {@link ChannelGroup}, at the time of calling, are closed.
*/
public ChannelGroupFuture newCloseFuture() {
return newCloseFuture(ChannelMatchers.all());
}
/**
* Returns the {@link ChannelGroupFuture} which will be notified when all {@link Channel}s that are part of this
* {@link ChannelGroup}, at the time of calling, are closed.
*/
public ChannelGroupFuture newCloseFuture(ChannelMatcher matcher) {
Map futures =
new LinkedHashMap(size());
for (Channel c: serverChannels) {
if (matcher.matches(c)) {
futures.put(c, c.closeFuture());
}
}
for (Channel c: nonServerChannels) {
if (matcher.matches(c)) {
futures.put(c, c.closeFuture());
}
}
return new DefaultChannelGroupFuture(this, futures, executor);
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int compareTo(ChannelGroup o) {
int v = name().compareTo(o.name());
if (v != 0) {
return v;
}
return System.identityHashCode(this) - System.identityHashCode(o);
}
@Override
public String toString() {
return StringUtil.simpleClassName(this) + "(name: " + name() + ", size: " + size() + ')';
}
}