I have a Java class that -upon a certain action from the GUI- initiates a connection with the RabbitMQ server (using the pub/sub patter) and listens for new events.
I want to add a new feature where I will allow the user to set an "end time" that will stop my application from listening to new events (stop consuming from the queue without closing it).
I tried to utilise the basicCancel method, but I can't find a way to make it work for a predefined date.
Would it be a good idea to initiate a new thread inside my Subscribe class that will call the basicCancel upon reaching the given date or is there a better way to do that?
Listen to new events
private void listenToEvents(String queueName) {
try {
logger.info(" [*] Waiting for events. Subscribed to : " + queueName);
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
TypeOfEvent event = null;
String message = new String(body);
// process the payload
InteractionEventManager eventManager = new InteractionEventManager();
event = eventManager.toCoreMonitorFormatObject(message);
if(event!=null){
String latestEventOpnName = event.getType().getOperationMessage().getOperationName();
if(latestEventOpnName.equals("END_OF_PERIOD"))
event.getMessageArgs().getContext().setTimestamp(++latestEventTimeStamp);
latestEventTimeStamp = event.getMessageArgs().getContext().getTimestamp();
ndaec.receiveTypeOfEventObject(event);
}
}
};
channel.basicConsume(queueName, true, consumer);
//Should I add the basicCancel here?
}
catch (Exception e) {
logger.info("The Monitor could not reach the EventBus. " +e.toString());
}
}
Initiate Connection
public String initiateConnection(Timestamp endTime) {
Properties props = new Properties();
try {
props.load(new FileInputStream(everestHome+ "/monitoring-system/rabbit.properties"));
}catch(IOException e){
e.printStackTrace();
}
RabbitConfigure config = new RabbitConfigure(props,props.getProperty("queuName").trim());
ConnectionFactory factory = new ConnectionFactory();
exchangeTopic = new HashMap<String,String>();
String exchangeMerged = config.getExchange();
logger.info("Exchange=" + exchangeMerged);
String[] couples = exchangeMerged.split(";");
for(String couple : couples)
{
String[] infos = couple.split(":");
if (infos.length == 2)
{
exchangeTopic.put(infos[0], infos[1]);
}
else
{
logger.error("Invalid Exchange Detail: " + couple);
}
}
for(Entry<String, String> entry : exchangeTopic.entrySet()) {
String exchange = entry.getKey();
String topic = entry.getValue();
factory.setHost(config.getHost());
factory.setPort(Integer.parseInt(config.getPort()));
factory.setUsername(config.getUsername());
factory.setPassword(config.getPassword());
try {
connection1= factory.newConnection();
channel = connection1.createChannel();
channel.exchangeDeclare(exchange, EXCHANGE_TYPE);
/*Map<String, Object> args = new HashMap<String, Object>();
args.put("x-expires", endTime.getTime());*/
channel.queueDeclare(config.getQueue(),false,false,false,null);
channel.queueBind(config.getQueue(),exchange,topic);
logger.info("Connected to RabbitMQ.\n Exchange: " + exchange + " Topic: " + topic +"\n Queue Name is: "+ config.getQueue());
return config.getQueue();
} catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
} catch (TimeoutException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}
return null;
}
You can create a delayed queue, setting the time-to-leave so the message you push there will be dead-lettered exactly as soon as you want to stop your consumer.
Then you have to bind the dead letter exchange to a queue whose consumer will stop the other one as soon as it gets the message.
Never use threads when you have RabbitMq, you can do a lot of interesting stuff with delayed messages!
Related
I have a queue on Rabbitmq and each time I run my code the queue on the rebbitmq website is empty. I think it's because each time the basicConsume fetches all of the elements at once.
I need help in a way to get an element one at a time.
this is the function to add to queue:
public static void addToQueueRabbit(String qname, String id) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
//If we wanted to connect to a node on a different machine we'd simply specify its hostname or IP address here
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
//Declaring a queue is idempotent - it will only be created if it doesn't exist already.
channel.queueDeclare(qname, false, false, false, null);
String message = id;
channel.basicPublish("", qname, null, message.getBytes());
System.out.println( "Added to queue: "+qname);
}
}
this code is how the queue build:
public class waitingQueueConsumer implements Runnable {
private ConnectionFactory factory;
private Connection connection;
private Channel channel;
public waitingQueueConsumer() throws TimeoutException, IOException {
factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
//declare the queue from which we're going to consume
channel.queueDeclare("waitingQueue", false, false, false, null);
// System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
System.out.println("init waitingQueueConsumer DONE");
}
public synchronized void run() {
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
//spliited to ID:permit:token
String[] splitted = message.split(":");
int permit = Integer.parseInt(splitted[1]);
String token = splitted[2];
System.out.println("permit: "+permit);
permit--;
//update the permit
message = splitted[0]+":"+permit+":"+token;
//TODO send the api request.
String id = splitted[0];
//to change to production when ready
apiService APICall= new apiService(id,token);
Boolean isUrlExist = null;
boolean isUrlExistArray[]=null;
boolean isMotobiEmpty= false;
Boolean tokenExpired = false;
JSONObject jsonObject=null;
try {
jsonObject = APICall.send();
if (jsonObject.get("data") instanceof JSONArray) {
tokenExpired=true;
}
else {
isUrlExistArray = APICall.checkForCsvLink(jsonObject);
if(isUrlExistArray[0]== true && isUrlExistArray[1]==false){
String urlMotobi = APICall.returnMotobiUrl(jsonObject);
csvCompare csvUrl = new csvCompare(urlMotobi, "");
isMotobiEmpty= csvUrl.checkIfReportEmpty(urlMotobi);
}
isUrlExist = isUrlExistArray[0] && isUrlExistArray[1];
}
} catch(ParseException e){
e.printStackTrace();
} catch(InterruptedException e){
e.printStackTrace();
}
try {
if(tokenExpired == true){
buildMail mymail = new buildMail(jsonObject);
mymail.mailForTokenExpired(id, token);
}
else if (isMotobiEmpty == true){
queueUtils.addToQueueRabbit("comparingQueue", "equal&" + id);
}
else if (isUrlExist) {
//the report is ready lets take the urls
//url1&url2&id
String urls = "";
urls = APICall.returnUrls(jsonObject);
APICall.setMailInfo(jsonObject);
queueUtils.addToQueueRabbit("comparingQueue", urls + "&" + id);
} else if (permit > 0) {
System.out.println("waiting 30 secs");
queueUtils.addToQueueRabbit("waitingQueue", message);
sleep(30000);
} else {
//inc the reports we handled
TestServiceApplication.compareReportsCounter++;
int num = TestServiceApplication.compareReportsCounter;
buildMail mymail = new buildMail(jsonObject);
mymail.mailForUnreadyUrl(APICall, isUrlExistArray, id);
}
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
try {
channel.basicConsume("waitingQueue", true, deliverCallback, consumerTag -> { });
} catch (IOException e) {
e.printStackTrace();
}
}
}
i would like for help in explanation how to get only the fist element from rabbitmq and if its possible at all.
I am trying to find a bug is some RabbitMQ client code that was developed six or seven years ago. The code was modified to allow for delayed messages. It seems that connections are created to the RabbitMQ server and then never destroyed. Each exists in a separate thread so I end up with 1000's of threads. I am sure the problem is very obvious / simple - but I am having trouble seeing it. I have been looking at the exchangeDeclare method (the commented out version is from the original code which seemed to work), but I have been unable to find the default values for autoDelete and durable which are being set in the modified code. The method below in within a Spring service class. Any help, advice, guidance and pointing out huge obvious errors appreciated!
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "fanout");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-delay", 10000); //delay in miliseconds i.e 10secs
AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
Connection connection = null;
Channel channel = null;
try {
connection = myConnection.getConnection();
}
catch(Exception e) {
log.error("AMQP send method Exception. Unable to get connection.");
e.printStackTrace();
return;
}
try {
if (connection != null) {
log.debug(" [CORE: AMQP] Sending message with key {} : {}",routingKey, message);
channel = connection.createChannel();
// channel.exchangeDeclare(exchange, exchangeType);
channel.exchangeDeclare(exchange, "x-delayed-message", true, false, args);
// channel.basicPublish(exchange, routingKey, null, message.getBytes());
channel.basicPublish(exchange, routingKey, props.build(), message.getBytes());
}
else {
log.error("Total AMQP melt down. This should never happen!");
}
}
catch(Exception e) {
log.error("AMQP send method Exception. Unable to get send.");
e.printStackTrace();
}
finally {
channel.close();
}
}
This is the connection class
#Service
public class PersistentConnection {
private static final Logger log = LoggerFactory.getLogger(PersistentConnection.class);
private static Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
log.error("Unable to close AMQP Connection.");
e.printStackTrace();
}
}
public Connection getConnection( ) {
if (myConnection == null) {
start();
}
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String user = applicationConfiguration.getAMQPUser();
String password = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
log.error("Unable to initialise AMQP Connection.");
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
log.warn("Message Server has blocked. It may be resource limitted.");
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
log.warn("Message server is unblocked.");
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
I am trying to recieve SNMP v3 traps from a device, using Adventnet.
When getting a trap I see the following AdventNet exception thrown:
Exception while constructing message after receiving PDU. Dropping this PDU received from xxx.xxx.xxx.xxx. com.adventnet.snmp.snmp2.SnmpException: Parse Header: Incorrect Scoped data
If I monitor the traps using NG-Soft browser the traps are recieved correctly.
Here is my code:
private void initV3Parameters(NEData neData) throws InterruptedException
{
logger.debug("in.");
try
{
logger.debug(".in");
SnmpAPI m_api = new SnmpAPI();
m_api.setDebug( true );
SnmpSession m_session = new SnmpSession(m_api);
m_session.addSnmpClient(this);
UDPProtocolOptions m_udpOpt = new UDPProtocolOptions();
m_udpOpt.setRemoteHost(neData.m_szIpAddress);
m_session.setProtocolOptions(m_udpOpt);
try
{
m_session.open();
String message="Succes to bind port: "+session.getLocalPort();
logger.info(message);
System.out.println(message);
}
catch (Exception ex)
{
String message = "Failed to open session - Port in use or permission denied. \n Message- "+ ex.getMessage() + "\n Will exit from Trap process. ";
logger.error(message, ex);
System.err.println(message);
throw new RuntimeException(message);
}
SnmpEngineEntry engineentry = new SnmpEngineEntry(neData.m_szIpAddress, m_udpOpt.getRemotePort());
SnmpEngineTable enginetable = m_api.getSnmpEngine();
enginetable.addEntry(engineentry);
try
{
engineentry.discoverSnmpEngineID(m_session,10000,3);
}
catch (Exception e)
{
logger.error("Failed to discover snmp EngineID. " + e.getMessage());
printToLog("failed",neData);
return;
}
USMUserEntry entry = new USMUserEntry(neData.usmUser.getBytes(), engineentry.getEngineID());
entry.setAuthProtocol(Integer.parseInt(neData.authProtocol));
entry.setAuthPassword(neData.authPassword.getBytes());
entry.setPrivProtocol(Integer.parseInt(neData.privProtocol));
entry.setPrivPassword(neData.privPassword.getBytes());
byte[] authKey = USMUtils.password_to_key(entry.getAuthProtocol(),
neData.authPassword.getBytes(),
neData.authPassword.getBytes().length,
engineentry.getEngineID());
entry.setAuthKey(authKey);
byte[] privKey = USMUtils.password_to_key(entry.getAuthProtocol(),
neData.privPassword.getBytes(),
neData.privPassword.getBytes().length,
engineentry.getEngineID());
entry.setPrivKey(privKey);
entry.setEngineEntry(engineentry);
entry.setSecurityLevel(Snmp3Message.AUTH_PRIV);
SecurityProvider provider = m_api.getSecurityProvider();
USMUserTable userTable = (USMUserTable) provider.getTable(3);
userTable.addEntry(entry);
entry.timeSynchronize(m_session, m_udpOpt);
printToLog("success",neData);
}
catch (Exception exp)
{
logger.error(exp.getMessage()+" for ip = "+neData.m_szIpAddress,exp);
discoveredDeque.put(neData);
printToLog("failed",neData);
}
}
I've also tried Using High-Level API
USMUtils.init_v3_parameters(
neData.usmUser,
null,
Integer.valueOf(neData.authProtocol),
neData.authPassword,
neData.privPassword,
udpOptions,
session,
false,
Integer.valueOf(neData.privProtocol));
In this case I see the trap using public void debugPrint (String debugOutput)
and no exception is throwing.
But there is nothing in the callback
Any advice will be welcome!!!
It turns out that there was a problem with the time synchronization of the device that sends the traps and my code worked perfectly fine.
probably NG-Soft doesn't care from time sync...
I attach my code here in case any of you will need it in the future...
private SnmpSession session;
/**
* Create a listener for trap version 1-2
*/
public void trapsListener ()
{
logger.debug(".in");
SnmpAPI api = new SnmpAPI();
// api.setDebug( true );
session = new SnmpSession(api);
session.addSnmpClient(this);
UDPProtocolOptions udpOpt = new UDPProtocolOptions();
udpOpt.setLocalPort(TRAP_PORT);
session.setProtocolOptions(udpOpt);
try
{
session.open();
String message="Succes to bind port: "+session.getLocalPort();
logger.info(message);
System.out.println(message);
}
catch (Exception ex)
{
String message = "Failed to open session - Port in use or permission denied. \n Message- "+ ex.getMessage() + "\n Will exit from Trap process. ";
logger.error(message, ex);
System.err.println(message);
throw new RuntimeException(message);
}
}
/**
* For each new device
* 1) discover the snmp engineID
* 2) create SnmpEngineEntry and add it to SnmpEngineTable
* 3) create USMUserEntry and add it to USMUserTable
* 4) performs time synchronization
**/
private void initV3Parameters(Device data) throws InterruptedException
{
logger.debug("in.");
try
{
UDPProtocolOptions udpOptions = new UDPProtocolOptions();
udpOptions.setLocalPort(TRAP_PORT);
udpOptions.setRemoteHost(data.getIpAddress());
USMUtils.init_v3_parameters(
data.getUsmUser(),
null,// null means that the SNMPv3 discovery will be activated
Integer.valueOf(data.getAuthProtocol()),
data.getAuthPassword(),
data.getPrivPassword(),
udpOptions,
session,
false,
Integer.valueOf(data.getPrivProtocol()));
printToLog("secsses",data);
}
catch (SnmpException exp) {
logger.error(exp.getMessage()+" for ip = "+data.getIpAddress(),exp);
printToLog("failed",data);
}
}
I'm trying to add the option to get a TRAP V3 (I can get TRAP V1 and V2).
here is the Session init
public void CreateSession()
{
logger.debug(".in");
System.out.println("Waiting to receive traps .......");
api = new SnmpAPI();
api.setDebug( true );
session = new SnmpSession(api);
session.addSnmpClient(this);
udpOpt = new UDPProtocolOptions();
udpOpt.setLocalPort(TRAP_PORT);
session.setProtocolOptions(udpOpt);
try
{
session.open();
String message="Succes to bind port: "+session.getLocalPort();
logger.info(message);
System.out.println(message);
}
catch (Exception ex)
{
String message = "Failed to open session - Port in use or permission denied. \n Message- "+ ex.getMessage() + "\n Will exit from Trap process. ";
logger.error(message, ex);
System.err.println(message);
throw new RuntimeException(message);
}
}
Every time I set up a device in V3 I perform the discovery and its registration to the tables using the method USMUtils.init_v3_parameters.
private void initV3Parameters(NEData neData) throws InterruptedException
{
logger.debug("in.");
try
{
UDPProtocolOptions udpOptions = new UDPProtocolOptions();
udpOptions.setLocalPort(TRAP_PORT);
udpOptions.setRemoteHost(neData.m_szIpAddress);
USMUtils.init_v3_parameters(
neData.usmUser,
null,
Integer.valueOf(neData.authProtocol),
neData.authPassword,
neData.privPassword,
udpOptions,
session,
false,
Integer.valueOf(neData.privProtocol));
printToLog("success",neData);
}
catch (SnmpException exp) {
logger.error(exp.getMessage()+" for ip = "+neData.m_szIpAddress,exp);
discoveredDeque.put(neData);
printToLog("failed",neData);
}
}
When TRAP V1 or V2 arrives it comes to this function and it works perfect.But I want to add the V3 as well
/**
* Receives incoming PDUs and adds them to the traps queue. Notifies the
* internal thread on the arrival of each PDU.
*/
public boolean callback (SnmpSession session, SnmpPDU pdu, int requestID)
{
logger.info("in session= "+session+" ,pdu= "+pdu+" ,requestID = "+requestID);
if (pdu == null)
{
logger.info("Received null PDU");
}
else
{
// Add the PDU to the end of the traps queue.
// Notify the internal thread.
try
{
synchronized (this)
{
Object t = m_Traps.addElement(pdu);
if (t != null)
{
// The new trap replaced an old one.
logger.info("Queue full: replaced old PDU");
}
notify();
}
}
catch (Exception ex)
{
logger.error(ex.getMessage(),ex);
}
}
// All PDUs are handled, so true should always be returned.
return true;
}
I have debugPrint implementation from SnmpClient and it seems that AdventNet gets the TRAP but does not do anything with it...
The SNMP agent is implemented using NetSNMP.
What is missing to make AdventNet handle the V3 TRAP?
Is this the right callback function to use?
public boolean callback (SnmpSession session, SnmpPDU pdu, int requestID)
I am writing a code to send a UDP Multicast over Wifi from my mobile device. There is a server code running on other devices in the network. The servers will listen to the multicast and respond with their IP Address and Type of the system (Type: Computer, Mobile Device, Raspberry Pi, Flyports etc..)
On the mobile device which has sent the UDP Multicast, I need to get the list of the devices responding to the UDP Multicast.
For this I have created a class which will work as the structure of the device details.
DeviceDetails.class
public class DeviceDetails
{
String DeviceType;
String IPAddr;
public DeviceDetails(String type, String IP)
{
this.DeviceType=type;
this.IPAddr=IP;
}
}
I am sending the UDP Multicast packet at the group address of 225.4.5.6 and Port Number 5432.
I have made a class which will call a thread which will send the UDP Packets. And on the other hand I have made a receiver thread which implements Callable Interface to return the list of the devices responding.
Here is the code:
MulticastReceiver.java
public class MulticastReceiver implements Callable<DeviceDetails>
{
DatagramSocket socket = null;
DatagramPacket inPacket = null;
boolean check = true;
public MulticastReceiver()
{
try
{
socket = new DatagramSocket(5500);
}
catch(Exception ioe)
{
System.out.println(ioe);
}
}
#Override
public DeviceDetails call() throws Exception
{
// TODO Auto-generated method stub
try
{
byte[] inBuf = new byte[WifiConstants.DGRAM_LEN];
//System.out.println("Listening");
inPacket = new DatagramPacket(inBuf, inBuf.length);
if(check)
{
socket.receive(inPacket);
}
String msg = new String(inBuf, 0, inPacket.getLength());
Log.v("Received: ","From :" + inPacket.getAddress() + " Msg : " + msg);
DeviceDetails device = getDeviceFromString(msg);
Thread.sleep(100);
return device;
}
catch(Exception e)
{
Log.v("Receiving Error: ",e.toString());
return null;
}
}
public DeviceDetails getDeviceFromString(String str)
{
String type;
String IP;
type=str.substring(0,str.indexOf('`'));
str = str.substring(str.indexOf('`')+1);
IP=str;
DeviceDetails device = new DeviceDetails(type,IP);
return device;
}
}
The following code is of the activity which calls the Receiver Thread:
public class DeviceManagerWindow extends Activity
{
public void searchDevice(View view)
{
sendMulticast = new Thread(new MultiCastThread());
sendMulticast.start();
ExecutorService executorService = Executors.newFixedThreadPool(1);
List<Future<DeviceDetails>> deviceList = new ArrayList<Future<DeviceDetails>>();
Callable<DeviceDetails> device = new MulticastReceiver();
Future<DeviceDetails> submit = executorService.submit(device);
deviceList.add(submit);
DeviceDetails[] devices = new DeviceDetails[deviceList.size()];
int i=0;
for(Future<DeviceDetails> future :deviceList)
{
try
{
devices[i] = future.get();
}
catch(Exception e)
{
Log.v("future Exception: ",e.toString());
}
}
}
}
Now the standard way of receiving the packet says to call the receive method under an infinite loop. But I want to receive the incoming connections only for first 30seconds and then stop looking for connections.
This is similar to that of a bluetooth searching. It stops after 1 minute of search.
Now the problem lies is, I could use a counter but the problem is thread.stop is now depricated. And not just this, if I put the receive method under infinite loop it will never return the value.
What should I do.? I want to search for say 30 seconds and then stop the search and want to return the list of the devices responding.
Instead of calling stop(), you should call interrupt(). This causes a InterruptedException to be thrown at interruptable spots at your code, e.g. when calling Thread.sleep() or when blocked by an I/O operation. Unfortunately, DatagramSocket does not implement InterruptibleChannel, so the call to receive cannot be interrupted.
So you either use DatagramChannel instead of the DatagramSocket, such that receive() will throw a ClosedByInterruptException if Thread.interrupt() is called. Or you need to set a timeout by calling DatagramSocket.setSoTimeout() causing receive() to throw a SocketTimeoutException after the specified interval - in that case, you won't need to interrupt the thread.
Simple approach
The easiest way would be to simply set a socket timeout:
public MulticastReceiver() {
try {
socket = new DatagramSocket(5500);
socket.setSoTimeout(30 * 1000);
} catch (Exception ioe) {
throw new RuntimeException(ioe);
}
}
This will cause socket.receive(inPacket); to throw a SocketTimeoutException after 30 seconds. As you already catch Exception, that's all you need to do.
Making MulticastReceiver interruptible
This is a more radical refactoring.
public class MulticastReceiver implements Callable<DeviceDetails> {
private DatagramChannel channel;
public MulticastReceiver() {
try {
channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(5500));
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
public DeviceDetails call() throws Exception {
ByteBuffer inBuf = ByteBuffer.allocate(WifiConstants.DGRAM_LEN);
SocketAddress socketAddress = channel.receive(inBuf);
String msg = new String(inBuf.array(), 0, inBuf.capacity());
Log.v("Received: ","From :" + socketAddress + " Msg : " + msg);
return getDeviceFromString(msg);;
}
}
The DeviceManagerWindow looks a bit different; I'm not sure what you intend to do there, as you juggle around with lists and arrays, but you only have one future... So I assume you want to listen for 30 secs and fetch as many devices as possible.
ExecutorService executorService = Executors.newFixedThreadPool(1);
MulticastReceiver receiver = new MulticastReceiver();
List<DeviceDetails> devices = new ArrayList<DeviceDetails>();
long runUntil = System.currentTimeMillis() + 30 * 1000;
while (System.currentTimeMillis() < runUntil) {
Future<Object> future = executorService.submit(receiver);
try {
// wait no longer than the original 30s for a result
long timeout = runUntil - System.currentTimeMillis();
devices.add(future.get(timeout, TimeUnit.MILLISECONDS));
} catch (Exception e) {
Log.v("future Exception: ",e.toString());
}
}
// shutdown the executor service, interrupting the executed tasks
executorService.shutdownNow();
That's about it. No matter which solution you choose, don't forget to close the socket/channel.
I have solved it.. you can run your code in following fashion:
DeviceManagerWindow.java
public class DeviceManagerWindow extends Activity
{
public static Context con;
public static int rowCounter=0;
Thread sendMulticast;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_device_manager_window);
WifiManager wifi = (WifiManager)getSystemService( Context.WIFI_SERVICE );
if(wifi != null)
{
WifiManager.MulticastLock lock = wifi.createMulticastLock("WifiDevices");
lock.acquire();
}
TableLayout tb = (TableLayout) findViewById(R.id.DeviceList);
tb.removeAllViews();
con = getApplicationContext();
}
public void searchDevice(View view) throws IOException, InterruptedException
{
try
{
sendMulticast = new Thread(new MultiCastThread());
sendMulticast.start();
sendMulticast.join();
}
catch(Exception e)
{
Log.v("Exception in Sending:",e.toString());
}
here is the time bound search.... and you can quit your thread using thread.join
//Device Will only search for 1 minute
for(long stop=System.nanoTime()+TimeUnit.SECONDS.toNanos(1); stop>System.nanoTime();)
{
Thread recv = new Thread(new MulticastReceiver());
recv.start();
recv.join();
}
}
public static synchronized void addDevice(DeviceDetails device) throws InterruptedException
{
....
Prepare your desired list here.
....
}
}
Dont add any loop on the listening side. simply use socket.receive
MulticastReceiver.java
public class MulticastReceiver implements Runnable
{
DatagramSocket socket = null;
DatagramPacket inPacket = null;
public MulticastReceiver()
{
try
{
socket = new DatagramSocket(WifiConstants.PORT_NO_RECV);
}
catch(Exception ioe)
{
System.out.println(ioe);
}
}
#Override
public void run()
{
byte[] inBuf = new byte[WifiConstants.DGRAM_LEN];
//System.out.println("Listening");
inPacket = new DatagramPacket(inBuf, inBuf.length);
try
{
socket.setSoTimeout(3000)
socket.receive(inPacket);
String msg = new String(inBuf, 0, inPacket.getLength());
Log.v("Received: ","From :" + inPacket.getAddress() + " Msg : " + msg);
DeviceDetails device = getDeviceFromString(msg);
DeviceManagerWindow.addDevice(device);
socket.setSoTimeout(3000)will set the listening time for the socket only for 3 seconds. If the packet dont arrive it will go further.DeviceManagerWindow.addDevice(device);this line will call the addDevice method in the calling class. where you can prepare your list
}
catch(Exception e)
{
Log.v("Receiving Error: ",e.toString());
}
finally
{
socket.close();
}
}
public DeviceDetails getDeviceFromString(String str)
{
String type;
String IP;
type=str.substring(0,str.indexOf('`'));
str = str.substring(str.indexOf('`')+1);
IP=str;
DeviceDetails device = new DeviceDetails(type,IP);
return device;
}
}
Hope that works.. Well it will work.
All the best. Let me know if any problem.