[#5174] Expose Bootstrap getter methods and add some additional ones

Motivation:

The Bootstrap class (applies also to AbstractBootstrap and ServerBootstrap) has a few package private getter methods and some things such as #attr() and #options() aren't exposed at all.

Modifications:

Expose "getters" for configured things in a safe-manner via the config() method.

Result:

Easier for the user to check what is configured for a Bootstrap/ServerBootstrap.
This commit is contained in:
Norman Maurer 2016-05-09 10:57:35 +02:00
parent da46928950
commit a425a8551d
6 changed files with 383 additions and 115 deletions

View File

@ -35,6 +35,7 @@ import io.netty.util.internal.StringUtil;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -314,7 +315,7 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
} }
final ChannelFuture initAndRegister() { final ChannelFuture initAndRegister() {
final Channel channel = channelFactory().newChannel(); final Channel channel = channelFactory.newChannel();
try { try {
init(channel); init(channel);
} catch (Throwable t) { } catch (Throwable t) {
@ -376,6 +377,41 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
return (B) this; return (B) this;
} }
/**
* Returns the configured {@link EventLoopGroup} or {@code null} if non is configured yet.
*
* @deprecated Use {@link #config()} instead.
*/
@Deprecated
public final EventLoopGroup group() {
return group;
}
/**
* Returns the {@link AbstractBootstrapConfig} object that can be used to obtain the current config
* of the bootstrap.
*/
public abstract AbstractBootstrapConfig<B, C> config();
static <K, V> Map<K, V> copiedMap(Map<K, V> map) {
final Map<K, V> copied;
synchronized (map) {
if (map.isEmpty()) {
return Collections.emptyMap();
}
copied = new LinkedHashMap<K, V>(map);
}
return Collections.unmodifiableMap(copied);
}
final Map<ChannelOption<?>, Object> options0() {
return options;
}
final Map<AttributeKey<?>, Object> attrs0() {
return attrs;
}
final SocketAddress localAddress() { final SocketAddress localAddress() {
return localAddress; return localAddress;
} }
@ -389,66 +425,19 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
return handler; return handler;
} }
/**
* Return the configured {@link EventLoopGroup} or {@code null} if non is configured yet.
*/
public EventLoopGroup group() {
return group;
}
final Map<ChannelOption<?>, Object> options() { final Map<ChannelOption<?>, Object> options() {
return options; return copiedMap(options);
} }
final Map<AttributeKey<?>, Object> attrs() { final Map<AttributeKey<?>, Object> attrs() {
return attrs; return copiedMap(attrs);
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder() StringBuilder buf = new StringBuilder()
.append(StringUtil.simpleClassName(this)) .append(StringUtil.simpleClassName(this))
.append('('); .append('(').append(config()).append(')');
if (group != null) {
buf.append("group: ")
.append(StringUtil.simpleClassName(group))
.append(", ");
}
if (channelFactory != null) {
buf.append("channelFactory: ")
.append(channelFactory)
.append(", ");
}
if (localAddress != null) {
buf.append("localAddress: ")
.append(localAddress)
.append(", ");
}
synchronized (options) {
if (!options.isEmpty()) {
buf.append("options: ")
.append(options)
.append(", ");
}
}
synchronized (attrs) {
if (!attrs.isEmpty()) {
buf.append("attrs: ")
.append(attrs)
.append(", ");
}
}
if (handler != null) {
buf.append("handler: ")
.append(handler)
.append(", ");
}
if (buf.charAt(buf.length() - 1) == '(') {
buf.append(')');
} else {
buf.setCharAt(buf.length() - 2, ')');
buf.setLength(buf.length() - 1);
}
return buf.toString(); return buf.toString();
} }

View File

