I'm working on a Spring project using the Apache commons-net TelnetClient. The TelnetClient has a method to register a listener of type TelnetInputListener, which is an interface containing a callback method that is invoked whenever there's new data available on the TelnetClient.
I'm trying to use Spring's DI framework to manage dependencies, but I am running into a circular dependency issue. I have created a TelnetService class (and registered it to the ApplicationContext using @Service) to do things with the TelnetClient such as connect, send a message, etc. (the TelnetClient is itself registered as a @Bean with Spring within a @Confugration class), but trying to work with the TelnetInputListener via dependency injection is causing issues because I want to be able to access it from within my TelnetService class, but the TelnetInputListener itself needs a reference to the TelnetService in order to invoke the method to read a message once the callback is called upon new data becoming available.
Here's some code snippets to clarify what I mean:
@Service
public class TelnetService {
private final TelnetClient telnetClient;
private final TelnetInputListenerImpl telnetInputListener;
private final ObjectProvider<Reader> objectProvider;
public TelnetService(TelnetClient telnetClient, TelnetInputListenerImpl telnetInputListener,
ObjectProvider<Reader> objectProvider) {
this.telnetClient = telnetClient;
this.telnetInputListener = telnetInputListener;
this.objectProvider = objectProvider;
}
public void connect() {
// connection code goes here, not relevant to question at hand
}
**// this is the method I'm trying to invoke when the TelnetInputListener callback is invoked**
public void read() {
InputStream inputStream = telnetClient.getInputStream();
Reader inputStreamReader = objectProvider.getObject(inputStream);
try {
int byteData = inputStreamReader.read();
while (byteData != -1) {
System.out.print((char) byteData);
byteData = inputStreamReader.read();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Service **// the implementation of the TelnetInputListener interface**
public class TelnetInputListenerImpl implements TelnetInputListener {
private final TelnetService telnetService;
public TelnetInputListenerImpl(TelnetService telnetService) {
this.telnetService = telnetService;
}
@Override
public void telnetInputAvailable() {
telnetService.read();
}
}
**// and finally, the Beans defined in a @Configuration class**
@Bean
public TelnetClient telnetClient() {
return new TelnetClient();
}
@Bean
@Scope(value = "prototype")
public Reader reader(InputStream inputStream)
{
return new InputStreamReader(inputStream);
}
Any help would be greatly appreciated. How would you refactor this to still make the best use of dependency injection (unless DI is completely unnecessary/unworkable in this situation, although I'm sure there's a way to do this that I'm overlooking)?
Console output when trying to run the program:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
appController defined in file [\Controller\AppController.class]
┌─────┐
| telnetService defined in file [\Service\TelnetService.class]
↑ ↓
| telnetInputListenerImpl defined in file [\Service\TelnetInputListenerImpl.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.