private ArrayList getLocalViaHeaders() throws IOException {
    /*
     * We can't keep a cached copy because the callers
     * of this method change the viaHeaders.  In particular
     * a branch may be added which causes INVITES to fail.
     */
    if (viaHeaders != null) {
      return viaHeaders;
    }

    ListeningPoint lp = sipProvider.getListeningPoint();
    viaHeaders = new ArrayList();

    try {
      String addr = lp.getIPAddress();

      ViaHeader viaHeader =
          headerFactory.createViaHeader(addr, lp.getPort(), lp.getTransport(), null);

      viaHeader.setRPort();

      viaHeaders.add(viaHeader);
      return viaHeaders;
    } catch (ParseException e) {
      throw new IOException(
          "A ParseException occurred while creating Via Headers! " + e.getMessage());
    } catch (InvalidArgumentException e) {
      throw new IOException(
          "Unable to create a via header for port " + lp.getPort() + " " + e.getMessage());
    }
  }
  private FromHeader getFromHeader() throws IOException {

    if (fromHeader != null) {
      return fromHeader;
    }

    try {
      SipURI fromURI =
          (SipURI)
              addressFactory.createURI("sip:" + proxyCredentials.getUserName() + "@" + registrar);

      fromURI.setTransportParam(sipProvider.getListeningPoint().getTransport());

      fromURI.setPort(sipProvider.getListeningPoint().getPort());

      Address fromAddress = addressFactory.createAddress(fromURI);

      fromAddress.setDisplayName(proxyCredentials.getUserDisplay());

      fromHeader = headerFactory.createFromHeader(fromAddress, Integer.toString(hashCode()));

    } catch (ParseException e) {
      throw new IOException(
          "A ParseException occurred while creating From Header! " + e.getMessage());
    }

    return fromHeader;
  }
  /**
   * Populates a specific <tt>Request</tt> instance with the headers common to dialog-creating
   * <tt>Request</tt>s and ones sent inside existing dialogs and specific to the general event
   * package subscription functionality that this instance and a specific <tt>Subscription</tt>
   * represent.
   *
   * @param req the <tt>Request</tt> instance to be populated with common headers and ones specific
   *     to the event package of a specific <tt>Subscription</tt>
   * @param subscription the <tt>Subscription</tt> which is to be described in the specified
   *     <tt>Request</tt> i.e. its properties are to be used to populate the specified
   *     <tt>Request</tt>
   * @param expires the subscription duration to be set into the Expires header of the specified
   *     SUBSCRIBE <tt>Request</tt>
   * @throws OperationFailedException if we fail parsing or populating the subscription request.
   */
  protected void populateSubscribeRequest(Request req, Subscription subscription, int expires)
      throws OperationFailedException {
    HeaderFactory headerFactory = protocolProvider.getHeaderFactory();

    // Event
    EventHeader evHeader;
    try {
      evHeader = headerFactory.createEventHeader(eventPackage);

      String eventId = subscription.getEventId();
      if (eventId != null) evHeader.setEventId(eventId);
    } catch (ParseException e) {
      // these two should never happen.
      logger.error("An unexpected error occurred while" + "constructing the EventHeader", e);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the EventHeader",
          OperationFailedException.INTERNAL_ERROR,
          e);
    }
    req.setHeader(evHeader);

    // Accept
    AcceptHeader accept;
    try {
      accept = headerFactory.createAcceptHeader("application", contentSubType);
    } catch (ParseException e) {
      logger.error("wrong accept header", e);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the AcceptHeader",
          OperationFailedException.INTERNAL_ERROR,
          e);
    }
    req.setHeader(accept);

    // Expires
    ExpiresHeader expHeader;
    try {
      expHeader = headerFactory.createExpiresHeader(expires);
    } catch (InvalidArgumentException e) {
      logger.error("Invalid expires value: " + expires, e);
      throw new OperationFailedException(
          "An unexpected error occurred while" + "constructing the ExpiresHeader",
          OperationFailedException.INTERNAL_ERROR,
          e);
    }
    req.setHeader(expHeader);
  }
  private MaxForwardsHeader getMaxForwardsHeader() throws IOException {
    if (maxForwardsHeader != null) {
      return maxForwardsHeader;
    }

    try {
      maxForwardsHeader = headerFactory.createMaxForwardsHeader(MAX_FORWARDS);
      return maxForwardsHeader;
    } catch (InvalidArgumentException e) {
      throw new IOException(
          "A problem occurred while creating MaxForwardsHeader " + e.getMessage());
    }
  }
  private ContactHeader getRegistrationContactHeader() throws IOException {
    if (contactHeader != null) {
      return contactHeader;
    }

    try {
      SipURI contactURI =
          (SipURI)
              addressFactory.createURI(
                  "sip:" + proxyCredentials.getUserName() + "@" + proxyCredentials.getHost());

      contactURI.setTransportParam(sipProvider.getListeningPoint().getTransport());
      contactURI.setPort(sipProvider.getListeningPoint().getPort());
      Address contactAddress = addressFactory.createAddress(contactURI);
      contactAddress.setDisplayName(proxyCredentials.getUserDisplay());
      contactHeader = headerFactory.createContactHeader(contactAddress);
      return contactHeader;
    } catch (ParseException e) {
      throw new IOException(
          "A ParseException occurred while creating From Header! " + " " + e.getMessage());
    }
  }
  private void register() throws IOException {
    Log.info("Registering with " + registrar);
    FromHeader fromHeader = getFromHeader();
    Address fromAddress = fromHeader.getAddress();

    // Request URI
    SipURI requestURI = null;
    try {
      requestURI = addressFactory.createSipURI(null, registrar);

    } catch (ParseException e) {
      throw new IOException("Bad registrar address:" + registrar + " " + e.getMessage());
    }
    // requestURI.setPort(registrarPort);

    try {
      requestURI.setTransportParam(sipProvider.getListeningPoint().getTransport());

    } catch (ParseException e) {
      throw new IOException(
          sipProvider.getListeningPoint().getTransport()
              + " is not a valid transport! "
              + e.getMessage());
    }

    CallIdHeader callIdHeader = sipProvider.getNewCallId();
    CSeqHeader cSeqHeader = null;

    try {
      cSeqHeader = headerFactory.createCSeqHeader(1, Request.REGISTER);
    } catch (ParseException e) {
      // Should never happen
      throw new IOException("Corrupt Sip Stack " + e.getMessage());
    } catch (InvalidArgumentException e) {
      // Should never happen
      throw new IOException("The application is corrupt ");
    }

    ToHeader toHeader = null;
    try {
      String proxyWorkAround = System.getProperty("com.sun.mc.softphone.REGISTRAR_WORKAROUND");

      if (proxyWorkAround != null && proxyWorkAround.toUpperCase().equals("TRUE")) {

        SipURI toURI = (SipURI) (requestURI.clone());
        toURI.setUser(System.getProperty("user.name"));

        toHeader = headerFactory.createToHeader(addressFactory.createAddress(toURI), null);
      } else {
        toHeader = headerFactory.createToHeader(fromAddress, null);
      }
    } catch (ParseException e) {
      throw new IOException(
          "Could not create a To header for address:"
              + fromHeader.getAddress()
              + " "
              + e.getMessage());
    }

    ArrayList viaHeaders = getLocalViaHeaders();
    MaxForwardsHeader maxForwardsHeader = getMaxForwardsHeader();
    Request request = null;

    try {
      request =
          messageFactory.createRequest(
              requestURI,
              Request.REGISTER,
              callIdHeader,
              cSeqHeader,
              fromHeader,
              toHeader,
              viaHeaders,
              maxForwardsHeader);
    } catch (ParseException e) {
      throw new IOException("Could not create the register request! " + e.getMessage());
    }

    ExpiresHeader expHeader = null;

    for (int retry = 0; retry < 2; retry++) {
      try {
        expHeader = headerFactory.createExpiresHeader(expires);
      } catch (InvalidArgumentException e) {
        if (retry == 0) {
          continue;
        }
        throw new IOException(
            "Invalid registrations expiration parameter - " + expires + " " + e.getMessage());
      }
    }

    request.addHeader(expHeader);
    ContactHeader contactHeader = getRegistrationContactHeader();
    request.addHeader(contactHeader);

    try {
      SipURI routeURI =
          (SipURI) addressFactory.createURI("sip:" + proxyCredentials.getProxy() + ";lr");
      RouteHeader routeHeader =
          headerFactory.createRouteHeader(addressFactory.createAddress(routeURI));
      request.addHeader(routeHeader);

    } catch (Exception e) {

      Log.error("Creating registration route error ", e);
    }

    ClientTransaction regTrans = null;
    try {
      regTrans = sipProvider.getNewClientTransaction(request);
    } catch (TransactionUnavailableException e) {
      throw new IOException(
          "Could not create a register transaction!\n"
              + "Check that the Registrar address is correct! "
              + e.getMessage());
    }

    try {
      sipCallId = callIdHeader.getCallId();
      sipServerCallback.addSipListener(sipCallId, this);
      registerRequest = request;
      regTrans.sendRequest();

      Log.debug("Sent register request " + registerRequest);

      if (expires > 0) {
        scheduleReRegistration();
      }
    } catch (Exception e) {
      throw new IOException("Could not send out the register request! " + e.getMessage());
    }

    this.registerRequest = request;
  }
  /**
   * 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;
  }