1

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();
    }
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
sco0ter
  • 65
  • 1
  • 6
  • First of all, why must it be an EJB? I'm not seeing anything which requires it to be an EJB. Do you actually understand when exactly to use an EJB? If it's merely for the @Startup, then your reasons were wrong. – BalusC Jan 19 '18 at 13:42
  • I thought nearly everything container managed is an EJB, but honestly I am no Java EE expert. I want to use the connections and messages to be used in a managed environment, e.g. using CDI and JPA to persist messages. I've looked into GenericServlet, but to communicate with other stateless EJBs, it would become a managed bean as well. – sco0ter Jan 19 '18 at 13:49

1 Answers1

1

Why can an enterprise bean not listen to or accept connections on a socket?

Because an EJB must act as a client, not as a server. This is confirmed EJB 3.2 spec. The technical reason is that EJBs are inherently serializable and open streams (sockets, files, etc) certainly aren't serializable.

The correct approach for your functional requirement is to just use a CDI managed bean, not an EJB.

@ApplicationScoped
public class SocketServerBean {

    public void startup(@Observes @Initialized(ApplicationScoped.class) ServletContext context) {
        // ...

    }

    @PreDestroy
    private void shutdown() {
        // ...
    }
}

The @Observes @Initialized(ApplicationScoped.class) will make sure that it gets constructed during application initialization.

In this CDI managed bean, you can in turn just inject an EJB if necessary.

See also:


Unrelated to the concrete problem, manually opening a socket from a Java EE application on stinks. Are you sure you aren't running into another XY-problem with this approach as well? Perhaps you actually wanted to use web sockets? If so, use JSR-356 @ServerEndpoint instead.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks very much for your thoughts. I've thought these restrictions generally apply to nearly everything what is deployed in a JavaEE container, except maybe JCA stuff, i.e. I thought a CDI bean is a subset of EJBs. The problem: I want to run an XMPP Chat Server, which accepts normal socket connections on port 5222, but also WebSocket connections and also HTTP-long-polling connections (BOSH). Clients can choose their connection method and I only want to work with an abstraction in the backend. Imagine an admin UI where you can filter all users connected via port 5222 or via WebSockets. – sco0ter Jan 19 '18 at 16:05
  • actually, the "proper" way is to use a JCA adapter that provides the connection as a resource to be consumed by the EJB rather than having the EJB manage the service. for the reasons described above. moving the problem to a CDI bean does not alleviate the issue because CDI beans are managed and anything you do that is non-transactional can impede or conflict with the management container. – him Jan 23 '18 at 07:00
  • What's the issue with being non-transactional? As I understood transactions are important for data integrity in databases and "grouping" multiple operations into one. Either all of them succeed or all of them fail.What I want here is simply an alternative to let's say a WebSocket Endpoint or a JAX-RS webservice. In the end I get a message object from all three (WS, JAX-RS, TCP sockets) endpoints and delegate them to some business layer, which may use transactions then. – sco0ter Jan 24 '18 at 09:48
  • transactional in the sense that there is no well defined boundary. i open a socket, and then what? how does the ejb/cdi container bound that method(s) execution? the same goes for file i/o. what constitutes the beginning or end of a file read (x number of bytes? end of line? end of file? what about random access? etc. ). what are the boundary constraints when a cdi bean with an open socket can be injected into n-many injection points on n-many threads? there is more than this in scope, and i only offer this as an example mention. again, what you describe above is exactly why JCA exists. – him Jan 24 '18 at 16:47
  • I thought CDI beans are non-transactional anyway. That's why EJBs, which are transactional restrict the usage of sockets, but CDI beans don't, as @BalusC pointed out. I don't understand the issue if my application-scoped bean is injected into other classes. The boundary is CDI container start and stop. I also haven't found a restriction for file IO in CDI beans, only for EJBs for the same reason (being transactional). – sco0ter Jan 25 '18 at 13:32