@ -0,0 +1,135 @@
/*
* Copyright 2016 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.bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.util.AttributeKey;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.StringUtil;
import java.net.SocketAddress;
import java.util.Map;
/**
* Exposes the configuration of an {@link AbstractBootstrap}.
*/
public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C>, C extends Channel> {
protected final B bootstrap;
protected AbstractBootstrapConfig(B bootstrap) {
this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap");
}
/**
* Returns the configured local address or {@code null} if non is configured yet.
*/
public final SocketAddress localAddress() {
return bootstrap.localAddress();
}
/**
* Returns the configured {@link ChannelFactory} or {@code null} if non is configured yet.
*/
@SuppressWarnings("deprecation")
public final ChannelFactory<? extends C> channelFactory() {
return bootstrap.channelFactory();
}
/**
* Returns the configured {@link ChannelHandler} or {@code null} if non is configured yet.
*/
public final ChannelHandler handler() {
return bootstrap.handler();
}
/**
* Returns a copy of the configured options.
*/
public final Map<ChannelOption<?>, Object> options() {
return bootstrap.options();
}
/**
* Returns a copy of the configured attributes.
*/
public final Map<AttributeKey<?>, Object> attrs() {
return bootstrap.attrs();
}
/**
* Returns the configured {@link EventLoopGroup} or {@code null} if non is configured yet.
*/
@SuppressWarnings("deprecation")
public final EventLoopGroup group() {
return bootstrap.group();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder()
.append(StringUtil.simpleClassName(this))
.append('(');
EventLoopGroup group = group();
if (group != null) {
buf.append("group: ")
.append(StringUtil.simpleClassName(group))
.append(", ");
}
@SuppressWarnings("deprecation")
ChannelFactory<? extends C> factory = channelFactory();
if (factory != null) {
buf.append("channelFactory: ")
.append(factory)
.append(", ");
}
SocketAddress localAddress = localAddress();
if (localAddress != null) {
buf.append("localAddress: ")
.append(localAddress)
.append(", ");
}
Map<ChannelOption<?>, Object> options = options();
if (!options.isEmpty()) {
buf.append("options: ")
.append(options)
.append(", ");
}
Map<AttributeKey<?>, Object> attrs = attrs();
if (!attrs.isEmpty()) {
buf.append("attrs: ")
.append(attrs)
.append(", ");
}
ChannelHandler handler = handler();
if (handler != null) {
buf.append("handler: ")
.append(handler)
.append(", ");
}
if (buf.charAt(buf.length() - 1) == '(') {
buf.append(')');
} else {
buf.setCharAt(buf.length() - 2, ')');
buf.setLength(buf.length() - 1);
}
return buf.toString();
}
}

View File

@ -53,6 +53,8 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE; private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;
private final BootstrapConfig config = new BootstrapConfig(this);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private volatile AddressResolverGroup<SocketAddress> resolver = private volatile AddressResolverGroup<SocketAddress> resolver =
(AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER; (AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
@ -113,7 +115,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
throw new IllegalStateException("remoteAddress not set"); throw new IllegalStateException("remoteAddress not set");
} }
return doResolveAndConnect(remoteAddress, localAddress()); return doResolveAndConnect(remoteAddress, config.localAddress());
} }
/** /**
@ -139,7 +141,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
} }
validate(); validate();
return doResolveAndConnect(remoteAddress, localAddress()); return doResolveAndConnect(remoteAddress, config.localAddress());
} }
/** /**
@ -256,9 +258,9 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
void init(Channel channel) throws Exception { void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline(); ChannelPipeline p = channel.pipeline();
p.addLast(handler()); p.addLast(config.handler());
final Map<ChannelOption<?>, Object> options = options(); final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) { synchronized (options) {
for (Entry<ChannelOption<?>, Object> e: options.entrySet()) { for (Entry<ChannelOption<?>, Object> e: options.entrySet()) {
try { try {
@ -271,7 +273,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
} }
} }
final Map<AttributeKey<?>, Object> attrs = attrs(); final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) { synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue()); channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
@ -282,7 +284,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
@Override @Override
public Bootstrap validate() { public Bootstrap validate() {
super.validate(); super.validate();
if (handler() == null) { if (config.handler() == null) {
throw new IllegalStateException("handler not set"); throw new IllegalStateException("handler not set");
} }
return this; return this;
@ -306,17 +308,15 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
} }
@Override @Override
public String toString() { public final BootstrapConfig config() {
if (remoteAddress == null) { return config;
return super.toString();
} }
StringBuilder buf = new StringBuilder(super.toString()); final SocketAddress remoteAddress() {
buf.setLength(buf.length() - 1); return remoteAddress;
}
return buf.append(", remoteAddress: ") final AddressResolverGroup<?> resolver() {
.append(remoteAddress) return resolver;
.append(')')
.toString();
} }
} }

View File

@ -0,0 +1,58 @@
/*
* Copyright 2016 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.bootstrap;
import io.netty.channel.Channel;
import io.netty.resolver.AddressResolverGroup;
import java.net.SocketAddress;
/**
* Exposes the configuration of a {@link Bootstrap}.
*/
public final class BootstrapConfig extends AbstractBootstrapConfig<Bootstrap, Channel> {
BootstrapConfig(Bootstrap bootstrap) {
super(bootstrap);
}
/**
* Returns the configured remote address or {@code null} if non is configured yet.
*/
public SocketAddress remoteAddress() {
return bootstrap.remoteAddress();
}
/**
* Returns the configured {@link AddressResolverGroup} or the default if non is configured yet.
*/
public AddressResolverGroup<?> resolver() {
return bootstrap.resolver();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(super.toString());
buf.setLength(buf.length() - 1);
buf.append(", resolver: ").append(resolver());
SocketAddress remoteAddress = remoteAddress();
if (remoteAddress != null) {
buf.append(", remoteAddress: ")
.append(remoteAddress);
}
return buf.append(')').toString();
}
}

