/** Send a CONNECT_MEDIATOR packet to the BackEnd to check if it is reachable */
  private boolean ping(int cnt) throws ICPException {
    // Try first with the current transport address, then with the various backup addresses
    for (int i = -1; i < backEndAddresses.length; i++) {
      if (i >= 0) {
        // Set the mediator address to a new address..
        String addr = backEndAddresses[i];
        int colonPos = addr.indexOf(':');
        String host = addr.substring(0, colonPos);
        String port = addr.substring(colonPos + 1, addr.length());
        mediatorTA = new JICPAddress(host, port, myMediatorID, "");
      }

      try {
        myLogger.log(
            Logger.FINE,
            "Ping " + mediatorTA.getHost() + ":" + mediatorTA.getPort() + "(" + cnt + ")...");
        JICPPacket pkt =
            new JICPPacket(JICPProtocol.CONNECT_MEDIATOR_TYPE, JICPProtocol.DEFAULT_INFO, null);
        pkt = deliver(pkt);
        if (pkt.getType() == JICPProtocol.ERROR_TYPE) {
          // Communication OK, but there was a JICP error.
          String errorMsg = new String(pkt.getData());
          if (errorMsg.equals(JICPProtocol.NOT_FOUND_ERROR)) {
            // Back-end not found: either the max disconnection time expired server side or there
            // was a fault and restart
            // --> Try to recreate the Back-end
            myLogger.log(
                Logger.WARNING,
                "Communication OK, but Back-end no longer present. Try to recreate it");
            if (myConnectionListener != null) {
              myConnectionListener.handleConnectionEvent(ConnectionListener.BE_NOT_FOUND, null);
            }
            try {
              createBackEnd();
            } catch (IMTPException imtpe) {
              myLogger.log(Logger.WARNING, "Error re-creating the Back-end.");
              return false;
            }
          } else {
            // Generic JICP error. No need to go on
            throw new ICPException("JICP error. " + errorMsg);
          }
        }
        return true;
      } catch (IOException ioe) {
        // Ignore it, and try the next address...
        myLogger.log(Logger.FINE, "Ping KO", ioe);
      }
    }

    // No address succeeded.
    return false;
  }
  /** 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*/
    }
  }
 public int writePacket(JICPPacket pkt) throws IOException {
   if (sc != null) {
     if (os == null) {
       os = getOutputStream();
     }
     int ret = pkt.writeTo(os);
     os.flush();
     return ret;
   } else {
     throw new IOException("Connection closed");
   }
 }
 public JICPPacket readPacket() throws IOException {
   if (sc != null) {
     if (is == null) {
       // #MIDP_EXCLUDE_BEGIN
       is = sc.getInputStream();
       // #MIDP_EXCLUDE_END
       /*#MIDP_INCLUDE_BEGIN
       is = sc.openInputStream();
       #MIDP_INCLUDE_END*/
     }
     return JICPPacket.readFrom(is);
   } else {
     throw new IOException("Connection closed");
   }
 }
  /**
   * Dispatch a serialized command to the BackEnd and get back a serialized response. Mutual
   * exclusion with itself to preserve dispatching order
   */
  public synchronized byte[] dispatch(byte[] payload, boolean flush) throws ICPException {
    // Note that we don't even try to dispatch packets while the
    // device is not reachable to preserve dispatching order.
    // If dispatching succeeded in fact this command would overcome
    // any postponed command waiting to be flushed.
    if (myDisconnectionManager.isReachable()) {
      // The following check preserves dispatching order when the
      // device has just reconnected but flushing has not started yet
      if (waitingForFlush && !flush) {
        throw new ICPException("Upsetting dispatching order");
      }
      waitingForFlush = false;

      int sid = outCnt;
      outCnt = (outCnt + 1) & 0x0f;
      myLogger.log(Logger.FINE, "Issuing outgoing command " + sid);
      try {
        JICPPacket pkt =
            new JICPPacket(JICPProtocol.COMMAND_TYPE, JICPProtocol.DEFAULT_INFO, payload);
        pkt.setSessionID((byte) sid);
        pkt = deliver(pkt);
        myLogger.log(Logger.FINE, "Response received " + pkt.getSessionID());
        if (pkt.getType() == JICPProtocol.ERROR_TYPE) {
          // Communication OK, but there was a JICP error on the peer
          throw new ICPException(new String(pkt.getData()));
        }
        return pkt.getData();
      } catch (IOException ioe) {
        // Can't reach the BackEnd. Assume we are unreachable
        myLogger.log(Logger.WARNING, "IOException on output connection", ioe);
        myDisconnectionManager.setUnreachable(false);
        throw new ICPException("Dispatching error.", ioe);
      }
    } else {
      throw new ICPException("Unreachable");
    }
  }
    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");
    }
  /**
   * Send the CREATE_MEDIATOR command with the necessary parameter in order to create the BackEnd in
   * the fixed network. Executed - at bootstrap time by the thread that creates the
   * FrontEndContainer. - To re-attach to the platform after a fault of the BackEnd
   */
  private synchronized void createBackEnd() throws IMTPException {
    StringBuffer sb = BackEndStub.encodeCreateMediatorRequest(props);
    if (myMediatorID != null) {
      // This is a request to re-create my expired back-end
      BackEndStub.appendProp(sb, JICPProtocol.MEDIATOR_ID_KEY, myMediatorID);
      BackEndStub.appendProp(sb, "outcnt", String.valueOf(outCnt));
      BackEndStub.appendProp(sb, "lastsid", String.valueOf(lastSid));
    }
    JICPPacket pkt =
        new JICPPacket(
            JICPProtocol.CREATE_MEDIATOR_TYPE,
            JICPProtocol.DEFAULT_INFO,
            null,
            sb.toString().getBytes());

    // Try first with the current transport address, then with the various backup addresses
    for (int i = -1; i < backEndAddresses.length; i++) {

      if (i >= 0) {
        // Set the mediator address to a new address..
        String addr = backEndAddresses[i];
        int colonPos = addr.indexOf(':');
        String host = addr.substring(0, colonPos);
        String port = addr.substring(colonPos + 1, addr.length());
        mediatorTA = new JICPAddress(host, port, myMediatorID, "");
      }

      try {
        HTTPClientConnection hc = (HTTPClientConnection) getConnection(mediatorTA);
        myLogger.log(
            Logger.INFO,
            "Creating BackEnd on "
                + hc.getProtocol()
                + mediatorTA.getHost()
                + ":"
                + mediatorTA.getPort());
        pkt = deliver(pkt);

        String replyMsg = new String(pkt.getData());
        if (pkt.getType() != JICPProtocol.ERROR_TYPE) {
          // BackEnd creation successful
          BackEndStub.parseCreateMediatorResponse(replyMsg, props);
          myMediatorID = props.getProperty(JICPProtocol.MEDIATOR_ID_KEY);
          // Complete the mediator address with the mediator ID
          mediatorTA =
              new JICPAddress(mediatorTA.getHost(), mediatorTA.getPort(), myMediatorID, null);
          myDisconnectionManager.setReachable();
          myKeepAliveManager.update();
          myLogger.log(Logger.INFO, "BackEnd OK. Mediator ID is " + myMediatorID);
          return;
        } else {
          myLogger.log(Logger.WARNING, "Mediator error: " + replyMsg);
        }
      } catch (IOException ioe) {
        // Ignore it, and try the next address...
        myLogger.log(Logger.WARNING, "Connection error", ioe);
      }
    }

    // No address succeeded!
    throw new IMTPException("Error creating the BackEnd.");
  }