/** @override {@link Thread}{@link #run()} to perform all asynchronous tasks. */
    @Override
    public void run() {
      ChatRoom chatRoom = chatRoomWrapper.getChatRoom();

      try {
        if (password != null && password.length > 0) chatRoom.joinAs(nickName, password);
        else if (nickName != null) chatRoom.joinAs(nickName);
        else chatRoom.join();

        done(JOIN_SUCCESS_PROP);
      } catch (OperationFailedException e) {
        if (logger.isTraceEnabled())
          logger.trace("Failed to join chat room: " + chatRoom.getName(), e);

        switch (e.getErrorCode()) {
          case OperationFailedException.AUTHENTICATION_FAILED:
            done(JOIN_AUTHENTICATION_FAILED_PROP);
            break;
          case OperationFailedException.REGISTRATION_REQUIRED:
            done(JOIN_REGISTRATION_REQUIRED_PROP);
            break;
          case OperationFailedException.PROVIDER_NOT_REGISTERED:
            done(JOIN_PROVIDER_NOT_REGISTERED_PROP);
            break;
          case OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS:
            done(JOIN_SUBSCRIPTION_ALREADY_EXISTS_PROP);
            break;
          default:
            done(JOIN_UNKNOWN_ERROR_PROP);
        }
      }
    }
    JoinChatRoomTask(
        ChatRoomWrapperImpl chatRoomWrapper,
        String nickName,
        byte[] password,
        boolean rememberPassword,
        boolean isFirstAttempt,
        String subject) {
      this.chatRoomWrapper = chatRoomWrapper;
      this.nickName = nickName;
      this.isFirstAttempt = isFirstAttempt;
      this.subject = subject;

      if (password == null) {
        String passString = chatRoomWrapper.loadPassword();
        if (passString != null) {
          this.password = passString.getBytes();
        } else {
          this.password = null;
        }
      } else {
        this.password = password;
      }
      this.rememberPassword = rememberPassword;
    }
    /**
     * Performs UI changes after the chat room join task has finished.
     *
     * @param returnCode the result code from the chat room join task.
     */
    private void done(String returnCode) {
      ConfigurationUtils.updateChatRoomStatus(
          chatRoomWrapper.getParentProvider().getProtocolProvider(),
          chatRoomWrapper.getChatRoomID(),
          GlobalStatusEnum.ONLINE_STATUS);

      String errorMessage = null;
      if (JOIN_AUTHENTICATION_FAILED_PROP.equals(returnCode)) {
        chatRoomWrapper.removePassword();

        AuthenticationWindowService authWindowsService =
            ServiceUtils.getService(MUCActivator.bundleContext, AuthenticationWindowService.class);

        AuthenticationWindowService.AuthenticationWindow authWindow =
            authWindowsService.create(
                null,
                null,
                null,
                false,
                chatRoomWrapper.isPersistent(),
                AuthenticationWindow.getAuthenticationWindowIcon(
                    chatRoomWrapper.getParentProvider().getProtocolProvider()),
                resources.getI18NString(
                    "service.gui.AUTHENTICATION_WINDOW_TITLE",
                    new String[] {chatRoomWrapper.getParentProvider().getName()}),
                resources.getI18NString(
                    "service.gui.CHAT_ROOM_REQUIRES_PASSWORD",
                    new String[] {chatRoomWrapper.getChatRoomName()}),
                "",
                null,
                isFirstAttempt
                    ? null
                    : resources.getI18NString(
                        "service.gui.AUTHENTICATION_FAILED",
                        new String[] {chatRoomWrapper.getChatRoomName()}),
                null);

        authWindow.setVisible(true);

        if (!authWindow.isCanceled()) {
          joinChatRoom(
              chatRoomWrapper,
              nickName,
              new String(authWindow.getPassword()).getBytes(),
              authWindow.isRememberPassword(),
              false,
              subject);
        }
      } else if (JOIN_REGISTRATION_REQUIRED_PROP.equals(returnCode)) {
        errorMessage =
            resources.getI18NString(
                "service.gui.CHAT_ROOM_REGISTRATION_REQUIRED",
                new String[] {chatRoomWrapper.getChatRoomName()});
      } else if (JOIN_PROVIDER_NOT_REGISTERED_PROP.equals(returnCode)) {
        errorMessage =
            resources.getI18NString(
                "service.gui.CHAT_ROOM_NOT_CONNECTED",
                new String[] {chatRoomWrapper.getChatRoomName()});
      } else if (JOIN_SUBSCRIPTION_ALREADY_EXISTS_PROP.equals(returnCode)) {
        errorMessage =
            resources.getI18NString(
                "service.gui.CHAT_ROOM_ALREADY_JOINED",
                new String[] {chatRoomWrapper.getChatRoomName()});
      } else {
        errorMessage =
            resources.getI18NString(
                "service.gui.FAILED_TO_JOIN_CHAT_ROOM",
                new String[] {chatRoomWrapper.getChatRoomName()});
      }

      if (!JOIN_SUCCESS_PROP.equals(returnCode)
          && !JOIN_AUTHENTICATION_FAILED_PROP.equals(returnCode)) {
        MUCActivator.getAlertUIService()
            .showAlertPopup(resources.getI18NString("service.gui.ERROR"), errorMessage);
      }

      if (JOIN_SUCCESS_PROP.equals(returnCode)) {
        if (rememberPassword) {
          chatRoomWrapper.savePassword(new String(password));
        }

        if (subject != null) {
          try {
            chatRoomWrapper.getChatRoom().setSubject(subject);
          } catch (OperationFailedException ex) {
            logger.warn("Failed to set subject.");
          }
        }
      }

      chatRoomWrapper.firePropertyChange(returnCode);
    }