Trying to perform an application that reads sms from gsm modem each a period of time.
Thought about this solution:
Got 2 Threads in my application.
T1)- GSMModemHandler which is a handler for serial communications.
T2)- SMSPicker that requests for sms each period of time and perform some string algorithms on them.
I want my application to do so:
A)- T2 asks for sms using readAllMessages(), a method from the GSMModemHandler class and then keeps blocked.
B)- T1 has got a SerialEventListener, so it listens for the response to the request sent from the GSM-Modem, and sends it back to T2.
C)- Once the response is available in a list from the T2 class, T2 resume its task concerning the string algorithms and then do again the same operations from A after waiting a certain period of time.
I've tried to code that, when i launch the application, it does its work for some time and then blocks, i guess the problem come from a missunderstanding between the 2 Threads, but can't find where the problem is and how to solve it.
Here's my code, and the result:
public class GSMModemHandler extends SerialPort implements
SerialPortEventListener{
private static final String
COMMAND_REMISE_A_ZERO = "ATZ",
COMMAND_SMS_MODE_TEXT = "AT+CMGF=1",
COMMAND_DETAILED_ERRORS = "AT+CMEE=1",
COMMAND_SET_UP_MEMORIES = "AT+CPMS=\"MT\",\"MT\",\"MT\"",
COMMAND_LIST_SUPPORTED_STORAGE_MODES = "AT+CPMS=?",
COMMAND_ENVOIE_SMS = "AT+CMGS=",
COMMAND_GET_ALL_SMS = "AT+CMGL=\"ALL\"",
COMMAND_GET_NEW_SMS = "AT+CMGL=\"REC UNREAD\"",
COMMAND_DELETE_ALL_MESSAGES = "AT+CMGD=0[,4]",
COMMAND_DELETE_READ_MESSAGES = "AT+CMGD=0[,1]";
private SMSPicker smsPicker = null;
private String response = "";
public GSMModemHandler(String port) throws SerialPortException{
super(port);
this.openPort();
this.setParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
this.addEventListener(this);
this.startGsm();
}
public void startGsm() throws SerialPortException{
this.writeString(GSMModemHandler.COMMAND_REMISE_A_ZERO + "\r\n");
this.writeString(GSMModemHandler.COMMAND_SMS_MODE_TEXT + "\r\n");
this.writeString(GSMModemHandler.COMMAND_DETAILED_ERRORS + "\r\n");
this.writeString(GSMModemHandler.COMMAND_SET_UP_MEMORIES + "\r\n");
}
public void sendMessage(SMS sms){
try{
if(this.isOpened()){
this.writeString(GSMModemHandler.COMMAND_ENVOIE_SMS + "\"" + sms.getCorrespondantSms() + "\"\r\n");
this.writeString(sms.getContenuSms() + '\032');
}
}
catch(SerialPortException exp){
exp.printStackTrace();
}
}
public void readAllMessages(){
try{
if(this.isOpened())
this.writeString(GSMModemHandler.COMMAND_GET_ALL_SMS + "\r\n");
}
catch(SerialPortException exp){
exp.printStackTrace();
}
}
public void readUnreadMessages(){
try{
if(this.isOpened())
this.writeString(GSMModemHandler.COMMAND_GET_NEW_SMS + "\r\n");
}
catch(SerialPortException exp){
exp.printStackTrace();
}
}
public void deleteAllMessages(){
try{
if(this.isOpened())
this.writeString(GSMModemHandler.COMMAND_DELETE_ALL_MESSAGES + "\r\n");
}
catch(SerialPortException exp){
exp.printStackTrace();
}
}
public void deleteReadMessages(){
try{
if(this.isOpened())
this.writeString(GSMModemHandler.COMMAND_DELETE_READ_MESSAGES + "\r\n");
}
catch(SerialPortException exp){
exp.printStackTrace();
}
}
public synchronized void fermerConnexion(){
try{
this.closePort();
}
catch(SerialPortException exp){
exp.printStackTrace();
}
}
AtomicBoolean nextResponseIsSms = new AtomicBoolean(false);
#Override
public void serialEvent(SerialPortEvent spe) {
try {
String reponse = this.readString();
System.out.println("GSM response = " + reponse);
// If the next response contains the wanted sms
if(reponse != null && reponse.contains("AT+CMGL=")){
this.nextResponseIsSms.set(true);
System.out.println("nextResponseIsSms = true");
}
// if the response contains sms
else if(this.nextResponseIsSms.get()){
this.smsPicker.getResponse().add(reponse);
System.out.println("response sent !");
this.deleteAllMessages(); // deleting the sms in the gsm modem
System.out.println("messages deleted");
this.nextResponseIsSms.set(false);
System.out.println("nextResponseIsSms = false");
// gives the SMSPicker the hand to treat the response
synchronized(this.smsPicker){ this.smsPicker.notify(); }
System.out.println("smsPicker notified");
}
} catch (SerialPortException ex) {
Logger.getLogger(GSMModemHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* #return the smsPicker
*/
public SMSPicker getSmsPicker() {
return smsPicker;
}
/**
* #param smsPicker the smsPicker to set
*/
public void setSmsPicker(SMSPicker smsPicker) {
this.smsPicker = smsPicker;
}
}
public class SMSPicker extends ControlledThread{
private GSMModemHandler modemGsm;
private SMSQueueToDatabase smsQueueHandler;
private volatile Queue<String> responses = new LinkedList<String>();
public SMSPicker(double frequency, GSMModemHandler gsmModem){
super(frequency);
this.modemGsm = gsmModem;
this.modemGsm.setSmsPicker(this);
this.smsQueueHandler = new SMSQueueToDatabase(frequency);
}
#Override
public void whatToDoBeforeTheLoop(){
this.smsQueueHandler.start();
try {
this.wait(2 * this.waitingPeriod.get());
} catch (InterruptedException ex) {
Logger.getLogger(SMSPicker.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void whatToDoDuringTheLoop() throws NullPointerException{
synchronized(this){
try {
System.out.println("I'm going to launch the request !");
// Sending the sms read request to the gsm modem
this.modemGsm.readAllMessages();
System.out.println("i'm going to be stopped!");
// wait till we get the answer
this.wait();
System.out.println("I've been stopped and now resuming");
}
catch (InterruptedException ex) {
Logger.getLogger(SMSPicker.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Treating the response in order to extract sms from it
while(!this.responses.isEmpty()){
String longMessage = this.responses.poll();
if(longMessage != null){
String[] shortMessages = null;
shortMessages = longMessage.split("\\+CMGL: [0-9]*,\"");
if(shortMessages == null) continue;
for(String shortMessage: shortMessages){
int indexLastOK = shortMessage.lastIndexOf("OK");
if(indexLastOK != -1 && shortMessage.contains("+"))
this.smsQueueHandler.getSmsFifo().add(this.fromStringToSms(shortMessage
.substring(0,shortMessage.lastIndexOf("OK") - 2))); // if it is the last sms
else if(shortMessage.contains("REC")) // if it is not the last one
this.smsQueueHandler.getSmsFifo().add(this.fromStringToSms(shortMessage));
}
}
}
}
private SMS fromStringToSms(String stringSms){
String[] smsParts = stringSms.split(",");
String correspondantSms = smsParts[1].replaceAll("\"", "");
String dateSms = smsParts[3].replace("\"","").replaceAll("/", "-");
String heureSms = smsParts[4].substring(0,smsParts[4].lastIndexOf("\"")).substring(0, 8);
String contenuSms = stringSms.substring(stringSms.lastIndexOf("\"") + 3);
LocalDateTime momentSms = LocalDateTime.parse("20" + dateSms + "T" + heureSms);
return new SMS(correspondantSms,contenuSms,momentSms);
}
#Override
public void whatToDoAfterTheLoop() {
}
/**
* #return the modemGsm
*/
public GSMModemHandler getModemGsm() {
return modemGsm;
}
/**
* #param modemGsm the modemGsm to set
*/
public void setModemGsm(GSMModemHandler modemGsm) {
this.modemGsm = modemGsm;
}
/**
* #return the smsQueueHandler
*/
public SMSQueueToDatabase getSmsQueueHandler() {
return smsQueueHandler;
}
/**
* #param smsQueueHandler the smsQueueHandler to set
*/
public void setSmsQueueHandler(SMSQueueToDatabase smsQueueHandler) {
this.smsQueueHandler = smsQueueHandler;
}
/**
* #return the response
*/
public Queue<String> getResponse() {
return responses;
}
/**
* #param response the response to set
*/
public void setResponse(Queue<String> responses) {
this.responses = responses;
}
}
public abstract class ControlledThread extends Thread{
protected AtomicBoolean workable = null;
protected AtomicLong waitingPeriod = null;
public ControlledThread(double frequency){
super();
this.workable = new AtomicBoolean(true);
this.waitingPeriod = new AtomicLong(((long)(1000 / frequency)));
}
#Override
public synchronized void run() {
this.whatToDoBeforeTheLoop();
while(this.workable.get()){
try{
this.whatToDoDuringTheLoop();
this.wait(this.waitingPeriod.get());
}
catch(InterruptedException exp){
exp.printStackTrace();
}
}
this.whatToDoAfterTheLoop();
}
public void stopWorking(){
this.workable.set(false);
}
public synchronized boolean isWorking(){
return this.workable.get();
}
public abstract void whatToDoBeforeTheLoop();
public abstract void whatToDoDuringTheLoop();
public abstract void whatToDoAfterTheLoop();
}
Result:
Note: The blocking state happens at the red line (BUILD STOPPED is just a result of the fact that i stopped the application by a kill)
Thanks in advance !
Most likely, you're experiencing a missed signal : you start waiting for a notify() that has already happened.
This is because you start waiting unconditionally. You should, however, always wait from within a loop that checks its wait condition.
In your case the contion to keep waiting is probably until an answer has been supplied.
so :
while (!hasAnswer()) {
this.wait();
}
You must also make sure that the monitor you synchronize on (the SMSPicker in your case) properly guards the state that determines the condition. Since you simply seem expose the response queue, it think it's likely not the case, but I'm missing too many details to say for sure.
For a more detailed explanation look here.
Related
I am trying to solve an exercise while studying for my next exam. This is the question
Use Java to implement a MsgBatcher class that holds a (finite) set of
messages and sends them, on request, in batch. Suppose we have a
Message class with a method void send(). MsgBatcher provides a method
void enqueue(Message) to add a new message to the batch. It suspends
the caller if the MsgBatcher is full (the maximum number of messages
that can be enqueued is provided in the MsgBatcher constructor). A
method void sendAll() is also provided to send all messages enqueued
up to that moment (it empties the MsgBatcher). Organize
synchronization that will take care of the fact that sending a message
may take a long time. Optional: implement the sendAll method so that
the sending is performed asynchronously w.r.t. the caller (i.e., in a
separate thread, which should be started at MsgBatcher creation time
and reused for each sending).
So far i have written this
import java.util.ArrayList;
public class MsgBatcher {
public ArrayList<Message> batch = new ArrayList<Message>();
int maxSpaces;
public void MsgBatcher(int max){
this.maxSpaces= max;
}
public synchronized void enqueue(Message m) throws InterruptedException{
while(batch.size() == maxSpaces) wait();
this.batch.add(m);
}
public void sendAll(){
ArrayList<Message> toSend = new ArrayList<Message>();
toSend.addAll(batch);
batch.clear(); //in this way i can accept other messages while sending the others
for (Message m : toSend){
m.send();
}
batch.clear();
}
}
and since the Message class is not really important i have written just a few lines to emulate the asked behaviour
public class Message {
public void send(){
System.out.println("Sending");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Sent");
}
}
what i do not understand is how to deal with
Organize synchronization that will take care of the fact that sending
a message may take a long time.
and especially with
Optional: implement the sendAll method so that the sending is
performed asynchronously w.r.t. the caller (i.e., in a separate
thread, which should be started at MsgBatcher creation time and reused
for each sending).
as per title of this question
I know i should ask the Professor, and i have already sent him an email, but he did not answer.
Thanks for your help!
I think you were preparing the exam Middleware technologies for dist sys at PoliMi.
I'm doing as well and I found your question because I'm doing the same exercise. This is my solution and it seems to work well.
I posted it, even if you don't probably need it anymore, in case someone else will need while preparing this exam.
import java.util.ArrayList;
public class TestThread extends Thread{
private String name;
private MsgBatcher msgBatcher;
public TestThread(String name, MsgBatcher msgBatcher){
this.name = name;
this.msgBatcher = msgBatcher;
}
/**
* Each thread tries to add 10 messages.
*/
public void run(){
for(int i = 0; i < 10; i++){
Message msg = new Message("Thread " + this.name + " | i: " + i);
try{
System.out.println("[T] Thread " + this.name + " Adding " + i);
this.msgBatcher.enqueue(msg);
System.out.println("[T] Thread " + this.name + " Added " + i);
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
MsgBatcher msgBatcher = new MsgBatcher(10);
TestThread t1 = new TestThread("1", msgBatcher);
TestThread t2 = new TestThread("2", msgBatcher);
t1.start();
t2.start();
try{
//After 3 seconds we try to send the first batch
Thread.sleep(3000);
msgBatcher.sendAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
class MsgSender extends Thread{
private ArrayList<Message> toSend;
public MsgSender(){
this.toSend = new ArrayList<Message>();
}
public void run(){
while(true){
synchronized (this){
try{
System.out.println("[MS] Starting run.");
while (this.toSend.size() == 0){
System.out.println("[MS] Waiting for messages." + this.toSend.size());
wait();
}
System.out.println("[MS] Messages ready to be sent.");
for(Message msg: this.toSend){
msg.send();
}
this.toSend.clear();
System.out.println("[MS] Messages sent.");
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
public synchronized void addMsgs(ArrayList<Message> toSend){
synchronized (this){
try{
System.out.println("[MS] Adding messages.");
while (this.toSend.size() != 0){
System.out.println("[MS] Adding messages. Waiting toSend to become empty");
wait();
}
this.toSend = toSend;
System.out.println("[MS] Messages added. Notifying the run." + this.toSend.size());
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
this.toSend = toSend;
notify();
}
}
class MsgBatcher{
private int size;
private ArrayList<Message> queue;
private MsgSender msgSender;
public MsgBatcher(int size){
this.size = size;
this.queue = new ArrayList<Message>(this.size);
this.msgSender = new MsgSender();
this.msgSender.start();
try{
//wait enough time to be sure the msgSender thread is ready
Thread.sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
}
public synchronized void enqueue(Message msg) throws InterruptedException{
System.out.println("[MB] size: " + this.queue.size());
while(this.queue.size() == this.size){
wait();
}
this.queue.add(msg);
notifyAll();
}
public synchronized void sendAll(){
System.out.println("Sending all messages");
ArrayList<Message> toSend = new ArrayList<Message>();
toSend.addAll(this.queue);
this.msgSender.addMsgs(toSend);
this.queue.clear();
notifyAll();
}
}
class Message {
private String text;
public Message(String text){
this.text = text;
}
public void send(){
System.out.println("[M] Sending: " + this.text);
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("[M] Sent: " + this.text);
}
}
In a task which is run by a thread pool, I want to write a bunch of strings to remote, and there is a flag to indicate if the task has been cancelled or not.
I'm using the following code to make sure I can stop as soon as possible:
public void sendToRemote(Iterator<String> data, OutputStream remote) {
try {
System.out.println("##### staring sending")
while(!this.cancelled && data.hasNext()) {
remote.write(data.next())
}
System.out.println("##### finished sending")
System.out.println("")
} catch(Throwable e) {
e.printStackTrace();
} finally {
remote.close();
}
}
I found sometimes, if I give a very large data(or infinite iterator) to this method, even if I have set the this.cancelled to true later, it can't finish in time. The code seems blocked, and after a long time(1 minute or so), there will be an error like:
java.net.SocketTimeoutException: write blocked too long
So I guess it might be the remote.write method can block itself if there is too much data to send, but the remote doesn't consume it in time. Although I set this.cancelled to true, but the method has been blocked in the remote.write(data.next()) line for a long time, so it doesn't have chance to check the value of this.cancelled and skip the loop. Instead, at last, it throws an SocketTimeoutException after a long time.
Is my understanding correct? If it is, how can I avoid the blocking if there is too much data to send?
Try simply closing the remote OutputStream. Your thread will end with an exception.
Thread#1 busy performing sendToRemote();
Thread#2 decided enough is enough and closed the remote. (Assuming
the OutPutStream object was not thread-local, as in a global
reference somewhere)
Thread#1 died with an exception :)
EDIT I found this on the internet
Enabling linger and setting the timeout to a certain number of seconds
will cause a subsequent call to Socket.Close to block until either all data
in the send buffer has been sent or the timeout has elapsed.
The proper solution is probably to use NIO in some manner. I already commented on how Hadoop did it here using nio underneath.
But the simpler solution is in Dexter's answer. I also came across an answer from EJP who suggest to use a BufferedOutputStream to control when data goes out. So I combined the two to arrive at the TimedOutputStream shown below. It does not give complete control on output buffering to remote (much of it is done by the OS), but combining an appropriate buffer size and write timeout provides at least some control (see the second program for testing the TimedOutputStream).
I have not completely tested the TimedOutputStream, so do your own due diligence.
Edit: updated write-method for better correlation between buffer-size and write timeout, also tweaked test program. Added comments about non-safe async close of socket outputstream.
import java.io.*;
import java.util.concurrent.*;
/**
* A {#link BufferedOutputStream} that sets time-out tasks on write operations
* (typically when the buffer is flushed). If a write timeout occurs, the underlying outputstream is closed
* (which may not be appropriate when sockets are used, see also comments on {#link TimedOutputStream#interruptWriteOut}).
* A {#link ScheduledThreadPoolExecutor} is required to schedule the time-out tasks.
* This {#code ScheduledThreadPoolExecutor} should have {#link ScheduledThreadPoolExecutor#setRemoveOnCancelPolicy(boolean)}
* set to {#code true} to prevent a huge task queue.
* If no {#code ScheduledThreadPoolExecutor} is provided in the constructor,
* the executor is created and shutdown with the {#link #close()} method.
* #author vanOekel
*
*/
public class TimedOutputStream extends FilterOutputStream {
protected int timeoutMs = 50_000;
protected final boolean closeExecutor;
protected final ScheduledExecutorService executor;
protected ScheduledFuture<?> timeoutTask;
protected volatile boolean writeTimedout;
protected volatile IOException writeTimeoutCloseException;
/* *** new methods not in BufferedOutputStream *** */
/**
* Default timeout is 50 seconds.
*/
public void setTimeoutMs(int timeoutMs) {
this.timeoutMs = timeoutMs;
}
public int getTimeoutMs() {
return timeoutMs;
}
public boolean isWriteTimeout() {
return writeTimedout;
}
/**
* If a write timeout occurs and closing the underlying output-stream caused an exception,
* then this method will return a non-null IOException.
*/
public IOException getWriteTimeoutCloseException() {
return writeTimeoutCloseException;
}
public ScheduledExecutorService getScheduledExecutor() {
return executor;
}
/**
* See {#link BufferedOutputStream#close()}.
*/
#Override
public void close() throws IOException {
try {
super.close(); // calls flush via FilterOutputStream.
} finally {
if (closeExecutor) {
executor.shutdownNow();
}
}
}
/* ** Mostly a copy of java.io.BufferedOutputStream and updated with time-out options. *** */
protected byte buf[];
protected int count;
public TimedOutputStream(OutputStream out) {
this(out, null);
}
public TimedOutputStream(OutputStream out, ScheduledExecutorService executor) {
this(out, 8192, executor);
}
public TimedOutputStream(OutputStream out, int size) {
this(out, size, null);
}
public TimedOutputStream(OutputStream out, int size, ScheduledExecutorService executor) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
if (executor == null) {
this.executor = Executors.newScheduledThreadPool(1);
ScheduledThreadPoolExecutor stp = (ScheduledThreadPoolExecutor) this.executor;
stp.setRemoveOnCancelPolicy(true);
closeExecutor = true;
} else {
this.executor = executor;
closeExecutor = false;
}
buf = new byte[size];
}
/**
* Flushbuffer is called by all the write-methods and "flush()".
*/
protected void flushBuffer(boolean flushOut) throws IOException {
if (count > 0 || flushOut) {
timeoutTask = executor.schedule(new TimeoutTask(this), getTimeoutMs(), TimeUnit.MILLISECONDS);
try {
// long start = System.currentTimeMillis(); int len = count;
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
if (flushOut) {
out.flush(); // in case out is also buffered, this will do the actual write.
}
// System.out.println(Thread.currentThread().getName() + " Write [" + len + "] " + (flushOut ? "and flush " : "") + "time: " + (System.currentTimeMillis() - start));
} finally {
timeoutTask.cancel(false);
}
}
}
protected class TimeoutTask implements Runnable {
protected final TimedOutputStream tout;
public TimeoutTask(TimedOutputStream tout) {
this.tout = tout;
}
#Override public void run() {
tout.interruptWriteOut();
}
}
/**
* Closes the outputstream after a write timeout.
* If sockets are used, calling {#link java.net.Socket#shutdownOutput()} is probably safer
* since the behavior of an async close of the outputstream is undefined.
*/
protected void interruptWriteOut() {
try {
writeTimedout = true;
out.close();
} catch (IOException e) {
writeTimeoutCloseException = e;
}
}
/**
* See {#link BufferedOutputStream#write(int b)}
*/
#Override
public void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer(false);
}
buf[count++] = (byte)b;
}
/**
* Like {#link BufferedOutputStream#write(byte[], int, int)}
* but with one big difference: the full buffer is always written
* to the underlying outputstream. Large byte-arrays are chopped
* into buffer-size pieces and writtten out piece by piece.
* <br>This provides a closer relation to the write timeout
* and the maximum (buffer) size of the write-operation to wait on.
*/
#Override
public void write(byte b[], int off, int len) throws IOException {
if (count >= buf.length) {
flushBuffer(false);
}
if (len <= buf.length - count) {
System.arraycopy(b, off, buf, count, len);
count += len;
} else {
final int fill = buf.length - count;
System.arraycopy(b, off, buf, count, fill);
count += fill;
flushBuffer(false);
final int remaining = len - fill;
int start = off + fill;
for (int i = 0; i < remaining / buf.length; i++) {
System.arraycopy(b, start, buf, count, buf.length);
count = buf.length;
flushBuffer(false);
start += buf.length;
}
count = remaining % buf.length;
System.arraycopy(b, start, buf, 0, count);
}
}
/**
* See {#link BufferedOutputStream#flush()}
* <br>If a write timeout occurred (i.e. {#link #isWriteTimeout()} returns {#code true}),
* then this method does nothing.
*/
#Override
public void flush() throws IOException {
// Protect against flushing before closing after a write-timeout.
// If that happens, then "out" is already closed in interruptWriteOut.
if (!isWriteTimeout()) {
flushBuffer(true);
}
}
}
And the test program:
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class TestTimedSocketOut implements Runnable, Closeable {
public static void main(String[] args) {
TestTimedSocketOut m = new TestTimedSocketOut();
try {
m.run();
} finally {
m.close();
}
}
final int clients = 3; // 2 is minimum, client 1 is expected to fail.
final int timeOut = 1000;
final int bufSize = 4096;
final long maxWait = 5000L;
// need a large array to write, else the OS just buffers everything and makes it work
byte[] largeMsg = new byte[28_602];
final ThreadPoolExecutor tp = (ThreadPoolExecutor) Executors.newCachedThreadPool();
final ScheduledThreadPoolExecutor stp = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
final ConcurrentLinkedQueue<Closeable> closeables = new ConcurrentLinkedQueue<Closeable>();
final CountDownLatch[] serversReady = new CountDownLatch[clients];
final CountDownLatch clientsDone = new CountDownLatch(clients);
final CountDownLatch serversDone = new CountDownLatch(clients);
ServerSocket ss;
int port;
#Override public void run() {
stp.setRemoveOnCancelPolicy(true);
try {
ss = new ServerSocket();
ss.bind(null);
port = ss.getLocalPort();
tp.execute(new SocketAccept());
for (int i = 0; i < clients; i++) {
serversReady[i] = new CountDownLatch(1);
ClientSideSocket css = new ClientSideSocket(i);
closeables.add(css);
tp.execute(css);
// need sleep to ensure client 0 connects first.
Thread.sleep(50L);
}
if (!clientsDone.await(maxWait, TimeUnit.MILLISECONDS)) {
println("CLIENTS DID NOT FINISH");
} else {
if (!serversDone.await(maxWait, TimeUnit.MILLISECONDS)) {
println("SERVERS DID NOT FINISH");
} else {
println("Finished");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override public void close() {
try { if (ss != null) ss.close(); } catch (Exception ignored) {}
Closeable c = null;
while ((c = closeables.poll()) != null) {
try { c.close(); } catch (Exception ignored) {}
}
tp.shutdownNow();
println("Scheduled tasks executed: " + stp.getTaskCount() + ", max. threads: " + stp.getLargestPoolSize());
stp.shutdownNow();
}
class SocketAccept implements Runnable {
#Override public void run() {
try {
for (int i = 0; i < clients; i++) {
SeverSideSocket sss = new SeverSideSocket(ss.accept(), i);
closeables.add(sss);
tp.execute(sss);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class SeverSideSocket implements Runnable, Closeable {
Socket s;
int number, cnumber;
boolean completed;
public SeverSideSocket(Socket s, int number) {
this.s = s;
this.number = number;
cnumber = -1;
}
#Override public void run() {
String t = "nothing";
try {
DataInputStream in = new DataInputStream(s.getInputStream());
DataOutputStream out = new DataOutputStream(s.getOutputStream());
serversReady[number].countDown();
Thread.sleep(timeOut);
t = in.readUTF();
in.readFully(new byte[largeMsg.length], 0, largeMsg.length);
t += in.readUTF();
out.writeByte(1);
out.flush();
cnumber = in.readInt();
completed = true;
} catch (Exception e) {
println("server side " + number + " stopped after " + e);
// e.printStackTrace();
} finally {
println("server side " + number + " received: " + t);
if (completed && cnumber != number) {
println("server side " + number + " expected client number " + number + " but got " + cnumber);
}
close();
serversDone.countDown();
}
}
#Override public void close() {
TestTimedSocketOut.close(s);
s = null;
}
}
class ClientSideSocket implements Runnable, Closeable {
Socket s;
int number;
public ClientSideSocket(int number) {
this.number = number;
}
#SuppressWarnings("resource")
#Override public void run() {
Byte b = -1;
TimedOutputStream tout = null;
try {
s = new Socket();
s.connect(new InetSocketAddress(port));
DataInputStream in = new DataInputStream(s.getInputStream());
tout = new TimedOutputStream(s.getOutputStream(), bufSize, stp);
if (number == 1) {
// expect fail
tout.setTimeoutMs(timeOut / 2);
} else {
// expect all OK
tout.setTimeoutMs(timeOut * 2);
}
DataOutputStream out = new DataOutputStream(tout);
if (!serversReady[number].await(maxWait, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Server side for client side " + number + " not ready.");
}
out.writeUTF("client side " + number + " starting transfer");
out.write(largeMsg);
out.writeUTF(" - client side " + number + " completed transfer");
out.flush();
b = in.readByte();
out.writeInt(number);
out.flush();
} catch (Exception e) {
println("client side " + number + " stopped after " + e);
// e.printStackTrace();
} finally {
println("client side " + number + " result: " + b);
if (tout != null) {
if (tout.isWriteTimeout()) {
println("client side " + number + " had write timeout, close exception: " + tout.getWriteTimeoutCloseException());
} else {
println("client side " + number + " had no write timeout");
}
}
close();
clientsDone.countDown();
}
}
#Override public void close() {
TestTimedSocketOut.close(s);
s = null;
}
}
private static void close(Socket s) {
try { if (s != null) s.close(); } catch (Exception ignored) {}
}
private static final long START_TIME = System.currentTimeMillis();
private static void println(String msg) {
System.out.println((System.currentTimeMillis() - START_TIME) + "\t " + msg);
}
}
Today I found a wierd problem. What I want is to check server availability (Particularly SSL checking) once application started and display proper message if server is down. This process should work in background and user is able to navigate the app if server has problem (app can works offline).
What I did is simple. In main activity I have
#Override
protected void onStart()
{
super.onStart();
// Check Internet connection
// Check Location sensor
// Check server accessibility
BackendCheck backendCheck = new BackendCheck(this);
if (!backendCheck.execute())
{
displayErrorDialog();
return;
}
}
This is BackendCheck class:
public class BackendCheck implements Callable<Boolean>
{
private static final String TAG = BackendCheck.class.getSimpleName();
// Thread sleep time
private static final int THREAD_SLEEP = 5000;
// Number of attempts to call an API in order to get response
private static final int MAX_ATTEMPT = 3;
// Current attempt
private int counter = 0;
// The url that should be used in order to get server response
private String mTestUrl;
// App mContext
private Context mContext;
// Server status
private boolean mServerStatus = false;
public BackendCheck(Context context)
{
this(context, "");
}
public BackendCheck(Context context, String url)
{
this.mTestUrl = url;
this.mContext = context;
}
public boolean execute()
{
// Check #mTestUrl and use Feature API if this variable is empty
if (TextUtils.isEmpty(mTestUrl))
{
mTestUrl = PassengerConstants.URL_BASE + mContext.getResources()
.getString(R.string.uri_feature_payments);
}
// Get ExecutorService from Executors utility class, thread pool size is 10
ExecutorService executor = Executors.newFixedThreadPool(10);
do
{
// Increment counter
counter++;
// Submit Callable tasks to be executed by thread pool
Future<Boolean> future = executor.submit(this);
try
{
// Break Do-While loop if server responded to request (there is no error)
if (!future.get())
{
mServerStatus = true;
break;
}
}
catch (InterruptedException e)
{
Logger.error(TAG, e.getMessage());
}
catch (ExecutionException e)
{
Logger.error(TAG, e.getMessage());
}
} while (counter < MAX_ATTEMPT);
// Shut down the executor service now
executor.shutdown();
// Return server status
return mServerStatus;
}
#Override
public Boolean call() throws Exception
{
// Sleep thread for a few seconds
Thread.sleep(THREAD_SLEEP);
try
{
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(mTestUrl);
Logger.debug(TAG, "Attempt (" + counter + "), try to check => " + mTestUrl);
HttpResponse httpResponse = client.execute(get);
int connectionStatusCode = httpResponse.getStatusLine().getStatusCode();
Logger.debug(TAG,
"Connection code: " + connectionStatusCode + " for Attempt (" + counter
+ ") of request: " + mTestUrl);
if (isServerError(connectionStatusCode))
{
return true;
}
}
catch (IllegalArgumentException e)
{
Logger.error(TAG, e.getMessage());
}
catch (Exception e)
{
Logger.error(TAG, e.getMessage());
}
return false;
}
/**
* Server status checker.
*
* #param statusCode status code of HTTP request
* #return True if connection code is 5xx, False otherwise.
*/
private static boolean isServerError(int statusCode)
{
return (statusCode >= HttpURLConnection.HTTP_INTERNAL_ERROR);
}
}
What happens is, When I launch the application splash screen displays. Then after a few seconds mainActivity runs (first code) then - since my server is down (for testing purposes) - I have black screen for 15 seconds (since I set MAX_ATTEMPT to 3 and have 5 seconds thread sleep) and after that I'm able to see UI of mainActivity and my error message.
I expect Callable<> should works in background and I see mainActivity after splashScreen without problem (black screen).
What you think? What problem might be? Thanks.
It would appear that you are executing the BackendCheck callable in the main thread.
Classes that extend Callable are usually executed via an ExecutorService which is a separate thread itself, thus it executes in the background. You may want to take a look at the Runnable interface or Thread if you'd like to run a separate thread to execute in the background that does not return a value. Calling the start method will cause the class to execute in a separate thread as indicated by the documentation:
When an object implementing interface Runnable is used to create a thread, starting the thread causes the object's run method to be called in that separately executing thread.
If you need to return some data at the end of execution I highly recommend an ExecutorService but you could probably also get away with using a FutureTask though I have less experience with that class. Hopefully that helps.
Okay, I just fixed my problem. 'njzk2' is right. The problem is future.get() which is running on or blocking main thread. I fixed the issue by doing a few changes.
First, I call my execute() method from a new thread. Therefore the whole of processing will be done in another thread.
I added new start() method in order to run it.
Add a listener in BackendCheck class and implemented it in my activity.
Since I want to display a dialog if server is down and I'm in another thread then runOnUiThread(runnable) uses to show the dialog in main thread.
This is my complete code for your reference.
In my activity:
#Override
protected void onStart()
{
super.onStart();
// Check Location sensor
// Check server accessibility
BackendCheck backendCheck = new BackendCheck(this);
backendCheck.setServerListener(new BackendCheck.BackendCheckListener()
{
#Override
public void onServerIsDown()
{
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
displayErrorDialog();
}
});
}
});
backendCheck.start();
}
And my BackendCheck class:
public class BackendCheck implements Callable<Boolean>
{
public interface BackendCheckListener
{
public void onServerIsDown();
}
private static final String TAG = BackendCheck.class.getSimpleName();
// Thread sleep time
private static final int THREAD_SLEEP = 5000;
// Number of attempts to call an API in order to get response
private static final int MAX_ATTEMPT = 3;
// Current attempt
private int counter = 0;
// The url that should be used in order to get server response
private String mTestUrl;
// App mContext
private Context mContext;
// Server status
private boolean mIsServerWorking = false;
// Server listener
private BackendCheckListener mListener;
public BackendCheck(Context context)
{
this(context, "");
}
public BackendCheck(Context context, String url)
{
this.mTestUrl = url;
this.mContext = context;
}
public void setServerListener (BackendCheckListener listener)
{
this.mListener = listener;
}
public void start()
{
Thread thread = new Thread()
{
#Override
public void run() {
boolean isServerWorking = execute();
if(!isServerWorking)
{
mListener.onServerIsDown();
}
}
};
thread.start();
}
private boolean execute()
{
// Check #mTestUrl and use Feature API if this variable is empty
if (TextUtils.isEmpty(mTestUrl))
{
mTestUrl = PassengerConstants.URL_BASE + mContext.getResources()
.getString(R.string.uri_feature_payments);
}
// Get ExecutorService from Executors utility class
ExecutorService executor = Executors.newFixedThreadPool(1);
do
{
// Increment counter
counter++;
// Submit Callable tasks to be executed by thread pool
Future<Boolean> future = executor.submit(this);
try
{
// Skip sleeping in first attempt
if(counter > 1)
{
// Sleep thread for a few seconds
Thread.sleep(THREAD_SLEEP);
}
// Break Do-While loop if server responded to request (there is no error)
if (!future.get())
{
mIsServerWorking = true;
break;
}
}
catch (InterruptedException e)
{
Logger.error(TAG, e.getMessage());
}
catch (ExecutionException e)
{
Logger.error(TAG, e.getMessage());
}
} while (counter < MAX_ATTEMPT);
// Try to shut down the executor service now
try
{
executor.shutdown();
executor.awaitTermination(THREAD_SLEEP, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
Logger.error(TAG, e.getMessage());
}
// Return server status
return mIsServerWorking;
}
#Override
public Boolean call() throws Exception
{
try
{
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(mTestUrl);
Logger.debug(TAG, "Attempt (" + counter + "), try to check => " + mTestUrl);
HttpResponse httpResponse = client.execute(get);
int connectionStatusCode = httpResponse.getStatusLine().getStatusCode();
Logger.debug(TAG,
"Connection code: " + connectionStatusCode + " for Attempt (" + counter
+ ") of request: " + mTestUrl);
if (isServerError(connectionStatusCode))
{
return true;
}
}
catch (IllegalArgumentException e)
{
Logger.error(TAG, e.getMessage());
}
catch (Exception e)
{
Logger.error(TAG, e.getMessage());
}
return false;
}
/**
* Server status checker.
*
* #param statusCode status code of HTTP request
* #return True if connection code is 5xx, False otherwise.
*/
private static boolean isServerError(int statusCode)
{
return (statusCode >= HttpURLConnection.HTTP_INTERNAL_ERROR);
}
}
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
// Check for closing frame
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
return;
}
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
}
if (!(frame instanceof TextWebSocketFrame)) {
throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
.getName()));
}
// Send the uppercase string back.
String request = ((TextWebSocketFrame) frame).text();
if (logger.isLoggable(Level.FINE)) {
logger.fine(String.format("%s received %s", ctx.channel(), request));
}
Message msg = new Message(ctx.channel(), request);
ReadQueueHandler.getInstance().addMessageToProcess(msg);
}
public class ReadQueueHandler implements Runnable {
private static int POOL_SIZE = 3;
private static ReadQueueHandler instance;
private final BlockingQueue<Message> messageQueue;
private final ExecutorService threadPool;
private final int threadPoolSize;
private final boolean isActive;
private ReadQueueHandler() {
this.threadPoolSize = POOL_SIZE;
this.threadPool = Executors.newFixedThreadPool(threadPoolSize);
this.messageQueue = new LinkedBlockingQueue<Message>();
isActive = true;
initThreadPool();
}
private void initThreadPool() {
for (int i = 0; i < this.threadPoolSize; i++) {
this.threadPool.execute(this);
}
}
/**
* Add message to read queue
*
* #param message
* - adding message
*/
public void addMessageToProcess(Message message) {
if (message != null) {
this.messageQueue.add(message);
}
}
#Override
public void run() {
while (isActive) {
Message message = null;
try {
message = this.messageQueue.take();
} catch (InterruptedException e) {
System.out.println("Exceptio " + e);
/*
* TODO Add logging
*/
}
if (message != null) {
Channel channel = message.getChannel();
channel.write(new TextWebSocketFrame("Message handled "));
}
}
}
public static ReadQueueHandler getInstance() {
if (instance == null) {
instance = new ReadQueueHandler();
}
return instance;
}
}
If i execute Channel.write("something") instead of adding data to queue, then all work fine and client get data. But if Channel.write("") execute from another thread, than data is not got. What can be reason? Channel write can not be execute from another thread?
For me it seems like you forgot to call flush() after the write is done to guaranteer it's flushed to the socket. For example you could fix this by use:
channel.writeAndFlush(new TextWebSocketFrame("Message handled "));
I am having a run method which tries to override another run method. But its not happening because I am getting a "Class not found Exception" before it passed on to run method.
HereĀ“s my class with run method
public class PollingSynchronizer implements Runnable{
public Collection<KamMessage> incomingQueue,outgoingQueue,fetchedMessages;
private Connection dbConnection;
/**
* Constructor. Requires to provide a reference to the Kam message queue
*
* #param incomingMessages reference to message queue
* #param dbConnection
*
*/
public PollingSynchronizer(Collection<KpiMessage> incomingQueue, Connection dbConnection) {
super();
this.incomingQueue = incomingQueue;
this.dbConnection = dbConnection;
}
private int seqId;
public int getSeqId() {
return seqId;
}
public void setSeqId(int seqId) {
this.seqId = seqId;
}
#Override
/**
* The method which runs Polling action and record the time at which it is done
*
*/
public void run() {
int seqId = 0;
while(true) {
List<KamMessage> list = null;
try {
list = fullPoll(seqId);
if (!list.isEmpty()) {
seqId = list.get(0).getSequence();
incomingQueue.addAll(list);
this.outgoingQueue = incomingQueue;
System.out.println("waiting 3 seconds");
System.out.println("new incoming message");
Thread.sleep(3000);
//when I debug my execution stops here and throws exception
MessageProcessor processor = new MessageProcessor() {
#Override
public void run() {
new MessageProcessor().generate(outgoingQueue);
}
};
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
This is the method which I have to call in order to execute.
public abstract class MessageProcessor implements Runnable {
private Collection<KpiMessage> fetchedMessages;
private Connection dbConnection;
Statement st = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
private Collection<KpiMessage> outgoingQueue;
public KpiMsg804 MessageProcessor(Collection<KpiMessage> outgoingQueue, Connection
dbConnection){
this.outgoingQueue = outgoingQueue;
this.dbConnection = dbConnection;
return (KpiMsg804) fetchedMessages;
}
public Collection<KamMessage> generate(Collection<KamMessage> outgoingQueue)
{
while(true){
try {
while (rs.next()) {
KamMessage filedClass = convertRecordsetToPojo(rs);
outgoingQueue.add(filedClass);
}
for (KamMessage pojoClass : outgoingQueue) {
KamMsg804 updatedValue = createKamMsg804(pojoClass);
System.out.print(" " + pojoClass.getSequence());
System.out.print(" " + pojoClass.getTableName());
System.out.print(" " + pojoClass.getAction());
System.out.print(" " + updatedValue.getKeyInfo1());
System.out.print(" " + updatedValue.getKeyInfo2());
System.out.println(" " + pojoClass.getEntryTime());
}
return outgoingQueue;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
How can I implement this?
Since I am new here, please give a reason for thumbs down. So that I can explain my question.
Try this:
MessageProcessor processor = new MessageProcessor() {
#Override
public void run() {
**new MessageProcessor()**.generate(outgoingQueue);
}
};
MessageProcessor is an abstract class. object creation inside the run method should have failed at compile time.
processor object is created but unused.. you need to create a thread with the processor instace and start the thread.