10 Replies Latest reply on May 6, 2014 7:27 PM by Markus KARG

    PushRegistry fires up MIDlet but acceptAndOpen blocks

    Markus KARG

      For lesson 4 homework I have modified my MIDlet to get notified by the PushRegistry.

      When a client wants to connect, the PushRegistry fires up my MIDlet and provides a connection string.

      I can open a ServerSocketConnection from that string, but when at acceptAndOpen it blocks!

       

      How could that happen?

       

      Thanks!

      -Markus

        • 1. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
          Tmcginn-Oracle

          Hi Markus -

           

          That's a good question.  Can you send the startApp method of your MIDlet?

           

          Also, if you print the connection string to the console, what is the result?

           

          Tom

          • 2. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
            Markus KARG

            Tim,

             

            thank you for looking into my code. The connection string I get is socket://:8042.

             

            In fact I think it is related to my use of threads, as I developed the solution from a multi-client version of the MIDlet. Possibly the AMS "kills" my MIDlet while the worker thread is still serving (how to prevent that?).

             

            public class Lesson42 extends MIDlet {

                @Override

                public void startApp() {

                    try {

                        final String[] cons = PushRegistry.listConnections(true);

                        if (cons.length > 0) {

                            for (final String con : cons) {

                                if (con.startsWith("socket")) {

                                    handleConnections((ServerSocketConnection) Connector.open(con));

                                }

                            }

                        }

                    } catch (IOException ex) {

                        ex.printStackTrace();

                    }

                    notifyDestroyed();

                }

             

             

                @Override

                public void pauseApp() {

                    // unused

                }

             

             

                @Override

                public void destroyApp(boolean unconditional) {

                }

             

             

                private void handleConnections(final ServerSocketConnection ssc) {

                    new Thread() {

                        @Override

                        public final void run() {

                            try {

                                while (true) {

                                    handleClient(ssc.acceptAndOpen());

                                }

                            } catch (IOException ex) {

                                ex.printStackTrace();

                            }

                        }

             

             

                        private void handleClient(final StreamConnection connection) throws IOException {

                            new Thread() {

                                @Override

                                public final void run() {

                                    final byte[] buffer = new byte[128];

                                    try (final InputStream is = connection.openDataInputStream(); final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(connection.openDataOutputStream()))) {

                                        OUTER:

                                        while (true) {

                                            final int b = is.read(buffer);

                                            final String s = new String(buffer, 0, b);

                                            System.out.println(s);

                                            switch (s) {

                                                case "READ":

                                                    String[] _msgToSend = {"^1389744181,5^^", "^1389747781,-3^^", "^1389751561,7^^", "^1389753606,10^^", "^1389756000,12^^", "^1389758577,8^^",

                                                        "^^1387202580,40.5583739,N,85.6591442,E,157.25^", "^^1387202967,41.1234567,S,86.1234567,W,135.89^"};

                                                    bw.write(Integer.toString(8));

                                                    bw.newLine();

                                                    for (final String m : _msgToSend) {

                                                        bw.write(m);

                                                        bw.newLine();

                                                    }

                                                    bw.flush();

                                                    break;

                                                case "DISCONNECT":

                                                    break OUTER;

                                            }

                                        }

                                    } catch (IOException ex) {

                                        Logger.getLogger(Lesson42.class.getName()).log(Level.SEVERE, null, ex);

                                    }

                                }

                            }.start();

                        }

                    }.start();

                };

            }

            • 3. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
              Tmcginn-Oracle

              Hi Markus -

               

              You are correct that the notifyDestroyed() in the startApp method is killing the MIDlet, and therefore all the threads with it.

               

              If I move the notifyDestroyed() to after the if statement, like this:

               

              if (cons.length > 0) {

                  //...       

              } else {        

                  notifyDestroyed();      

              }

               

              Then the MIDlet "sleeps" until the first client makes a connection, and then it is "awake" forever after that - given the way the code is written now, that may be what you wanted - the handleConnections method continually looks for another client socket.

               

              In general though, the push registry is designed to wake up, handle a task, and then sleep again - therefore the task should be relatively short.

               

              I think an approach that might work would be to use the MIDlet that wakes up via socket request to create the ServerSocketConnection, block on acceptAndOpen, and send the SocketConnection returned from the call to another MIDlet, in another suite using the inter-midlet communication (IMC) protocol discussed in lesson 5. This second MIDlet would be multi-threaded, and handle the socket connection until explicitly closed (by a DISCONNECT). This approach would accomplish what you want (I think) - rather than an expensive ServerSocketConnection.acceptAndOpen MIDlet running all the time, it would only run for as long as it took to create the SocketConnection and then go back to sleep, while the second long running MIDlet would handle all of the socket connections.

               

              Hope this helps and makes sense,

               

              Tom

              1 person found this helpful
              • 4. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                Markus KARG

                Tom,

                 

                thank you so much for sharing your ideas! This sounds very promising. I'll definitively try it  out!

                 

                Thanks!

                -Markus

                • 5. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                  Markus KARG

                  I found and fixed my problem! For interested parties, here is the solution: As I am using two threads, I certainly must not invoke notifyDestroyed() unconditionally as Tom said. Certainly, I must wait until all threads are finished serving the currently running connections. For that i simply used a volatile variable (counter of currently served connections), and a synchronized mutator method ("setter"). The setter is invoked at the end each worker thread and does notifyDestroyed() once the connection counter falls down to zero. Also, startApp does notifyDestroyed() when it is invoked with no connections. Finally, the threads waiting for acceptAndOpen() terminate themselves by checking for the connection counter being zero. That's it. Works like a charm, and is fully scalable.

                   

                  public class Lesson42 extends MIDlet {

                   

                   

                      private volatile int runningThreads;

                   

                   

                      private synchronized void addThread() {

                          this.runningThreads++;

                      }

                   

                   

                      private synchronized void removeThread() {

                          this.runningThreads--;

                          if (runningThreads == 0) {

                              this.notifyDestroyed();

                          }

                      }

                   

                   

                      @Override

                      public void startApp() {

                          final String[] cons = PushRegistry.listConnections(true);

                          if (cons.length > 0) {

                              for (final String con : cons) {

                                  if (con.startsWith("socket")) {

                                      handleConnections(con);

                                  }

                              }

                          } else

                              notifyDestroyed();

                      }

                   

                   

                      @Override

                      public void pauseApp() {

                          // unused

                      }

                   

                   

                      @Override

                      public void destroyApp(boolean unconditional) {

                      }

                   

                   

                      private void handleConnections(final String con) {

                          new Thread() {

                              @Override

                              public final void run() {

                                  try {

                                      try (final ServerSocketConnection ssc = (ServerSocketConnection) Connector.open(con)) {

                                          do {

                                              handleClient(ssc.acceptAndOpen());

                                          } while (runningThreads > 0);

                                      }

                                  } catch (IOException ex) {

                                      ex.printStackTrace();

                                  }

                              }

                   

                   

                              private void handleClient(final StreamConnection connection) throws IOException {

                                  addThread();

                                  new Thread() {

                                      @Override

                                      public final void run() {

                                          final byte[] buffer = new byte[128];

                                          try (final InputStream is = connection.openDataInputStream(); final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(connection.openDataOutputStream()))) {

                                              OUTER:

                                              while (true) {

                                                  final int b = is.read(buffer);

                                                  final String s = new String(buffer, 0, b);

                                                  System.out.println(s);

                                                  switch (s) {

                                                      case "READ":

                                                          String[] _msgToSend = {"^1389744181,5^^", "^1389747781,-3^^", "^1389751561,7^^", "^1389753606,10^^", "^1389756000,12^^", "^1389758577,8^^",

                                                              "^^1387202580,40.5583739,N,85.6591442,E,157.25^", "^^1387202967,41.1234567,S,86.1234567,W,135.89^"};

                                                          bw.write(Integer.toString(8));

                                                          bw.newLine();

                                                          for (final String m : _msgToSend) {

                                                              bw.write(m);

                                                              bw.newLine();

                                                          }

                                                          bw.flush();

                                                          break;

                                                      case "DISCONNECT":

                                                          break OUTER;

                                                  }

                                              }

                                          } catch (IOException ex) {

                                              Logger.getLogger(Lesson42.class.getName()).log(Level.SEVERE, null, ex);

                                          }

                                          removeThread();

                                      }

                                  }.start();

                              }

                          }.start();

                      }

                   

                   

                  ;

                  }

                  • 6. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                    Tmcginn-Oracle

                    Well done Markus - good solution!

                    • 7. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                      Trinity

                      Since this is a network application where one has to bare in mind that the network might not always stable, what happens if the client suddenly got disconnected without you pressing the "DISCONNECT" button and the disconnect message never reached your server? Will your counter still keep account of your live connections accurately?

                      • 8. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                        Markus KARG

                        Despite the fact that I forgot to skip new String() in case the client closes the socket without sending "DISCONNECT", it should work stable: Both unexpected terminations (is.read() == -1 and is.read() throws IOExeption) lead to removeThread(). Did you encounter different behaviour?

                        • 9. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                          Trinity

                          Sorry Markus, your code was a just little hard to read on my device yesterday and while in a rush I couldn't see whether if you had your removeThread() method invoked at the right place. I wrote a similar solution to yours a while back and thought of the issue when I was doing it so was just wondering if you had checked for some unexpected termination scenario. Its cool, your code seems to handle it

                          • 10. Re: PushRegistry fires up MIDlet but acceptAndOpen blocks
                            Markus KARG

                            Certainly a real-world solution would use a more object-oriented approach like self-deregistering threads instead of a volatile / synchronized counter. In fact, the above code was more intended as a proof-of-concept of multi-threaded PushRegistry use, than a final program design.