I am trying to figure out CDI and the best method that suits my needs.
I have an service(TcpServiceImpl
) that interacts with plain tcp communication. Now this service has some points where it needs to inform somebody that something happened. For this informations I have the Interface TcpConnection
which needs to be CDI injected to the correct implementation. Another problem is that the service TcpServiceImpl
itself is injected in a job (TcpConnectionJob
) that executes periodically and calls the service to do things.
This means that the service TcpServiceImpl
will exist multiple times. Each having another tcp connection it handles and having another device that needs another driver/protocol to be injected in the Interface TcpConnection
.
Let me show the three Elements taking part in this scenario:
Here is the Interface that will get multiple implementations:
public interface TcpConnection
{
/**
* Connected.
*
* @throws NGException the NG exception
*/
public void connected() throws NGException;
/**
* This method will send the received data from the InputStream of the connection.
*
* @param data the received data
* @throws NGException the NG exception
*/
public void received( byte[] data ) throws NGException;
/**
* Usable for the protocol to send data to the device.
*
* @param data the data to send to the device ( Will be converted to byte[] with getBytes() )
* @throws NGException the NG exception
*/
public void send( String data ) throws NGException;
/**
* Usable for the protocol to send data to the device.
*
* @param data the data to send to the device ( Will be send as is )
* @throws NGException the NG exception
*/
public void send( byte[] data ) throws NGException;
/**
* This method will inform the protocol that the connection got closed.
*
* @throws NGException the NG exception
*/
public void closed() throws NGException;
}
Also here is a example snippet of when this will be called in my existing service:
public class TCPServiceImpl implements TCPService, Runnable
{
/** The callback. */
private TcpConnection callback;
private void disconnect()
{
connection.disconnect();
if ( !getStatus( jndiName ).equals( ConnectionStatus.FAILURE ) )
{
setStatus( ConnectionStatus.CLOSED );
}
/* TODO: Tell driver connection is closed! */
callback.closed();
}
}
Below is the class that calls the service,which then needs to dynamically inject the correct implementation for the interface.
public class TcpConnectionJob implements JobRunnable
{
/** The service. */
private TCPService service;
public void execute()
{
service.checkConnection( connection );
}
}
The service injection callback
has to be linked to the implementation of the correct "protocol" or "driver" that will translate the data or handle the logic for the device. There will be multiple driver implementations of the interface acting different and I need to inject the correct one. A qualifier for this decision could be the name of the device. Now I looked at the following links:
Understanding the necessity of type Safety in CDI
How to use CDI qualifiers with multiple class implementations?
Question:
But I am still unsure about which way/method to use and what is the correct way. Any help would be appreciated.
My first thought was about copying my interface to an Qualifier Interface and appending this one with the possibility to enter the qualifier. Is that a good idea?
So this is my solution I came up with. The only problem now is to get a callback to work, but that's something different. Heres the solution that worked for me:
/**
* The Qualifier interface TcpDriver. The value of this annotation is the name the implementation
* is found under. Please only enter values that are configured in the wildfly config as the name of
* the device.
*/
@Documented
@Qualifier
@Retention( RUNTIME )
@Target( { TYPE, FIELD, METHOD, PARAMETER } )
public @interface TcpDriver
{
/**
* Value.
*
* @return the string
*/
String value();
}
The default Implementation just for the Qualifier interface:
/**
* The Class TcpDriverImpl.
*/
public class TcpDriverImpl extends AnnotationLiteral<TcpDriver> implements TcpDriver
{
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The name. */
private final String name;
/**
* Instantiates a new tcp driver impl.
*
* @param name the name
*/
public TcpDriverImpl( final String name )
{
this.name = name;
}
/** {@inheritDoc} */
@Override
public String value()
{
return name;
}
}
Now a test implementation to test it:
@TcpDriver( "terminal1" )
@Dependent
public class TestDriverImpl implements TcpConnection
{
/** The log. */
private Log log;
@Inject
public void init( Log log )
{
this.log = log;
}
@Override
public void connected() throws NGException
{
// TODO Auto-generated method stub
log.info( "IT WORKS!!" );
}
@Override
public void received( byte[] data ) throws NGException
{
// TODO Auto-generated method stub
}
@Override
public void send( String data ) throws NGException
{
// TODO Auto-generated method stub
}
@Override
public void send( byte[] data ) throws NGException
{
// TODO Auto-generated method stub
}
@Override
public void closed() throws NGException
{
// TODO Auto-generated method stub
log.info( "BYE BYE" );
}
}
Last but not least, the way I injected all this in my service:
/** The callback Instance for the driver to find. */
@Inject
@Any
private Instance<TcpConnection> callback;
private TcpConnection driver;
/**
* Inject driver.
*/
private void injectDriver()
{
final TcpDriver driver = new TcpDriverImpl( name );
this.driver = callback.select( driver ).get();
}
I hope this helps somebody having the same requirements I had.
PS: A little log to show it works if you check the log outputs in the test implementation and then look at the log :)
2017-02-28 08:37:00,011 INFO starting TCPConnection: TcpDevice1 with status: NOT_CONNECTED
2017-02-28 08:37:00,018 INFO initializing terminal1
2017-02-28 08:37:00,019 INFO Creating socket for: terminal1 with port: XXXXX
2017-02-28 08:37:00,023 INFO Updated Status to CONNECTED for connection TcpDevice1
2017-02-28 08:37:00,024 INFO opened connection to terminal1
2017-02-28 08:37:00,026 INFO (terminal1) IT WORKS!!
2017-02-28 08:37:00,038 INFO (terminal1) terminal1: In threaded method run
2017-02-28 08:37:00,039 INFO (terminal1) waiting for data...
2017-02-28 08:39:00,045 INFO (terminal1) Socket closed!
2017-02-28 08:39:00,045 INFO (terminal1) BYE BYE