AWS Active MQ STOMP Issue - spring

I am having issues with the Amazon MQ implementation of topics. When I use the in memory SimpleMessageBroker clients receive only messages that they have subscribed to. However with the externalized message broker, all clients receive all messages broadcast through the SimpMessagingTemplate.
relevant code:
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
if ("true".equalsIgnoreCase(environment.getProperty("aws.activemq.enabled")))
config.setApplicationDestinationPrefixes("/app")
.enableStompBrokerRelay("/topic")
.setUserDestinationBroadcast("/user")
.setRelayHost(this.activeMQCreds.getHost())
.setRelayPort(this.activeMQCreds.getPort())
.setClientLogin(this.activeMQCreds.getUsername())
.setClientPasscode(this.activeMQCreds.getPassword())
.setAutoStartup(true)
.setSystemLogin(this.activeMQCreds.getUsername())
.setSystemPasscode(this.activeMQCreds.getPassword())
.setTcpClient(this.createClient());
else config.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/topic");
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(webSocketSessionChannelInterceptor);
}
private TcpOperations<byte[]> createClient() {
return new ReactorNettyTcpClient<>(
(client) -> client.remoteAddress(this::getAddress).secure(), new StompReactorNettyCodec());
}
private SocketAddress getAddress() {
try {
InetAddress address =
InetAddress.getByName(this.activeMQCreds.getHost().replace("stomp+ssl://", ""));
return new InetSocketAddress(address, this.activeMQCreds.getPort());
} catch (UnknownHostException e) {
log.error("Exception", e);
return null;
}
}
}
How we are handling subscriptions:
public class WebSocketSessionChannelInterceptor implements ChannelInterceptor {
#Override
public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
log.info("Message: {}", message);
final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
try {
final String destination = accessor.getDestination();
if (StompCommand.CONNECT == accessor.getCommand()) {
final String token = accessor.getFirstNativeHeader("Authorization");
final UsernamePasswordAuthenticationToken user =
webSocketAuthenticatorService.getAuthenticatedOrFail(token);
if (user != null) SecurityContextHolder.getContext().setAuthentication(user);
accessor.setUser(user);
} else if (StompCommand.SUBSCRIBE == accessor.getCommand()
&& StringUtils.isNotEmpty(destination)
&& destination.contains("conversations?orgId=")) {
String orgId = destination.substring(destination.lastIndexOf('=') + 1);
if (WebSocketAuthenticatorService.hasPermissionToOrgId(orgId, (Authentication) accessor.getUser()))
return message;
else
throw new BadCredentialsException(
String.format("you do not have privilege to this organization: %s", orgId));
}
return message;
} catch (Exception e) {
log.error("Exception: ", e);
return null;
}
}
#Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, #Nullable Exception ex) {
final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
final String destination = accessor.getDestination();
if (StompCommand.SUBSCRIBE == accessor.getCommand()
&& StringUtils.isNotEmpty(destination)
&& destination.contains("conversations?orgId=")) {
String orgId = destination.substring(destination.lastIndexOf('=') + 1);
if (WebSocketAuthenticatorService.hasPermissionToOrgId(orgId, (Authentication) accessor.getUser()))
webSocketUtils.publishConversationsForOrgId(orgId);
}
}
}
How we are sending messages to clients:
this.simpMessagingTemplate.convertAndSend(
ApplicationConstants.ORG_WEBSOCKET_TOPIC_PATH + orgId, conversationBO);

Related

Can I change Netty pipeline by events?

