/** 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"); }