Design Pattern for Dependency-Injected TelnetInputListener implementation?

31 views Asked by At

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.

0

There are 0 answers