View File

@ -29,7 +29,6 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel; import io.netty.channel.ServerChannel;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.internal.OneTimeTask; import io.netty.util.internal.OneTimeTask;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -48,6 +47,7 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>(); private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup; private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler; private volatile ChannelHandler childHandler;
@ -138,22 +138,14 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
return this; return this;
} }
/**
* Return the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
* if non is configured yet.
*/
public EventLoopGroup childGroup() {
return childGroup;
}
@Override @Override
void init(Channel channel) throws Exception { void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options(); final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) { synchronized (options) {
channel.config().setOptions(options); channel.config().setOptions(options);
} }
final Map<AttributeKey<?>, Object> attrs = attrs(); final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) { synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -179,7 +171,7 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
@Override @Override
public void initChannel(Channel ch) throws Exception { public void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = handler(); ChannelHandler handler = config.handler();
if (handler != null) { if (handler != null) {
pipeline.addLast(handler); pipeline.addLast(handler);
} }
@ -294,42 +286,31 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
return new ServerBootstrap(this); return new ServerBootstrap(this);
} }
@Override /**
public String toString() { * Return the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
StringBuilder buf = new StringBuilder(super.toString()); * if non is configured yet.
buf.setLength(buf.length() - 1); *
buf.append(", "); * @deprecated Use {@link #config()} instead.
if (childGroup != null) { */
buf.append("childGroup: "); @Deprecated
buf.append(StringUtil.simpleClassName(childGroup)); public EventLoopGroup childGroup() {
buf.append(", "); return childGroup;
}
synchronized (childOptions) {
if (!childOptions.isEmpty()) {
buf.append("childOptions: ");
buf.append(childOptions);
buf.append(", ");
}
}
synchronized (childAttrs) {
if (!childAttrs.isEmpty()) {
buf.append("childAttrs: ");
buf.append(childAttrs);
buf.append(", ");
}
}
if (childHandler != null) {
buf.append("childHandler: ");
buf.append(childHandler);
buf.append(", ");
}
if (buf.charAt(buf.length() - 1) == '(') {
buf.append(')');
} else {
buf.setCharAt(buf.length() - 2, ')');
buf.setLength(buf.length() - 1);
} }
return buf.toString(); final ChannelHandler childHandler() {
return childHandler;
}
final Map<ChannelOption<?>, Object> childOptions() {
return copiedMap(childOptions);
}
final Map<AttributeKey<?>, Object> childAttrs() {
return copiedMap(childAttrs);
}
@Override
public final ServerBootstrapConfig config() {
return config;
} }
} }

View File

@ -0,0 +1,105 @@
/*
* Copyright 2016 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.bootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;
import java.util.Map;
/**
* Exposes the configuration of a {@link ServerBootstrapConfig}.
*/
public final class ServerBootstrapConfig extends AbstractBootstrapConfig<ServerBootstrap, ServerChannel> {
ServerBootstrapConfig(ServerBootstrap bootstrap) {
super(bootstrap);
}
/**
* Returns the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
* if non is configured yet.
*/
@SuppressWarnings("deprecation")
public EventLoopGroup childGroup() {
return bootstrap.childGroup();
}
/**
* Returns the configured {@link ChannelHandler} be used for the child channels or {@code null}
* if non is configured yet.
*/
public ChannelHandler childHandler() {
return bootstrap.childHandler();
}
/**
* Returns a copy of the configured options which will be used for the child channels.
*/
public Map<ChannelOption<?>, Object> childOptions() {
return bootstrap.childOptions();
}
/**
* Returns a copy of the configured attributes which will be used for the child channels.
*/
public Map<AttributeKey<?>, Object> childAttrs() {
return bootstrap.childAttrs();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(super.toString());
buf.setLength(buf.length() - 1);
buf.append(", ");
EventLoopGroup childGroup = childGroup();
if (childGroup != null) {
buf.append("childGroup: ");
buf.append(StringUtil.simpleClassName(childGroup));
buf.append(", ");
}
Map<ChannelOption<?>, Object> childOptions = childOptions();
if (!childOptions.isEmpty()) {
buf.append("childOptions: ");
buf.append(childOptions);
buf.append(", ");
}
Map<AttributeKey<?>, Object> childAttrs = childAttrs();
if (!childAttrs.isEmpty()) {
buf.append("childAttrs: ");
buf.append(childAttrs);
buf.append(", ");
}
ChannelHandler childHandler = childHandler();
if (childHandler != null) {
buf.append("childHandler: ");
buf.append(childHandler);
buf.append(", ");
}
if (buf.charAt(buf.length() - 1) == '(') {
buf.append(')');
} else {
buf.setCharAt(buf.length() - 2, ')');
buf.setLength(buf.length() - 1);
}
return buf.toString();
}
}