I want to serve a socket from a Java EE container like Wildfly, but found this EJB restriction on the Oracle site:
Why can an enterprise bean not listen to or accept connections on a socket?
Because if an enterprise bean is listening on a socket, it can't be passivated -- it must always be available.
I asked the same question to a Java EE expert and he answered "only stateful beans are passivated, so serving sockets from a singleton stateless startup bean is fine, since it's not passivated".
However, I then looked up the EJB 3.2 specification and found another explanation:
An enterprise bean must not attempt to listen on a socket, accept connect ions on a socket, or use a socket for multicast
The EJB architecture allows an enterprise bean instance to be a network socket client, but it does not allow it to be a network server. Allowing the instance to become a network server would conflict with the basic function of the enterprise bean — to serve the EJB clients.
Ugh. Now I am confused. I don't really get it.
Is this restriction outdated? Is it a left-over from a time when there where no singleton startup beans (< EJB 3.1)?
What's so wrong about an approach similar to the following (using Netty) or even a simpler approach with Java's ServerSocketChannel
which accepts client connections in a managed thread. (not sure if Netty spawns its own threads).
Can somebody please explain this restriction?
@Singleton
@Startup
public class SocketServerBean {
@Resource
private ManagedExecutorService managedExecutorService;
private Channel channel;
@PostConstruct
private void startup() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1, managedExecutorService);
EventLoopGroup workerGroup = new NioEventLoopGroup(1, managedExecutorService);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// ...
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
channel = b.bind(6789).channel();
} catch (Exception e) {
e.printStackTrace();
}
}
@PreDestroy
private void shutdown() {
channel.close();
}
}