/** * Adds a specific <tt>Subscription</tt> to the list of subscriptions managed by this instance * only if another <tt>Subscription</tt> with the same subscription <tt>Address</tt>/Request URI * and id tag of its associated Event header does not exist in the list. * * @param subscription the new <tt>Subscription</tt> to be added to the list of subscriptions * managed by this instance if there is no other <tt>Subscription</tt> in the list which has * the same subscription <tt>Address</tt>/Request URI and id tag of its Event header * @throws OperationFailedException if we fail constructing or sending the subscription request */ public void poll(Subscription subscription) throws OperationFailedException { if (getSubscription(subscription.getAddress(), subscription.getEventId()) == null) subscribe(subscription); }
/** * Implements {@link MethodProcessor#processResponse(ResponseEvent)}. Handles only responses to * SUBSCRIBE requests because they are the only requests concerning event package subscribers (and * the only requests sent by them, for that matter) and if the processing of a given response * requires event package-specific handling, delivers the response to the matching * <tt>Subscription</tt> instance. Examples of such event package-specific handling include * letting the respective <tt>Subscription</tt> handle the success or failure in the establishment * of a subscription. * * @param responseEvent a <tt>ResponseEvent</tt> specifying the SIP <tt>Response</tt> to be * processed * @return <tt>true</tt> if the SIP <tt>Response</tt> specified by <tt>responseEvent</tt> was * processed; otherwise, <tt>false</tt> */ @Override public boolean processResponse(ResponseEvent responseEvent) { Response response = responseEvent.getResponse(); CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); if (cseqHeader == null) { logger.error("An incoming response did not contain a CSeq header"); return false; } if (!Request.SUBSCRIBE.equals(cseqHeader.getMethod())) return false; ClientTransaction clientTransaction = responseEvent.getClientTransaction(); Request request = clientTransaction.getRequest(); /* * Don't handle responses to requests not coming from this event * package. */ if (request != null) { EventHeader eventHeader = (EventHeader) request.getHeader(EventHeader.NAME); if ((eventHeader == null) || !eventPackage.equalsIgnoreCase(eventHeader.getEventType())) return false; } // Find the subscription. CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME); String callId = callIdHeader.getCallId(); Subscription subscription = getSubscription(callId); // if it's the response to an unsubscribe message, we just ignore it // whatever the response is however if we need to handle a // challenge, we do it ExpiresHeader expHeader = response.getExpires(); int statusCode = response.getStatusCode(); SipProvider sourceProvider = (SipProvider) responseEvent.getSource(); if (((expHeader != null) && (expHeader.getExpires() == 0)) || (subscription == null)) // this handle the unsubscription // case where we removed the contact // from subscribedContacts { boolean processed = false; if ((statusCode == Response.UNAUTHORIZED) || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED)) { try { processAuthenticationChallenge(clientTransaction, response, sourceProvider); processed = true; } catch (OperationFailedException e) { logger.error("can't handle the challenge", e); } } else if ((statusCode != Response.OK) && (statusCode != Response.ACCEPTED)) processed = true; // any other cases (200/202) will imply a NOTIFY, so we will // handle the end of a subscription there return processed; } if ((statusCode >= Response.OK) && (statusCode < Response.MULTIPLE_CHOICES)) { // OK (200/202) if ((statusCode == Response.OK) || (statusCode == Response.ACCEPTED)) { if (expHeader == null) { // not conform to rfc3265 logger.error("no Expires header in this response"); return false; } SubscriptionRefreshTask refreshTask = new SubscriptionRefreshTask(subscription); subscription.setTimerTask(refreshTask); int refreshDelay = expHeader.getExpires(); // try to keep a margin if the refresh delay allows it if (refreshDelay >= (2 * refreshMargin)) refreshDelay -= refreshMargin; timer.schedule(refreshTask, refreshDelay * 1000); // do it to remember the dialog in case of a polling // subscription (which means no call to finalizeSubscription) subscription.setDialog(clientTransaction.getDialog()); subscription.processSuccessResponse(responseEvent, statusCode); } } else if ((statusCode >= Response.MULTIPLE_CHOICES) && (statusCode < Response.BAD_REQUEST)) { if (logger.isInfoEnabled()) logger.info( "Response to subscribe to " + subscription.getAddress() + ": " + response.getReasonPhrase()); } else if (statusCode >= Response.BAD_REQUEST) { // if the response is a 423 response, just re-send the request // with a valid expires value if (statusCode == Response.INTERVAL_TOO_BRIEF) { MinExpiresHeader min = (MinExpiresHeader) response.getHeader(MinExpiresHeader.NAME); if (min == null) { logger.error("no minimal expires value in this 423 " + "response"); return false; } ExpiresHeader exp = request.getExpires(); try { exp.setExpires(min.getExpires()); } catch (InvalidArgumentException e) { logger.error("can't set the new expires value", e); return false; } ClientTransaction transac = null; try { transac = protocolProvider.getDefaultJainSipProvider().getNewClientTransaction(request); } catch (TransactionUnavailableException e) { logger.error("can't create the client transaction", e); return false; } try { transac.sendRequest(); } catch (SipException e) { logger.error("can't send the new request", e); return false; } return true; // UNAUTHORIZED (401/407) } else if ((statusCode == Response.UNAUTHORIZED) || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED)) { try { processAuthenticationChallenge(clientTransaction, response, sourceProvider); } catch (OperationFailedException e) { logger.error("can't handle the challenge", e); removeSubscription(callId, subscription); subscription.processFailureResponse(responseEvent, statusCode); } // 408 480 486 600 603 : non definitive reject // others: definitive reject (or not implemented) } else { if (logger.isDebugEnabled()) logger.debug("error received from the network:\n" + response); removeSubscription(callId, subscription); subscription.processFailureResponse(responseEvent, statusCode); } } return true; }
/** * Creates a new SUBSCRIBE request in the form of a <tt>ClientTransaction</tt> with the parameters * of a specific <tt>Subscription</tt>. * * @param subscription the <tt>Subscription</tt> to be described in a SUBSCRIBE request * @param expires the subscription duration of the SUBSCRIBE request to be created * @return a new <tt>ClientTransaction</tt> initialized with a new SUBSCRIBE request which matches * the parameters of the specified <tt>Subscription</tt> * @throws OperationFailedException if the request could not be generated */ private ClientTransaction createSubscription(Subscription subscription, int expires) throws OperationFailedException { Address toAddress = subscription.getAddress(); HeaderFactory headerFactory = protocolProvider.getHeaderFactory(); // Call ID CallIdHeader callIdHeader = protocolProvider.getDefaultJainSipProvider().getNewCallId(); // CSeq CSeqHeader cSeqHeader; try { cSeqHeader = headerFactory.createCSeqHeader(1l, Request.SUBSCRIBE); } catch (InvalidArgumentException ex) { // Shouldn't happen logger.error("An unexpected error occurred while" + "constructing the CSeqHeader", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the CSeqHeader", OperationFailedException.INTERNAL_ERROR, ex); } catch (ParseException ex) { // shouldn't happen logger.error("An unexpected error occurred while" + "constructing the CSeqHeader", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the CSeqHeader", OperationFailedException.INTERNAL_ERROR, ex); } // FromHeader and ToHeader String localTag = SipMessageFactory.generateLocalTag(); FromHeader fromHeader; ToHeader toHeader; try { // FromHeader fromHeader = headerFactory.createFromHeader(protocolProvider.getOurSipAddress(toAddress), localTag); // ToHeader toHeader = headerFactory.createToHeader(toAddress, null); } catch (ParseException ex) { // these two should never happen. logger.error( "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", OperationFailedException.INTERNAL_ERROR, ex); } // ViaHeaders ArrayList<ViaHeader> viaHeaders = protocolProvider.getLocalViaHeaders(toAddress); // MaxForwards MaxForwardsHeader maxForwards = protocolProvider.getMaxForwardsHeader(); Request req; try { req = protocolProvider .getMessageFactory() .createRequest( toHeader.getAddress().getURI(), Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); } catch (ParseException ex) { // shouldn't happen logger.error("Failed to create message Request!", ex); throw new OperationFailedException( "Failed to create message Request!", OperationFailedException.INTERNAL_ERROR, ex); } populateSubscribeRequest(req, subscription, expires); // Transaction ClientTransaction subscribeTransaction; try { subscribeTransaction = protocolProvider.getDefaultJainSipProvider().getNewClientTransaction(req); } catch (TransactionUnavailableException ex) { logger.error( "Failed to create subscribe transaction.\n" + "This is most probably a network connection error.", ex); throw new OperationFailedException( "Failed to create the subscription transaction", OperationFailedException.NETWORK_FAILURE); } return subscribeTransaction; }