package net.i2p.router.message; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.util.Date; import net.i2p.data.Hash; import net.i2p.data.router.RouterInfo; import net.i2p.data.i2np.I2NPMessage; import net.i2p.router.Job; import net.i2p.router.JobImpl; import net.i2p.router.MessageSelector; import net.i2p.router.OutNetMessage; import net.i2p.router.ReplyJob; import net.i2p.router.RouterContext; import net.i2p.util.Log; /** * Send a message directly to another router, i.e. not through a tunnel. * This is safe to run inline via runJob(). * If the RouterInfo for the Hash is not found locally, it will * queue a lookup and register itself to be run again when the lookup * succeeds or times out. */ public class SendMessageDirectJob extends JobImpl { private final Log _log; private final I2NPMessage _message; private final Hash _targetHash; private RouterInfo _router; private final long _expiration; private final int _priority; private final Job _onSend; private final ReplyJob _onSuccess; private final Job _onFail; private final MessageSelector _selector; private boolean _alreadySearched; private boolean _sent; private long _searchOn; /** * @param toPeer may be ourselves */ public SendMessageDirectJob(RouterContext ctx, I2NPMessage message, Hash toPeer, int timeoutMs, int priority) { this(ctx, message, toPeer, null, null, null, null, timeoutMs, priority); } /** * @param toPeer may be ourselves * @param onSuccess may be null * @param onFail may be null * @param selector be null */ public SendMessageDirectJob(RouterContext ctx, I2NPMessage message, Hash toPeer, ReplyJob onSuccess, Job onFail, MessageSelector selector, int timeoutMs, int priority) { this(ctx, message, toPeer, null, onSuccess, onFail, selector, timeoutMs, priority); } /** * @param toPeer may be ourselves * @param onSend may be null * @param onSuccess may be null * @param onFail may be null * @param selector be null */ public SendMessageDirectJob(RouterContext ctx, I2NPMessage message, Hash toPeer, Job onSend, ReplyJob onSuccess, Job onFail, MessageSelector selector, int timeoutMs, int priority) { super(ctx); _log = getContext().logManager().getLog(SendMessageDirectJob.class); _message = message; _targetHash = toPeer; if (timeoutMs < 10*1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Very little time given [" + timeoutMs + "], resetting to 5s", new Exception("stingy caller!")); _expiration = ctx.clock().now() + 10*1000; } else { _expiration = timeoutMs + ctx.clock().now(); } _priority = priority; _onSend = onSend; _onSuccess = onSuccess; _onFail = onFail; _selector = selector; if (message == null) throw new IllegalArgumentException("Attempt to send a null message"); if (_targetHash == null) throw new IllegalArgumentException("Attempt to send a message to a null peer"); } public String getName() { return "Send Message Direct"; } public void runJob() { long now = getContext().clock().now(); if (_expiration < now) { if (_log.shouldLog(Log.WARN)) _log.warn("Timed out sending message " + _message + " directly (expiration = " + new Date(_expiration) + ") to " + _targetHash.toBase64()); if (_onFail != null) getContext().jobQueue().addJob(_onFail); return; } if (_router != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Router specified, sending"); send(); } else { _router = getContext().netDb().lookupRouterInfoLocally(_targetHash); if (_router != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Router not specified but lookup found it"); send(); } else { if (!_alreadySearched) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Router not specified, so we're looking for it..."); getContext().netDb().lookupRouterInfo(_targetHash, this, this, _expiration - getContext().clock().now()); _searchOn = getContext().clock().now(); _alreadySearched = true; } else { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to find the router to send to: " + _targetHash + " after searching for " + (getContext().clock().now()-_searchOn) + "ms, message: " + _message); if (_onFail != null) getContext().jobQueue().addJob(_onFail); } } } } private void send() { if (_sent) { if (_log.shouldLog(Log.WARN)) _log.warn("Not resending!", new Exception("blah")); return; } _sent = true; Hash to = _router.getIdentity().getHash(); Hash us = getContext().routerHash(); if (us.equals(to)) { if (_selector != null) { OutNetMessage outM = new OutNetMessage(getContext(), _message, _expiration, _priority, _router); outM.setOnFailedReplyJob(_onFail); outM.setOnFailedSendJob(_onFail); outM.setOnReplyJob(_onSuccess); outM.setOnSendJob(_onSend); outM.setReplySelector(_selector); getContext().messageRegistry().registerPending(outM); } if (_onSend != null) getContext().jobQueue().addJob(_onSend); getContext().inNetMessagePool().add(_message, _router.getIdentity(), null); if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding " + _message.getClass().getName() + " to inbound message pool as it was destined for ourselves"); //_log.debug("debug", _createdBy); } else { OutNetMessage msg = new OutNetMessage(getContext(), _message, _expiration, _priority, _router); msg.setOnFailedReplyJob(_onFail); msg.setOnFailedSendJob(_onFail); msg.setOnReplyJob(_onSuccess); msg.setOnSendJob(_onSend); msg.setReplySelector(_selector); getContext().outNetMessagePool().add(msg); if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding " + _message.getClass().getName() + " to outbound message pool targeting " + _router.getIdentity().getHash().toBase64()); //_log.debug("Message pooled: " + _message); } } }