My app should do the next:
Send a POST request to server to get the token.
Connect to the websocket using this token in the headers while handshake.
Short question: To activate WebSocketClientProtocolHandler I have to fire event ctx.fireChannelActive() but from channelRead method because in this method I receive token from server . Is it correct place?
I implemented custom ChannelInboundHandlerAdapter and override:
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
authenticator.authenticate(ctx.channel()).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (!channelFuture.isSuccess()) {
authPromise.tryFailure(channelFuture.cause());
ctx.fireExceptionCaught(new RuntimeException("Auth is failed."));
} else {
ctx.fireUserEventTriggered("Auth is successful");
}
}
});
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (!(msg instanceof FullHttpResponse)) {
ctx.fireChannelRead(msg);
}
FullHttpResponse response = (FullHttpResponse) msg;
try {
authenticator.finishAuthentication(ctx.channel(), response);
authPromise.trySuccess();
ctx.pipeline().remove(this);
ctx.fireChannelActive();
} finally {
response.release();
}
}
Authenticator class adds needed handlers, sends POST request and then it should parse response from server and change the pipeline.
public class Authenticator {
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
private final ObjectMapper mapper = new ObjectMapper();
private final MessengerConfig messengerConfig;
public Authenticator(MessengerConfig messengerConfig) {
this.messengerConfig = messengerConfig;
}
public ChannelFuture authenticate(Channel channel) {
this.preCheck(channel);
return this.authenticate(channel, channel.newPromise());
}
private void preCheck(Channel channel) {
ChannelPipeline pipeline = channel.pipeline();
HttpClientCodec httpClientCodec = pipeline.get(HttpClientCodec.class);
if (httpClientCodec == null) {
LOGGER.warn("Pipeline does not contain HttpClientCodec.");
pipeline.addFirst(HttpClientCodec.class.getName(), new HttpClientCodec());
LOGGER.info("HttpClientCodec was added to pipeline.");
}
HttpObjectAggregator httpObjectAggregator = pipeline.get(HttpObjectAggregator.class);
if (httpObjectAggregator == null) {
LOGGER.warn("Pipeline does not contain HttpObjectAggregator.");
pipeline.addAfter(
HttpClientCodec.class.getName(),
HttpObjectAggregator.class.getName(),
new HttpObjectAggregator(8192)
);
LOGGER.info("HttpObjectAggregator was added to pipeline.");
}
}
private ChannelFuture authenticate(Channel channel, ChannelPromise promise) {
HttpRequest request = createAuthRequest();
try {
channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
promise.setSuccess();
} else {
promise.setFailure(new RuntimeException(""));
}
}
});
} catch (Exception e) {
LOGGER.error("Error", e);
}
return promise;
}
public void finishAuthentication(Channel channel, FullHttpResponse response) {
ByteBuf content = response.content();
AuthenticationData authenticationData = null;
try {
authenticationData = this.mapper.readValue(content.toString(CharsetUtil.UTF_8), AuthenticationData.class);
} catch (JsonProcessingException e) {
LOGGER.error("Can't parse authentication data.", e);
throw new RuntimeException((e));
}
LOGGER.info(Objects.toString(authenticationData));
DefaultWebSocketClientProtocolHandlerFactory factory = new DefaultWebSocketClientProtocolHandlerFactory();
WebSocketClientProtocolHandler handler = factory.getHandler(this.messengerConfig, authenticationData);
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(WebSocketClientProtocolHandler.class.getName(), handler);
LOGGER.info("WebSocketClientProtocolHandler was added.");
pipeline.addLast(MessageHandler.class.getName(), new MessageHandler());
LOGGER.info("MessageHandler was added.");
}
So here I have two stages:
Auth stage with a pipeline:
io.netty.handler.codec.http.HttpClientCodec
io.netty.handler.codec.http.HttpObjectAggregator
AuthenticationHandler
2 Web-socket stage with a pipeline:
io.netty.handler.codec.http.HttpClientCodec
io.netty.handler.codec.http.HttpObjectAggregator
io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandshakeHandler
io.netty.handler.codec.http.websocketx.Utf8FrameValidator
io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler
com.github.apsyvenko.client.messaging.MessageHandler
To activate second stage I have to fire event - ctx.fireChannelActive() but from channelRead.
As a result I got an exception:
18:19:37.055 [nioEventLoopGroup-2-1] WARN i.n.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
java.net.SocketException: Connection reset
after hand-shake had started.

Sinch Video clling APi not working when app is cleared from recents

