Singleton services were never intended to be directly invokable. If possible, I would suggest packaging your logic into a @Singleton EJB, and deploying it as a singleton service.
You can then make traditional remote EJB invocations to the singleton EJB, which should only ever be deployed on one server of your cluster.
Alternatively, if you don't mind using WildFly specific APIs, you can leverage WildFly's CommandDispatcher to dispatch commands to the primary provider of your singleton service as a means to invoke on your singleton.
N.B. The singleton service installation API saw some much needed improvements for WF34, so the code below is specific to WF34 and later:
@MetaInfServices(ServiceActivator.class)
public void MySingletonServiceActivator implements SingletonServiceActivator {
@Override
public void activate(SingletonServiceActivatorContext context) {
SingletonServiceTarget target = context.getServiceTarget();
SingletonServiceController<?> singleton = target.addService().setInstance(new MyService()).install();
// Bind Singleton to JNDI for use by EE components
new InitialContext().bind(..., singleton);
}
}
From your application code, you can observe the state of the singleton and dispatch commands to the primary provider:
e.g.
@jakarta.ejb.Singleton
public void MySingletonBean {
@Resource(lookup = "jboss:clustering/server/command-dispatcher-factory/server")
CommandDispatcherFactory<GroupMember> dispatcherFactory;
CommandDispatcher<GroupMember, MySingletonBean> dispatcher;
@PostConstruct
public void init() {
this.dispatcher = dispatcherFactory.createCommandDispatcher("foo", this);
}
@PreDestroy
public void destroy() {
this.dispatcher.close();
}
public void invokeOnPrimary() {
Singleton singleton = (Singleton) new InitialContext().lookup(...);
Optional<GroupMember> member = singleton.getSingletonState().getPrimaryProvider();
if (member.isPresent()) {
Command<Foo, MySingletonBean, RuntimeException> command = new FooCommand<>();
CompletionStage<Foo> result = this.dispatcher.dispatchToMember(new FooCommand<>(), member.get());
// ...
}
}
}
Paul