/** Deliver a packet over a given connection and get back a response */
  private JICPPacket deliver(JICPPacket pkt, Connection c) throws IOException {
    boolean lastPacket = false;
    if (Thread.currentThread() == terminator) {
      pkt.setTerminatedInfo(true);
      lastPacket = true;
    }
    pkt.setRecipientID(mediatorTA.getFile());
    byte type = pkt.getType();

    int status = 0;
    try {
      c.writePacket(pkt);
      status = 1;
      /*#MIDP_INCLUDE_BEGIN
      lock();
      if (type == JICPProtocol.RESPONSE_TYPE) {
      	TimerDispatcher.getTimerDispatcher().add(new Timer(System.currentTimeMillis()+5000, this));
      }
      #MIDP_INCLUDE_END*/
      pkt = c.readPacket();

      status = 2;
      if (lastPacket && (pkt.getInfo() & JICPProtocol.TERMINATED_INFO) != 0) {
        // When we send a packet marked with the terminated-info, the back-end may either close
        // the connection (in this case we would have got an Exception) or reply with another
        // packet marked with the terminated-info --> throws an Exception to expose a uniform
        // behaviour
        myLogger.log(Logger.INFO, "Termination notification ACK received");
        throw new IOException("Terminated-info");
      }
      return pkt;
    } catch (IOException ioe) {
      // Re-throw the exception adding the status
      throw new IOException(ioe.getMessage() + '[' + status + ']');
    } finally {
      /*#MIDP_INCLUDE_BEGIN
      if (type != JICPProtocol.RESPONSE_TYPE) {
      	// If we delivered a RESPONSE unlock() is already called by the TimerDispatcher
      	unlock();
      }
      #MIDP_INCLUDE_END*/
    }
  }
 /**
  * Set the reachability state as "unreachable" and starts a separate thread that periodically
  * ping the back-end to detect when we are reachable again
  */
 private synchronized void setUnreachable(boolean missingKA) {
   if (reachable) {
     if (missingKA || !pingOK) {
       if (myConnectionListener != null) {
         myConnectionListener.handleConnectionEvent(ConnectionListener.DISCONNECTED, null);
       }
       reachable = false;
       myLogger.log(Logger.INFO, "Starting DM (" + System.currentTimeMillis() + ").");
       myThread = new Thread(this);
       myThread.start();
       if (pingOK) {
         // The InputManager is blocked waiting for data that will never arrive
         // Kill it and create a new one
         myInputManager.kill();
         myInputManager = new InputManager();
         myInputManager.start();
       }
     }
   }
 }
 /**
  * Make this HTTPFEDispatcher terminate. Note that when the BackEnd receives the termination
  * notification (explicitly sent in case of a self-initiated shutdown or attached to the response
  * to the EXIT command), it closes the input connection. The InputManager gets an exception and,
  * since it has been killed, terminates.
  */
 public void shutdown() {
   terminator = Thread.currentThread();
   myLogger.log(
       Logger.INFO,
       "Dispatcher shutting down. Self-initiated = " + (terminator != myInputManager));
   if (terminator != myInputManager) {
     // Self-initiated shut down
     // If connected, explicitly notify the BackEnd.
     if (myDisconnectionManager.isReachable()) {
       JICPPacket pkt =
           new JICPPacket(JICPProtocol.COMMAND_TYPE, (byte) (JICPProtocol.DEFAULT_INFO), null);
       myLogger.log(Logger.INFO, "Pushing termination notification");
       try {
         deliver(pkt);
       } catch (IOException ioe) {
         // When the BackEnd receives the termination notification,
         // it just closes the connection --> we always have this exception
         myLogger.log(Logger.FINE, "BackEnd closed");
       }
     }
     // Kill the InputManager
     myInputManager.kill();
   }
 }
 private void waitABit(long time) {
   try {
     Thread.sleep(time);
   } catch (InterruptedException ie) {
   }
 }
    public void run() {
      if (cnt == 0) {
        // Give precedence to the Thread that is creating the BackEnd
        Thread.yield();

        // In the meanwhile load the ConnectionListener if any
        try {
          myConnectionListener =
              (ConnectionListener)
                  Class.forName(props.getProperty("connection-listener")).newInstance();
        } catch (Exception e) {
          // Just ignore it
        }
      }
      myId = cnt++;
      myLogger.log(Logger.INFO, "IM-" + myId + " started");

      // Prepare an initial dummy response
      JICPPacket rsp = new JICPPacket(JICPProtocol.RESPONSE_TYPE, JICPProtocol.DEFAULT_INFO, null);
      while (active) {
        try {
          // Prepare the connection for next incoming command
          refreshConnection();

          myDisconnectionManager.waitUntilReachable();
          // Deliver the response to the previous incoming command and wait for the next one
          // If we are delivering the response to an exit command, set active to false to avoid an
          // annoying stack trace
          // (due to the fact that the back-end will close the connection instead of sending further
          // commands) and exit
          if (this == terminator) {
            active = false;
          }
          JICPPacket cmd = deliver(rsp, myConnection);
          myKeepAliveManager.update();
          if (cmd.getType() == JICPProtocol.KEEP_ALIVE_TYPE) {
            // Keep-alive
            if (myLogger.isLoggable(Logger.FINER)) {
              myLogger.log(Logger.FINER, "Keep-alive received");
            }
            rsp = new JICPPacket(JICPProtocol.RESPONSE_TYPE, JICPProtocol.OK_INFO, null);
          } else {
            // Command
            byte sid = cmd.getSessionID();
            if (sid == lastSid) {
              myLogger.log(Logger.WARNING, "Duplicated command received " + sid);
              rsp = lastResponse;
            } else {
              myLogger.log(Logger.FINE, "Incoming command received " + sid);
              byte[] rspData = mySkel.handleCommand(cmd.getData());
              myLogger.log(Logger.FINE, "Incoming command served " + sid);
              rsp = new JICPPacket(JICPProtocol.RESPONSE_TYPE, JICPProtocol.DEFAULT_INFO, rspData);
              rsp.setSessionID(sid);
              lastSid = sid;
              lastResponse = rsp;
            }
          }
        } catch (Exception e) {
          if (active) {
            myLogger.log(Logger.WARNING, "Exception on input connection", e);
            // Note that the boolean value passed to setUnreachable() only indicates if the
            // unreachability was detected by a missing keep-alive
            myDisconnectionManager.setUnreachable(false);
          }
        }
      }
      myLogger.log(Logger.INFO, "IM-" + myId + " terminated");
    }