Java tutorial
/* * * Copyright (c) 2016 Caricah <info@caricah.com>. * * Caricah 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 com.caricah.iotracah.server.netty.channelgroup; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.channel.*; import io.netty.channel.group.*; import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.EventExecutor; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * @author <a href="mailto:bwire@caricah.com"> Peter Bwire </a> * @version 1.0 2/23/16 */ public class IotChannelGroup extends AbstractSet<Channel> implements ChannelGroup { private static final AtomicInteger nextId = new AtomicInteger(); private final String name; private final EventExecutor executor; private final ConcurrentMap<String, Channel> serverChannels = PlatformDependent.newConcurrentHashMap(); private final ConcurrentMap<String, Channel> nonServerChannels = PlatformDependent.newConcurrentHashMap(); private final ChannelFutureListener remover = future -> remove(future.channel()); /** * Creates a new group with a generated name and the provided {@link EventExecutor} to notify the * {@link ChannelGroupFuture}s. */ public IotChannelGroup(EventExecutor executor) { this("group-0x" + Integer.toHexString(nextId.incrementAndGet()), executor); } /** * 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 IotChannelGroup(String name, EventExecutor executor) { if (name == null) { throw new NullPointerException("name"); } this.name = name; this.executor = executor; } @Override public String name() { return name; } @Override public Channel find(ChannelId id) { Channel c = nonServerChannels.get(id.asLongText()); if (c != null) { return c; } else { return serverChannels.get(id.asLongText()); } } public Channel find(String idString) { Channel c = nonServerChannels.get(idString); if (c != null) { return c; } else { return serverChannels.get(idString); } } @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.containsValue(c); } else { return nonServerChannels.containsValue(c); } } else { return false; } } @Override public boolean add(Channel channel) { ConcurrentMap<String, Channel> map = channel instanceof ServerChannel ? serverChannels : nonServerChannels; boolean added = map.putIfAbsent(channel.id().asLongText(), channel) == null; if (added) { channel.closeFuture().addListener(remover); } return added; } @Override public boolean remove(Object o) { Channel c = null; if (o instanceof ChannelId) { c = nonServerChannels.remove(o); if (c == null) { c = serverChannels.remove(o); } } else if (o instanceof Channel) { c = (Channel) o; if (c instanceof ServerChannel) { c = serverChannels.remove(c.id().asLongText()); } else { c = nonServerChannels.remove(c.id().asLongText()); } } if (c == null) { return false; } c.closeFuture().removeListener(remover); return true; } @Override public void clear() { nonServerChannels.clear(); serverChannels.clear(); } @Override public Iterator<Channel> iterator() { return new IotCombinedIterator<>(serverChannels.values().iterator(), nonServerChannels.values().iterator()); } @Override public Object[] toArray() { Collection<Channel> channels = new ArrayList<>(size()); channels.addAll(serverChannels.values()); channels.addAll(nonServerChannels.values()); return channels.toArray(); } @Override public <T> T[] toArray(T[] a) { Collection<Channel> channels = new ArrayList<>(size()); channels.addAll(serverChannels.values()); channels.addAll(nonServerChannels.values()); 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<Channel, ChannelFuture> futures = new LinkedHashMap<>(size()); nonServerChannels.values().stream().filter(matcher::matches) .forEach(c -> futures.put(c, c.write(safeDuplicate(message)))); ReferenceCountUtil.release(message); return new IotChannelGroupFuture(this, futures, executor); } @Override public ChannelGroup flush() { return flush(ChannelMatchers.all()); } @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<Channel, ChannelFuture> futures = new LinkedHashMap<>(size()); serverChannels.values().stream().filter(matcher::matches).forEach(c -> futures.put(c, c.disconnect())); nonServerChannels.values().stream().filter(matcher::matches).forEach(c -> futures.put(c, c.disconnect())); return new IotChannelGroupFuture(this, futures, executor); } @Override public ChannelGroupFuture close(ChannelMatcher matcher) { if (matcher == null) { throw new NullPointerException("matcher"); } Map<Channel, ChannelFuture> futures = new LinkedHashMap<>(size()); serverChannels.values().stream().filter(matcher::matches).forEach(c -> futures.put(c, c.close())); nonServerChannels.values().stream().filter(matcher::matches).forEach(c -> futures.put(c, c.close())); return new IotChannelGroupFuture(this, futures, executor); } @Override public ChannelGroupFuture deregister(ChannelMatcher matcher) { if (matcher == null) { throw new NullPointerException("matcher"); } Map<Channel, ChannelFuture> futures = new LinkedHashMap<>(size()); serverChannels.values().stream().filter(matcher::matches).forEach(c -> futures.put(c, c.deregister())); nonServerChannels.values().stream().filter(matcher::matches).forEach(c -> futures.put(c, c.deregister())); return new IotChannelGroupFuture(this, futures, executor); } @Override public ChannelGroup flush(ChannelMatcher matcher) { nonServerChannels.values().stream().filter(matcher::matches).forEach(io.netty.channel.Channel::flush); return this; } @Override public ChannelGroupFuture writeAndFlush(Object message, ChannelMatcher matcher) { if (message == null) { throw new NullPointerException("message"); } Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(size()); nonServerChannels.values().stream().filter(matcher::matches) .forEach(c -> futures.put(c, c.writeAndFlush(safeDuplicate(message)))); ReferenceCountUtil.release(message); return new IotChannelGroupFuture(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() + ')'; } }