0

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 programmatically lookup and inject a CDI managed bean where the qualifier contains the name of a class

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?

Community
  • 1
  • 1
Nico
  • 1,727
  • 1
  • 24
  • 42

2 Answers2

1

Use CDI events and do not obther with callbacks. Some resources:

https://docs.oracle.com/javaee/7/tutorial/cdi-adv005.htm

http://www.adam-bien.com/roller/abien/entry/java_ee_6_observer_with

http://www.next-presso.com/2014/06/you-think-you-know-everything-about-cdi-events-think-again/

  • Sadly I can't use CDI Events. I originally wanted to do it that way, but since there is expected to be a huge amount of traffic and data going through the application, my recommendation of this got refused. As far as I know events are synchronous so firing an event will cause the programm to wait for all observers to be finished until further code is executed. But what if I need to watch for more incoming data during notifying others that data came in? – Nico Feb 27 '17 at 13:09
  • @Nico : take a look ath this interesting article which explains how you can make CDI Event Asynchronous. It can be a solution for you : http://piotrnowicki.com/2013/05/asynchronous-cdi-events/ – Rouliboy Feb 27 '17 at 14:10
  • Thank you for your article. Really good to know and I will save it, but I would like to think about a solution without using CDI events. Let's say it's not possible because of rejection from someone. My thoughts were about creating a producer in combination with a qualifier somehow. Is that a valid strategy? – Nico Feb 28 '17 at 06:25
  • I have to point out that I don't want and can't be able to know every implementation in my producer. I need some kind of lookup to detect it automatically... – Nico Feb 28 '17 at 06:35
  • Sadly, you are working with an idiot. Seen this many times. In Java EE 8, there will be asynchronous events by default. Nothing left to say. –  Feb 28 '17 at 17:42
  • Well I tried discussing about using event's that are asynchronous in the service since we don't care there what the observer does. If he wants to do something with the service he will tell him. Didn't help...because of this I am stuck at a new problem now that would never exist with using events. I can't wait for Java EE 8 to be honest. :) – Nico Mar 03 '17 at 06:52
0

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
Nico
  • 1,727
  • 1
  • 24
  • 42