mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
security — sub server on custom port must not provide all IDE services if it is not desired (IdeTalk XML-RPC server must provide only IdeTalk services)
This commit is contained in:
@@ -16,6 +16,13 @@
|
||||
package com.intellij.ide;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public interface XmlRpcServer {
|
||||
void addHandler(String name, Object handler);
|
||||
@@ -24,6 +31,8 @@ public interface XmlRpcServer {
|
||||
|
||||
void removeHandler(String name);
|
||||
|
||||
boolean process(@NotNull String path, @NotNull HttpRequest request, @NotNull ChannelHandlerContext context, @Nullable Map<String, Object> handlers) throws IOException;
|
||||
|
||||
final class SERVICE {
|
||||
private SERVICE() {
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.jetbrains.ide;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class CustomPortServerManager {
|
||||
public static final ExtensionPointName<CustomPortServerManager> EP_NAME = ExtensionPointName.create("com.intellij.customPortServerManager");
|
||||
|
||||
@@ -34,4 +36,11 @@ public abstract class CustomPortServerManager {
|
||||
public abstract boolean isAvailableExternally();
|
||||
|
||||
public abstract void setManager(@Nullable CustomPortService manager);
|
||||
|
||||
/**
|
||||
* This server will accept only XML-RPC requests if this method returns not-null map of XMl-RPC handlers
|
||||
*/
|
||||
public Map<String, Object> createXmlRpcHandlers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package com.intellij.ide;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import gnu.trove.THashMap;
|
||||
import org.apache.xmlrpc.*;
|
||||
import org.jboss.netty.buffer.ChannelBuffer;
|
||||
@@ -33,47 +34,23 @@ import org.jetbrains.ide.HttpRequestHandler;
|
||||
import org.jetbrains.io.Responses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
public class XmlRpcServerImpl implements XmlRpcServer {
|
||||
private static final Logger LOG = Logger.getInstance(XmlRpcServerImpl.class);
|
||||
|
||||
private final XmlRpcHandlerMappingImpl handlerMapping;
|
||||
// idea doesn't use authentication
|
||||
private final XmlRpcContext xmlRpcContext = new XmlRpcContext() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUserName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlRpcHandlerMapping getHandlerMapping() {
|
||||
return handlerMapping;
|
||||
}
|
||||
};
|
||||
private final Map<String, Object> handlerMapping;
|
||||
|
||||
public XmlRpcServerImpl() {
|
||||
handlerMapping = LOG.isDebugEnabled() ? new LoggingDefaultHandlerMapping() : new XmlRpcHandlerMappingImpl();
|
||||
|
||||
handlerMapping = new THashMap<String, Object>();
|
||||
for (XmlRpcHandlerBean handlerBean : Extensions.getExtensions(XmlRpcHandlerBean.EP_NAME)) {
|
||||
final Object handler;
|
||||
try {
|
||||
handler = handlerBean.instantiate();
|
||||
handlerMapping.put(handlerBean.name, handlerBean.instantiate());
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
LOG.error(e);
|
||||
continue;
|
||||
}
|
||||
handlerMapping.addHandler(handlerBean.name, handler);
|
||||
}
|
||||
|
||||
LOG.debug("XmlRpcServerImpl instantiated, handlers " + handlerMapping);
|
||||
}
|
||||
|
||||
@@ -85,27 +62,28 @@ public class XmlRpcServerImpl implements XmlRpcServer {
|
||||
|
||||
@Override
|
||||
public boolean process(QueryStringDecoder urlDecoder, HttpRequest request, ChannelHandlerContext context) throws IOException {
|
||||
return ((XmlRpcServerImpl)SERVICE.getInstance()).process(urlDecoder, request, context);
|
||||
return SERVICE.getInstance().process(urlDecoder.getPath(), request, context, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHandler(String name) {
|
||||
return handlerMapping.handlers.containsKey(name);
|
||||
return handlerMapping.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHandler(String name, Object handler) {
|
||||
handlerMapping.addHandler(name, handler);
|
||||
handlerMapping.put(name, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHandler(String name) {
|
||||
handlerMapping.removeHandler(name);
|
||||
handlerMapping.remove(name);
|
||||
}
|
||||
|
||||
private boolean process(QueryStringDecoder urlDecoder, HttpRequest request, ChannelHandlerContext context) throws IOException {
|
||||
if (!isXmlRpcRequest(urlDecoder.getPath())) {
|
||||
@Override
|
||||
public boolean process(@NotNull String path, @NotNull HttpRequest request, @NotNull ChannelHandlerContext context, @Nullable Map<String, Object> handlers) throws IOException {
|
||||
if (!(path.isEmpty() || (path.length() == 1 && path.charAt(0) == '/') || path.equalsIgnoreCase("/RPC2"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,11 +91,13 @@ public class XmlRpcServerImpl implements XmlRpcServer {
|
||||
ChannelBuffer result;
|
||||
ChannelBufferInputStream in = new ChannelBufferInputStream(request.getContent());
|
||||
try {
|
||||
result = ChannelBuffers.copiedBuffer(new XmlRpcWorker(handlerMapping).execute(in, xmlRpcContext));
|
||||
XmlRpcServerRequest xmlRpcServerRequest = new XmlRpcRequestProcessor().decodeRequest(in);
|
||||
Object response = invokeHandler(getHandler(xmlRpcServerRequest.getMethodName(), handlers == null ? handlerMapping : handlers), xmlRpcServerRequest);
|
||||
result = ChannelBuffers.copiedBuffer(new XmlRpcResponseProcessor().encodeResponse(response, CharsetToolkit.UTF8));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
catch (Throwable e) {
|
||||
context.getChannel().close();
|
||||
LOG.error(ex);
|
||||
LOG.error(e);
|
||||
return true;
|
||||
}
|
||||
finally {
|
||||
@@ -137,80 +117,32 @@ public class XmlRpcServerImpl implements XmlRpcServer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isXmlRpcRequest(String path) {
|
||||
return path.isEmpty() || (path.length() == 1 && path.charAt(0) == '/') || path.equalsIgnoreCase("/RPC2");
|
||||
private static Object getHandler(String methodName, Map<String, Object> handlers) {
|
||||
Object handler = null;
|
||||
String handlerName = null;
|
||||
int dot = methodName.lastIndexOf('.');
|
||||
if (dot > -1) {
|
||||
handlerName = methodName.substring(0, dot);
|
||||
handler = handlers.get(handlerName);
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
IllegalStateException exception;
|
||||
if (dot > -1) {
|
||||
exception = new IllegalStateException("RPC handler object \"" + handlerName + "\" not found");
|
||||
}
|
||||
else {
|
||||
exception = new IllegalStateException("RPC handler object not found for \"" + methodName);
|
||||
}
|
||||
|
||||
LOG.error(exception);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
private static class XmlRpcHandlerMappingImpl implements XmlRpcHandlerMapping {
|
||||
protected final THashMap<String, Object> handlers = new THashMap<String, Object>();
|
||||
|
||||
public void addHandler(@NotNull String handlerName, @NotNull Object handler) {
|
||||
if (handler instanceof XmlRpcHandler) {
|
||||
handlers.put(handlerName, handler);
|
||||
}
|
||||
else {
|
||||
handlers.put(handlerName, new Invoker(handler));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeHandler(String handlerName) {
|
||||
handlers.remove(handlerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandler(String methodName) {
|
||||
Object handler = null;
|
||||
String handlerName = null;
|
||||
int dot = methodName.lastIndexOf('.');
|
||||
if (dot > -1) {
|
||||
handlerName = methodName.substring(0, dot);
|
||||
handler = handlers.get(handlerName);
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
IllegalStateException exception;
|
||||
if (dot > -1) {
|
||||
exception = new IllegalStateException("RPC handler object \"" + handlerName + "\" not found");
|
||||
}
|
||||
else {
|
||||
exception = new IllegalStateException("RPC handler object not found for \"" + methodName);
|
||||
}
|
||||
|
||||
LOG.error(exception);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private static class LoggingDefaultHandlerMapping extends XmlRpcHandlerMappingImpl {
|
||||
@Override
|
||||
public void addHandler(@NotNull String handlerName, @NotNull Object handler) {
|
||||
LOG.debug(String.format("addHandler: handlerName: %s, handler: %s%s", handlerName, handler, getHandlers()));
|
||||
super.addHandler(handlerName, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHandler(String handlerName) {
|
||||
LOG.debug(String.format("removeHandler: handlerName: %s%s", handlerName, getHandlers()));
|
||||
super.removeHandler(handlerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandler(String methodName) {
|
||||
LOG.debug(String.format("getHandler: methodName: %s%s", methodName, getHandlers()));
|
||||
return super.getHandler(methodName);
|
||||
}
|
||||
|
||||
private String getHandlers() {
|
||||
//noinspection SpellCheckingInspection
|
||||
return String.format("%nhandlers: %s %s", Arrays.toString(handlers.keySet().toArray()), Arrays.toString(handlers.values().toArray()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getHandlers();
|
||||
}
|
||||
private static Object invokeHandler(@NotNull Object handler, XmlRpcServerRequest request) throws Exception {
|
||||
return (handler instanceof XmlRpcHandler ? (XmlRpcHandler)handler : new Invoker(handler)).execute(request.getMethodName(), request.getParameters());
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class BuiltInServerManagerImpl extends BuiltInServerManager {
|
||||
|
||||
@Nullable
|
||||
private BuiltInServer server;
|
||||
private boolean myEnabledInUnitTestMode = true;
|
||||
private boolean enabledInUnitTestMode = true;
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
@@ -83,7 +83,7 @@ public class BuiltInServerManagerImpl extends BuiltInServerManager {
|
||||
|
||||
private Future<?> startServerInPooledThread() {
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (application.isUnitTestMode() && !myEnabledInUnitTestMode) {
|
||||
if (application.isUnitTestMode() && !enabledInUnitTestMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,6 @@ public class BuiltInServerManagerImpl extends BuiltInServerManager {
|
||||
*/
|
||||
@TestOnly
|
||||
public void setEnabledInUnitTestMode(boolean enabled) {
|
||||
myEnabledInUnitTestMode = enabled;
|
||||
enabledInUnitTestMode = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.io;
|
||||
|
||||
import com.intellij.ide.XmlRpcServer;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
@@ -23,17 +24,19 @@ import org.jboss.netty.channel.*;
|
||||
import org.jboss.netty.channel.group.ChannelGroup;
|
||||
import org.jboss.netty.channel.group.DefaultChannelGroup;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.jboss.netty.handler.codec.http.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.ide.CustomPortServerManager;
|
||||
import org.jetbrains.ide.PooledThreadExecutor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.jboss.netty.channel.Channels.pipeline;
|
||||
|
||||
public class BuiltInServer implements Disposable {
|
||||
private final ChannelGroup openChannels = new DefaultChannelGroup();
|
||||
|
||||
@@ -63,17 +66,34 @@ public class BuiltInServer implements Disposable {
|
||||
throw new IllegalStateException("server already started");
|
||||
}
|
||||
|
||||
ServerBootstrap bootstrap = createServerBootstrap(channelFactory, openChannels);
|
||||
ServerBootstrap bootstrap = createServerBootstrap(channelFactory, openChannels, null);
|
||||
int port = bind(firstPort, portsCount, tryAnyPort, bootstrap);
|
||||
bindCustomPorts(firstPort, port);
|
||||
return port;
|
||||
}
|
||||
|
||||
static ServerBootstrap createServerBootstrap(NioServerSocketChannelFactory channelFactory, ChannelGroup openChannels) {
|
||||
static ServerBootstrap createServerBootstrap(NioServerSocketChannelFactory channelFactory, ChannelGroup openChannels, @Nullable Map<String, Object> xmlRpcHandlers) {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap(channelFactory);
|
||||
bootstrap.setOption("child.tcpNoDelay", true);
|
||||
bootstrap.setOption("child.keepAlive", true);
|
||||
bootstrap.setPipelineFactory(new ChannelPipelineFactoryImpl(new PortUnificationServerHandler(openChannels)));
|
||||
if (xmlRpcHandlers == null) {
|
||||
final ChannelHandler handler = new PortUnificationServerHandler(openChannels);
|
||||
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
||||
@Override
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
return Channels.pipeline(handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
final XmlRpcDelegatingHttpRequestHandler handler = new XmlRpcDelegatingHttpRequestHandler(xmlRpcHandlers);
|
||||
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
||||
@Override
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
return Channels.pipeline(new HttpRequestDecoder(), new HttpChunkAggregator(1048576), new HttpResponseEncoder(), handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
return bootstrap;
|
||||
}
|
||||
|
||||
@@ -157,16 +177,17 @@ public class BuiltInServer implements Disposable {
|
||||
context.getPipeline().replace(DelegatingHttpRequestHandler.class, "replacedDefaultHandler", messageChannelHandler);
|
||||
}
|
||||
|
||||
private static class ChannelPipelineFactoryImpl implements ChannelPipelineFactory {
|
||||
private final ChannelHandler defaultHandler;
|
||||
private static final class XmlRpcDelegatingHttpRequestHandler extends DelegatingHttpRequestHandlerBase {
|
||||
private final Map<String, Object> handlers;
|
||||
|
||||
public ChannelPipelineFactoryImpl(ChannelHandler defaultHandler) {
|
||||
this.defaultHandler = defaultHandler;
|
||||
public XmlRpcDelegatingHttpRequestHandler(Map<String, Object> handlers) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
return pipeline(defaultHandler);
|
||||
protected boolean process(ChannelHandlerContext context, HttpRequest request, QueryStringDecoder urlDecoder) throws IOException {
|
||||
return (request.getMethod() == HttpMethod.POST || request.getMethod() == HttpMethod.OPTIONS) &&
|
||||
XmlRpcServer.SERVICE.getInstance().process(urlDecoder.getPath(), request, context, handlers);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,10 @@ import com.intellij.openapi.application.ex.ApplicationInfoEx;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.apache.sanselan.ImageFormat;
|
||||
import org.apache.sanselan.ImageWriteException;
|
||||
import org.apache.sanselan.Sanselan;
|
||||
import org.jboss.netty.channel.*;
|
||||
import org.jboss.netty.channel.ChannelHandler;
|
||||
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
|
||||
import org.jetbrains.ide.BuiltInServerManager;
|
||||
@@ -28,29 +30,16 @@ import org.jetbrains.ide.HttpRequestHandler;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
|
||||
import java.io.IOException;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
final class DelegatingHttpRequestHandler extends SimpleChannelUpstreamHandler {
|
||||
final class DelegatingHttpRequestHandler extends DelegatingHttpRequestHandlerBase {
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext context, MessageEvent event) throws Exception {
|
||||
if (!(event.getMessage() instanceof HttpRequest)) {
|
||||
context.sendUpstream(event);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequest request = (HttpRequest)event.getMessage();
|
||||
//if (BuiltInServer.LOG.isDebugEnabled()) {
|
||||
//BuiltInServer.LOG.debug(request.toString());
|
||||
//}
|
||||
|
||||
QueryStringDecoder urlDecoder = new QueryStringDecoder(request.getUri());
|
||||
|
||||
protected boolean process(ChannelHandlerContext context, HttpRequest request, QueryStringDecoder urlDecoder) throws IOException, ImageWriteException {
|
||||
HttpRequestHandler connectedHandler = (HttpRequestHandler)context.getAttachment();
|
||||
if (connectedHandler != null) {
|
||||
if (connectedHandler.isSupported(request) && connectedHandler.process(urlDecoder, request, context)) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
// prev cached connectedHandler is not suitable for this request, so, let's find it again
|
||||
context.setAttachment(null);
|
||||
@@ -63,7 +52,7 @@ final class DelegatingHttpRequestHandler extends SimpleChannelUpstreamHandler {
|
||||
icon.paintIcon(null, image.getGraphics(), 0, 0);
|
||||
byte[] icoBytes = Sanselan.writeImageToBytes(image, ImageFormat.IMAGE_FORMAT_ICO, null);
|
||||
Responses.send(icoBytes, FileResponses.createResponse(urlDecoder.getPath()), request, context);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,24 +62,13 @@ final class DelegatingHttpRequestHandler extends SimpleChannelUpstreamHandler {
|
||||
if (context.getAttachment() == null) {
|
||||
context.setAttachment(handler);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
BuiltInServer.LOG.error(e);
|
||||
}
|
||||
}
|
||||
Responses.sendStatus(request, context, NOT_FOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext context, ExceptionEvent event) throws Exception {
|
||||
try {
|
||||
BuiltInServer.LOG.error(event.getCause());
|
||||
}
|
||||
finally {
|
||||
context.setAttachment(null);
|
||||
event.getChannel().close();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.jetbrains.io;
|
||||
|
||||
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||
import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
|
||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
|
||||
|
||||
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
|
||||
|
||||
abstract class DelegatingHttpRequestHandlerBase extends SimpleChannelUpstreamHandler {
|
||||
@Override
|
||||
public final void messageReceived(ChannelHandlerContext context, MessageEvent event) throws Exception {
|
||||
if (!(event.getMessage() instanceof HttpRequest)) {
|
||||
context.sendUpstream(event);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequest request = (HttpRequest)event.getMessage();
|
||||
//if (BuiltInServer.LOG.isDebugEnabled()) {
|
||||
//BuiltInServer.LOG.debug(request.toString());
|
||||
//}
|
||||
|
||||
if (!process(context, request, new QueryStringDecoder(request.getUri()))) {
|
||||
Responses.sendStatus(request, context, NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean process(ChannelHandlerContext context, HttpRequest request, QueryStringDecoder urlDecoder) throws Exception;
|
||||
|
||||
@Override
|
||||
public final void exceptionCaught(ChannelHandlerContext context, ExceptionEvent event) throws Exception {
|
||||
try {
|
||||
BuiltInServer.LOG.error(event.getCause());
|
||||
}
|
||||
finally {
|
||||
context.setAttachment(null);
|
||||
event.getChannel().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ final class SubServer implements CustomPortServerManager.CustomPortService, Disp
|
||||
public SubServer(CustomPortServerManager user, NioServerSocketChannelFactory channelFactory) {
|
||||
this.user = user;
|
||||
user.setManager(this);
|
||||
bootstrap = BuiltInServer.createServerBootstrap(channelFactory, openChannels);
|
||||
bootstrap = BuiltInServer.createServerBootstrap(channelFactory, openChannels, user.createXmlRpcHandlers());
|
||||
}
|
||||
|
||||
public boolean bind(int port) {
|
||||
@@ -59,7 +59,6 @@ final class SubServer implements CustomPortServerManager.CustomPortService, Disp
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
// todo should we call releaseExternalResources? We use only 1 boss&worker thread
|
||||
openChannels.close().awaitUninterruptibly();
|
||||
openChannels.clear();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user