I am using sinch Video calling API for my android application. But when the app is in the background, whenever the user is offline i.e user cleared app from the recent tab, he was unable to get the call. I used Firebase for pushing notification whenever the user is offline, but it is not getting triggered all the time, it is getting triggered sometimes only. can anyone suggest me how to push notification of incoming call when the user cleared the app from recent and will sinch actually run in the background?
public class SinchService extends Service {
private static final String APP_KEY = "key";
private static final String APP_SECRET = "secret";
private static final String ENVIRONMENT = "clientapi.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
static Context context;
private StartFailedListener mListener;
#Override
public void onCreate() {
super.onCreate();
}
/* #Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}*/
private void start(String userName,String fcm_token) {
if (mSinchClient == null) {
mUserId = userName;
/*LooperThread looperThread = new LooperThread();
looperThread.start();*/
/* new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
}
});*/
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(mUserId)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT)
.build();
//Log.d("OnMsg","coming after getSinchServiceInterface()");
mSinchClient.setSupportMessaging(true);
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);
mSinchClient.startListeningOnActiveConnection();
/* mSinchClient.startListeningOnActiveConnection();
mSinchClient.setSupportActiveConnectionInBackground(true);*/
// mSinchClient.setSupportActiveConnectionInBackground(true);
// mSinchClient.startListeningOnActiveConnection();
/* mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);*/
// Log.d("fcm:","fcm token in bytes "+ fcm_token.getBytes());
mSinchClient.checkManifest();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();
// mSinchClient.registerPushNotificationData(fcm_token.getBytes());
}
}
class LooperThread extends Thread {
public Handler mHandler;
#Override
public void run() {
// Initialize the current thread as a Looper
// (this thread can have a MessageQueue now)
Looper.prepare();
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// process incoming messages here
/* mSinchClient.setSupportMessaging(true);
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);
mSinchClient.startListeningOnActiveConnection();
*//* mSinchClient.startListeningOnActiveConnection();
mSinchClient.setSupportActiveConnectionInBackground(true);*//*
// mSinchClient.setSupportActiveConnectionInBackground(true);
// mSinchClient.startListeningOnActiveConnection();
*//* mSinchClient.setSupportManagedPush(true);
mSinchClient.setSupportPushNotifications(true);*//*
// Log.d("fcm:","fcm token in bytes "+ fcm_token.getBytes());
mSinchClient.checkManifest();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();*/
}
};
// Run the message queue in this thread
Looper.loop();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName,String token) {
start(userName,token);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
#Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
/* mSinchClient.terminate();
mSinchClient = null;*/
}
#Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
mSinchClient.startListeningOnActiveConnection();
}
}
#Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
#Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
public class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d("OnMsg", "Incoming call");
Intent intent = new Intent(SinchService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchService.this.startActivity(intent);
}
}
}
public class PlaceCallActivity extends BaseActivity implements SinchService.StartFailedListener/*,PushTokenRegistrationCallback*/{
private Button mCallButton;
private EditText mCallName;
Button stopButton;
String token;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//initializing UI elements
mCallName = findViewById(R.id.callName);
mCallButton = findViewById(R.id.callButton);
mCallButton.setEnabled(false);
mCallButton.setOnClickListener(buttonClickListener);
stopButton = findViewById(R.id.stopButton);
stopButton.setEnabled(false);
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w("test", "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
token = task.getResult().getToken();
// Log and toast
//String msg = getString(R.string.msg_token_fmt, token);
Log.d("test", token);
Toast.makeText(PlaceCallActivity.this, token, Toast.LENGTH_SHORT).show();
}
});
stopButton.setOnClickListener(buttonClickListener);
/* try {
#SuppressLint("WrongThread") String regId = FirebaseInstanceId.getInstance().getToken("Your-Sender-ID", "FCM");
} catch (IOException e) {
e.printStackTrace();
}*/
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
getSinchServiceInterface().startClient(new PreferenceUtils(PlaceCallActivity.this).getName(),token);
}
}, 4000);
}
// invoked when the connection with SinchServer is established
#Override
protected void onServiceConnected() {
TextView userName = (TextView) findViewById(R.id.loggedInName);
getSinchServiceInterface().setStartListener(this);
userName.setText(new PreferenceUtils(PlaceCallActivity.this).getName());
mCallButton.setEnabled(true);
stopButton.setEnabled(true);
}
/* #Override
public void onDestroy() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
super.onDestroy();
}*/
/*//to kill the current session of SinchService
private void stopButtonClicked() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
finish();
}*/
//to place the call to the entered name
private void callButtonClicked() {
String userName = mCallName.getText().toString();
if (userName.isEmpty()) {
Toast.makeText(this, "Please enter a user to call", Toast.LENGTH_LONG).show();
return;
}
Call call = getSinchServiceInterface().callUserVideo(userName);
String callId = call.getCallId();
Log.d("test","call id is"+callId);
Intent callScreen = new Intent(this, CallScreenActivity.class);
callScreen.putExtra(SinchService.CALL_ID, callId);
callScreen.putExtra("TOKEN", token);
startActivity(callScreen);
}
private OnClickListener buttonClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.callButton:
callButtonClicked();
break;
case R.id.stopButton:
// stopButtonClicked();
break;
}
}
};
#Override
public void onStartFailed(SinchError error) {
Toast.makeText(this, error.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onStarted() {
}
}

Spring JDBC insert into DB will not work

For some reason my jdbctemplate is not picking up the info in my application.properties file. I can only insert in my db when I make my own connection exposing my username and password in the file. Why isn't spring boot picking this up? Please help!
I am calling the addTaglocation mode while I am still reading tags from multiple RFID readers. Would this cause the issue? If so how do I solve it. Thanks in advance.
I start reading the tag from multiple rfid readers from a controller.
Full class where I am calling the create method (addTaglocation method):
public class TagInventory extends AlienClass1Reader implements
MessageListener, TagTableListener{
private final int MAX_THREAD = 50;
private Thread[] m_run_process = new Thread[MAX_THREAD];
private AlienReader[] m_inventory = new AlienReader[MAX_THREAD];
public boolean stopInventory = false;
ReaderProfileService rps;
VehicleService vs;
private boolean ThreadStop = true;
private int lastThreadId = -1;
private MessageListenerService service;
private TagTable tagTable = new TagTable();
private TagTableListener tagTableListener;
private static final Logger log =LogManager.getLogger(TagInventory.class);
#Autowired
TaglocationDAOJdbc taglocationDAOJdbc;
public TagInventory() throws AlienReaderException, IOException{
Start();
}
public void stopTag() throws AlienReaderException{
Stop();
}
private void Stop() throws AlienReaderException{
ThreadStop = true;
for (lastThreadId=0; lastThreadId < Reader.ipAddress.length;
lastThreadId++){
if(m_inventory[lastThreadId] != null){
m_inventory[lastThreadId].stopInventory = true;
service.stopService();
try{
Thread.sleep(200);
}catch(Exception e){
e.getMessage();
}
//m_inventory[lastThreadId].close();
m_inventory[lastThreadId].open();
m_inventory[lastThreadId].autoModeReset();
m_inventory[lastThreadId].setAutoMode(AlienClass1Reader.OFF);
m_inventory[lastThreadId].setNotifyMode(AlienClass1Reader.OFF);
m_inventory[lastThreadId].close();
}
}
}
private void Start() throws AlienReaderException, IOException{
ThreadStop = false;
service= new MessageListenerService(3900);
service.setMessageListener(this);
service.startService();
for (lastThreadId = 0; lastThreadId < Reader.ipAddress.length; lastThreadId++)
{
m_inventory[lastThreadId] = new AlienReader(Reader.ipAddress[lastThreadId], Reader.port, Reader.username[lastThreadId], Reader.password[lastThreadId]);
log.info("taginventory reader: "+ Reader.ipAddress[lastThreadId]+"Thread: "+lastThreadId);
m_run_process[lastThreadId] = new Thread(new StartInventoryThread(Reader.ipAddress[lastThreadId], Reader.port, Reader.username[lastThreadId], Reader.password[lastThreadId], m_inventory[lastThreadId]));
m_run_process[lastThreadId].start();
}
--lastThreadId;
try
{
Thread.sleep(1000);
}
catch (Exception ex)
{
ex.getMessage();
}
}
class StartInventoryThread implements Runnable{
private String ip;
private int port;
private String user;
private String pwd;
private AlienReader ar;
StartInventoryThread(String ip, int port, String user, String pwd, AlienReader ar){
this.ip=ip;
this.port=port;
this.user=user;
this.pwd=pwd;
this.ar=ar;
}
#Override
public void run() {
try {
while(!stopInventory){
startRead(ip,port,user,pwd);
}
} catch (AlienReaderException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
#Override
public void tagAdded(Tag tag) {
log.info("NEW TAG: " + tag.getTagID()+ " LAST SEEN DATE: "+ tag.getRenewTime());
}
#Override
public void tagRemoved(Tag tag) {
// TODO Auto-generated method stub
}
#Override
public void tagRenewed(Tag tag) {
// TODO Auto-generated method stub
}
#Override
public synchronized void messageReceived(Message msg) {
if(msg instanceof ErrorMessage){
// log.info("Notify error from " + msg.getReaderIPAddress());
}else if (msg.getTagCount() == 0){
log.info("No tags!");
}else{
//log.info("Message received from: "+msg.getReaderIPAddress());
Tag[] tagL=msg.getTagList();
//String[] tagLString=new String[tagL.length];
for (int i=0;i<msg.getTagCount(); i++){
Tag tag = msg.getTag(i);
//System.out.println("Tag ID: "+tag.getTagID()+ " Last Seen: "+tag.getRenewTime());
this.tagTable.addTag(tag);
// log.info("Tag ID: "+tag.getTagID()+ " Last Seen: "+tag.getRenewTime()+ " Receive Antenna: "+tag.getReceiveAntenna());
//System.out.println("Tag: "+tag+ " Last Seen: "+tag.getRenewTime());
}
}
//check readerprofile
try {
updateLocation(Reader.ipAddress[this.lastThreadId]);
this.tagTable.removeOldTags();
} catch (NoReaderInfoException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoVehicleException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void addTaglocation(Taglocation tl){
Taglocation newtagloca= new Taglocation(tl.getReadername(),tl.getRfidtag(),tl.getZone(), tl.getTaggingsource(), tl.getVineight(), tl.getZonedate());
taglocationDAOJdbc.addtaglocation(newtagloca);
log.info("ADDED TAGG INFO!");
}
public void updateLocation(String read) throws NoReaderInfoException, NoVehicleException{
//update location of vehicle when tag read by readers
log.info("IN UPDATE LOCATION with reader: "+read);
Tag[] temp;
temp=this.tagTable.getTagList();
for (int i = 0; i < temp.length; i++) {
log.info("get taglist");
Tag tag = temp[i];
String rfid=tag.getTagID();
Timestamp newDate=new Timestamp(Calendar.getInstance().getTime().getTime());
Taglocation tl=new Taglocation(read, rfid, read, "", "vineight", newDate);
addTaglocation(tl);
}
}
public void startRead(String ip, int port, String user, String password) throws AlienReaderException, InterruptedException, UnknownHostException{
String myIP=InetAddress.getLocalHost().getHostAddress();
System.out.println("ip"+ ip);
AlienReader ar= new AlienReader(ip, port, user, password);
ar.open();
log.info("Reader" + ar.getIPAddress());
ar.setNotifyAddress(myIP, 3900);
ar.setNotifyFormat(AlienClass1Reader.TEXT_FORMAT);
ar.setNotifyTrigger("TrueFalse");
ar.setNotifyMode(AlienClass1Reader.ON);
ar.autoModeReset();
ar.setAutoStopTimer(5000); // Read for 5 seconds
ar.setAutoMode(AlienClass1Reader.ON);
tagTable.setTagTableListener(tagTableListener);
tagTable.setPersistTime(5000);
//tagTable.setPersistTime(1800000);
ar.close();
long runTime = 10000; // milliseconds
long startTime = System.currentTimeMillis();
do {
Thread.sleep(1000);
} while(service.isRunning()
&& (System.currentTimeMillis()-startTime) < runTime);
// Reconnect to the reader and turn off AutoMode and TagStreamMode.
log.info("\nResetting Reader");
ar.open();
ar.autoModeReset();
ar.setNotifyMode(AlienClass1Reader.OFF);
ar.close();
}
}
(UPDATED)
Application.properties file:
spring.datasource.url=jdbc:mysql://localhost:3306/DB
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
(UPDATED)
taglocationJDBCTemplate file
#Repository
public class TaglocationJDBCTemplate implements TaglocationDAO {
#Autowired
JdbcTemplate jdbcTemplate;
public void create(Taglocation tl){
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator(){
public PreparedStatement createPreparedStatement(final Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement("INSERT INTO TAGLOCATION (READERNAME, RFIDTAG, TAGGINGSOURCE, ZONE, VINEIGHT, ZONEDATE) VALUES (?,?,?,?,?,?)",
Statement.RETURN_GENERATED_KEYS);
ps.setString(1, tl.getReadername());
ps.setString(2,tl.getRfidtag());
ps.setString(3, tl.getTaggingsource());
ps.setString(4, tl.getZone());
ps.setString(5, tl.getVineight());
ps.setTimestamp(6, new Timestamp(System.currentTimeMillis()));
return ps;
}
}, keyHolder);
return (Integer) keyHolder.getKey();
}
controller file:
#RestController
public class TagInventoryController {
#Autowired
TagService tagService;
#RequestMapping("/runTasks")
public String tagRead(){
tagService.startTagRead();
return "Readers are activated.";
}
}

How to use Netty's channel pool map as a ConnectorProvider for a Jax RS client

I have wasted several hours trying to solve a issue with the use of netty's channel pool map and a jax rs client.
I have used jersey's own netty connector as an inspiration but exchanged netty's channel with netty's channel pool map.
https://jersey.github.io/apidocs/2.27/jersey/org/glassfish/jersey/netty/connector/NettyConnectorProvider.html
My problem is that I have references that I need inside my custom SimpleChannelInboundHandler. However by the design of netty's way to create a channel pool map, I can not pass the references through my custom ChannelPoolHandler, because as soon as the pool map has created a pool the constructor of the channel pool handler never runs again.
This is the method where it makes acquires a pool and check out a channel to make a HTTP request.
#Override
public Future<?> apply(ClientRequest request, AsyncConnectorCallback callback) {
final CompletableFuture<Object> completableFuture = new CompletableFuture<>();
try{
HttpRequest httpRequest = buildHttpRequest(request);
// guard against prematurely closed channel
final GenericFutureListener<io.netty.util.concurrent.Future<? super Void>> closeListener =
future -> {
if (!completableFuture.isDone()) {
completableFuture.completeExceptionally(new IOException("Channel closed."));
}
};
try {
ClientRequestDTO clientRequestDTO = new ClientRequestDTO(NettyChannelPoolConnector.this, request, completableFuture, callback);
dtoMap.putIfAbsent(request.getUri(), clientRequestDTO);
// Retrieves a channel pool for the given host
FixedChannelPool pool = this.poolMap.get(clientRequestDTO);
// Acquire a new channel from the pool
io.netty.util.concurrent.Future<Channel> f = pool.acquire();
f.addListener((FutureListener<Channel>) futureWrite -> {
//Succeeded with acquiring a channel
if (futureWrite.isSuccess()) {
Channel channel = futureWrite.getNow();
channel.closeFuture().addListener(closeListener);
try {
if(request.hasEntity()) {
channel.writeAndFlush(httpRequest);
final JerseyChunkedInput jerseyChunkedInput = new JerseyChunkedInput(channel);
request.setStreamProvider(contentLength -> jerseyChunkedInput);
if(HttpUtil.isTransferEncodingChunked(httpRequest)) {
channel.write(jerseyChunkedInput);
} else {
channel.write(jerseyChunkedInput);
}
executorService.execute(() -> {
channel.closeFuture().removeListener(closeListener);
try {
request.writeEntity();
} catch (IOException ex) {
callback.failure(ex);
completableFuture.completeExceptionally(ex);
}
});
channel.flush();
} else {
channel.closeFuture().removeListener(closeListener);
channel.writeAndFlush(httpRequest);
}
} catch (Exception ex) {
System.err.println("Failed to sync and flush http request" + ex.getLocalizedMessage());
}
pool.release(channel);
}
});
} catch (NullPointerException ex) {
System.err.println("Failed to acquire socket from pool " + ex.getLocalizedMessage());
}
} catch (Exception ex) {
completableFuture.completeExceptionally(ex);
return completableFuture;
}
return completableFuture;
}
This is my ChannelPoolHandler
public class SimpleChannelPoolHandler implements ChannelPoolHandler {
private ClientRequestDTO clientRequestDTO;
private boolean ssl;
private URI uri;
private int port;
SimpleChannelPoolHandler(URI uri) {
this.uri = uri;
if(uri != null) {
this.port = uri.getPort() != -1 ? uri.getPort() : "https".equals(uri.getScheme()) ? 443 : 80;
ssl = "https".equalsIgnoreCase(uri.getScheme());
}
}
#Override
public void channelReleased(Channel ch) throws Exception {
System.out.println("Channel released: " + ch.toString());
}
#Override
public void channelAcquired(Channel ch) throws Exception {
System.out.println("Channel acquired: " + ch.toString());
}
#Override
public void channelCreated(Channel ch) throws Exception {
System.out.println("Channel created: " + ch.toString());
int readTimeout = Integer.parseInt(ApplicationEnvironment.getInstance().get("READ_TIMEOUT"));
SocketChannelConfig channelConfig = (SocketChannelConfig) ch.config();
channelConfig.setConnectTimeoutMillis(2000);
ChannelPipeline channelPipeline = ch.pipeline();
if(ssl) {
SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
channelPipeline.addLast("ssl", sslContext.newHandler(ch.alloc(), uri.getHost(), this.port));
}
channelPipeline.addLast("client codec", new HttpClientCodec());
channelPipeline.addLast("chunked content writer",new ChunkedWriteHandler());
channelPipeline.addLast("content decompressor", new HttpContentDecompressor());
channelPipeline.addLast("read timeout", new ReadTimeoutHandler(readTimeout, TimeUnit.MILLISECONDS));
channelPipeline.addLast("business logic", new JerseyNettyClientHandler(this.uri));
}
}
And this is my SimpleInboundHandler
public class JerseyNettyClientHandler extends SimpleChannelInboundHandler<HttpObject> {
private final NettyChannelPoolConnector nettyChannelPoolConnector;
private final LinkedBlockingDeque<InputStream> isList = new LinkedBlockingDeque<>();
private final AsyncConnectorCallback asyncConnectorCallback;
private final ClientRequest jerseyRequest;
private final CompletableFuture future;
public JerseyNettyClientHandler(ClientRequestDto clientRequestDTO) {
this.nettyChannelPoolConnector = clientRequestDTO.getNettyChannelPoolConnector();
ClientRequestDTO cdto = clientRequestDTO.getNettyChannelPoolConnector().getDtoMap().get(clientRequestDTO.getClientRequest());
this.asyncConnectorCallback = cdto.getCallback();
this.jerseyRequest = cdto.getClientRequest();
this.future = cdto.getFuture();
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if(msg instanceof HttpResponse) {
final HttpResponse httpResponse = (HttpResponse) msg;
final ClientResponse response = new ClientResponse(new Response.StatusType() {
#Override
public int getStatusCode() {
return httpResponse.status().code();
}
#Override
public Response.Status.Family getFamily() {
return Response.Status.Family.familyOf(httpResponse.status().code());
}
#Override
public String getReasonPhrase() {
return httpResponse.status().reasonPhrase();
}
}, jerseyRequest);
for (Map.Entry<String, String> entry : httpResponse.headers().entries()) {
response.getHeaders().add(entry.getKey(), entry.getValue());
}
if((httpResponse.headers().contains(HttpHeaderNames.CONTENT_LENGTH) && HttpUtil.getContentLength(httpResponse) > 0) || HttpUtil.isTransferEncodingChunked(httpResponse)) {
ctx.channel().closeFuture().addListener(future -> isList.add(NettyInputStream.END_OF_INPUT_ERROR));
response.setEntityStream(new NettyInputStream(isList));
} else {
response.setEntityStream(new InputStream() {
#Override
public int read() {
return -1;
}
});
}
if(asyncConnectorCallback != null) {
nettyChannelPoolConnector.executorService.execute(() -> {
asyncConnectorCallback.response(response);
future.complete(response);
});
}
}
if(msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
ByteBuf byteContent = content.content();
if(byteContent.isReadable()) {
byte[] bytes = new byte[byteContent.readableBytes()];
byteContent.getBytes(byteContent.readerIndex(), bytes);
isList.add(new ByteArrayInputStream(bytes));
}
}
if(msg instanceof LastHttpContent) {
isList.add(NettyInputStream.END_OF_INPUT);
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if(asyncConnectorCallback != null) {
nettyChannelPoolConnector.executorService.execute(() -> asyncConnectorCallback.failure(cause));
}
future.completeExceptionally(cause);
isList.add(NettyInputStream.END_OF_INPUT_ERROR);
}
The references needed to be passed to the SimpleChannelInboundHandler is what is packed into the ClientRequestDTO as seen in the first code block.
I am not sure as it is not a tested code. But it could be achieved by the following code.
SimpleChannelPool sPool = poolMap.get(Req.getAddress());
Future<Channel> f = sPool.acquire();
f.get().pipeline().addLast("inbound", new NettyClientInBoundHandler(Req, jbContext, ReportData));
f.addListener(new NettyClientFutureListener(this.Req, sPool));
where Req, jbContext, ReportData could be input data for InboundHandler().

Building a Future API on top of Netty

I want to build an API based on Futures (from java.util.concurrent) that is powered by a custom protocol on top of Netty (version 4). Basic idea is to write a simple library that would abstract the underlying Netty implementation and make it easier to make requests.
Using this library, one should be able to write something like this:
Request req = new Request(...);
Future<Response> responseFuture = new ServerIFace(host, port).call(req);
// For example, let's block until this future is resolved
Reponse res = responseFuture.get().getResult();
Underneath this code, a Netty client is connected
public class ServerIFace {
private Bootstrap bootstrap;
private EventLoopGroup workerGroup;
private String host;
private int port;
public ServerIFace(String host, int port) {
this.host = host;
this.port = port;
this.workerGroup = new NioEventLoopGroup();
bootstrap();
}
private void bootstrap() {
bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Response.class.getClassLoader())));
ch.pipeline().addLast("response", new ResponseReceiverChannelHandler());
}
});
}
public Future<Response> call(final Request request) throws InterruptedException {
CompletableFuture<Response> responseFuture = new CompletableFuture<>();
Channel ch = bootstrap.connect(host, port).sync().channel();
ch.writeAndFlush(request).addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Wrote successfully");
} else {
f.cause().printStackTrace();
}
});
ChannelFuture closeFuture = ch.closeFuture();
// Have to 'convert' ChannelFuture to java.util.concurrent.Future
closeFuture.addListener((f) -> {
if (f.isSuccess()) {
// How to get this response?
Response response = ((ResponseReceiverChannelHandler) ch.pipeline().get("response")).getResponse();
responseFuture.complete(response);
} else {
f.cause().printStackTrace();
responseFuture.cancel(true);
}
ch.close();
}).sync();
return responseFuture;
}
}
Now, as you can see, in order to abstract Netty's inner ChannelFuture, I have to 'convert' it to Java's Future (I'm aware that ChannelFuture is derived from Future, but that information doesn't seem useful at this point).
Right now, I'm capturing this Response object in the last handler of my inbound part of the client pipeline, the ResponseReceiverChannelHandler.
public class ResponseReceiverChannelHandler extends ChannelInboundHandlerAdapter {
private Response response = null;
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
this.response = (Response)msg;
ctx.close();
}
public Response getResponse() {
return response;
}
}
Since I'm new to Netty and these things in general, I'm looking for a cleaner, thread-safe way of delivering this object to the API user.
Correct me if I'm wrong, but none of the Netty examples show how to achieve this, and most of the Client examples just print out whatever they get from Server.
Please note that my main goal here is to learn more about Netty, and that this code has no production purposes.
For the reference (although I don't think it's that relevant) here's the Server code.
public class Server {
public static class RequestProcessorHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ChannelFuture future;
if (msg instanceof Request) {
Request req = (Request)msg;
Response res = some function of req
future = ctx.writeAndFlush(res);
} else {
future = ctx.writeAndFlush("Error, not a request!");
}
future.addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Response sent!");
} else {
System.out.println("Response not sent!");
f.cause().printStackTrace();
}
});
}
}
public int port;
public Server(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Request.class.getClassLoader())));
ch.pipeline().addLast(new ObjectEncoder());
// Not really shutting down this threadpool but it's ok for now
ch.pipeline().addLast(new DefaultEventExecutorGroup(2), new RequestProcessorHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new Server(port).run();
}
}

Resources