Package net.solosky.maplefetion.client

Source Code of net.solosky.maplefetion.client.LoginWork

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Project  : MapleFetion2
* Package  : net.solosky.maplefetion.client
* File     : LoginWork.java
* Author   : solosky < solosky772@qq.com >
* Created  : 2010-2-24
* License  : Apache License 2.0
*/
package net.solosky.maplefetion.client;


import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import net.solosky.maplefetion.ClientState;
import net.solosky.maplefetion.FetionConfig;
import net.solosky.maplefetion.FetionContext;
import net.solosky.maplefetion.LoginState;
import net.solosky.maplefetion.bean.Group;
import net.solosky.maplefetion.bean.Presence;
import net.solosky.maplefetion.bean.StoreVersion;
import net.solosky.maplefetion.bean.VerifyImage;
import net.solosky.maplefetion.client.dialog.Dialog;
import net.solosky.maplefetion.client.dialog.DialogException;
import net.solosky.maplefetion.client.dialog.GroupDialog;
import net.solosky.maplefetion.client.dialog.IllegalResponseException;
import net.solosky.maplefetion.client.dialog.ServerDialog;
import net.solosky.maplefetion.event.ActionEvent;
import net.solosky.maplefetion.event.ActionEventType;
import net.solosky.maplefetion.event.action.ActionEventFuture;
import net.solosky.maplefetion.event.action.ActionEventListener;
import net.solosky.maplefetion.event.action.FailureEvent;
import net.solosky.maplefetion.event.action.FailureType;
import net.solosky.maplefetion.event.action.FutureActionEventListener;
import net.solosky.maplefetion.event.notify.LoginStateEvent;
import net.solosky.maplefetion.net.RequestTimeoutException;
import net.solosky.maplefetion.net.TransferException;
import net.solosky.maplefetion.store.FetionStore;
import net.solosky.maplefetion.util.LocaleSetting;
import net.solosky.maplefetion.util.ObjectWaiter;

import org.apache.log4j.Logger;

/**
*
* 登录过程
*
* @author solosky <solosky772@qq.com>
*/
public class LoginWork implements Runnable
{
  /**
   * 飞信运行上下文
   */
  private FetionContext context;

  /**
   * SSI登录对象
   */
  private SSISign signAction;
 
  /**
   * 用户状态
   */
  private int presence;

  /**
   * 是否用户取消了登录
   */
  private boolean isCanceledLogin;
 
  /**
   * 等待用户处理SSI验证码事件
   */
  private ObjectWaiter<VerifyImage> ssiVerifyWaiter;
 
  /**
   * 同步登录的处理对象
   */
  private ObjectWaiter<LoginState> loginWaiter;
  /**
   * LOGGER
   */
  private static final Logger logger = Logger.getLogger(LoginWork.class);
 
  /**
   * 构造函数
   * @param context
   */
  public LoginWork(FetionContext context, int presence)
  {
    this.context = context;
    this.presence = presence;
    this.signAction = new SSISignV4();
    this.ssiVerifyWaiter = new ObjectWaiter<VerifyImage>();
    this.loginWaiter     = new ObjectWaiter<LoginState>();
   
    this.signAction.setLocaleSetting(this.context.getLocaleSetting());
    this.signAction.setFetionContext(this.context);
  }
 
 
  /**
   * 尝试登录
   * @throws InterruptedException
   * @throws SystemException
   * @throws DialogException
   * @throws RequestTimeoutException
   * @throws TransferException
   * @throws LoginException
   */
  public void login() throws LoginException, TransferException, RequestTimeoutException, DialogException, SystemException, InterruptedException
  {
    this.context.updateState(ClientState.LOGGING);
    this.updateSystemConfig()//获取自适应配置
      this.checkCanceledLogin();
    this.SSISign();        //SSI登录
      this.checkCanceledLogin();
    this.openServerDialog()//服务器连接并验证
       this.checkCanceledLogin();
    this.getContactsInfo();    //获取联系人列表和信息
      this.checkCanceledLogin();
    boolean groupEnabled = FetionConfig.getBoolean("fetion.group.enable");
    if(groupEnabled) {  //启用了群
      this.getGroupsInfo();     //获取群信息
      this.openGroupDialogs()//建立群会话
    }
 
    this.updateLoginState(LoginState.LOGIN_SUCCESS, null);
   
  }
 
  /**
   * 检查是否取消登录,如果取消登录抛出登录异常,结束登录过程
   * @throws LoginException
   */
  private void checkCanceledLogin() throws LoginException {
    if(this.isCanceledLogin
      throw new LoginException(LoginState.LOGIN_CANCELED);
  }
 

    @Override
    public void run()
    {
      try {
          this.login();
        } catch (LoginException e) {
          this.updateLoginState(e.getState(), e);
        } catch (TransferException e) {
          this.updateLoginState(LoginState.SIPC_CONNECT_FAIL, e);
        } catch (RequestTimeoutException e) {
          this.updateLoginState(LoginState.SIPC_TIMEOUT, e);
        } catch (DialogException e) {
          this.updateLoginState(LoginState.OTHER_ERROR, e);
        } catch (SystemException e) {
          this.updateLoginState(LoginState.OTHER_ERROR, e);
        } catch (InterruptedException e) {
        this.updateLoginState(LoginState.OTHER_ERROR, e);
        }
    }
   
   
    ////////////////////////////////////////////////////////////////////
    /**
     * 更新自适应配置
     */
    private void updateSystemConfig() throws LoginException
    {
      LocaleSetting localeSetting = this.context.getLocaleSetting();
      if(!localeSetting.isLoaded()) {
        try {
          logger.debug("Loading locale setting...");
        this.updateLoginState(LoginState.SEETING_LOAD_DOING, null);
        localeSetting.load(this.context.getFetionUser());
        if(!localeSetting.isValid())  //获取配置中如果无效,表明用户输入的账号无效
          throw new LoginException(LoginState.SSI_ACCOUNT_NOT_FOUND);
       
        this.updateLoginState(LoginState.SETTING_LOAD_SUCCESS, null);
      } catch (Exception e) {
        logger.debug("Load localeSetting error", e);
        throw new LoginException(LoginState.SETTING_LOAD_FAIL, e);
      }
      }
    }
   
    /**
     * SSI登录
     * @throws LoginException
     */
    private void SSISign() throws LoginException
    {
      this.updateLoginState(LoginState.SSI_SIGN_IN_DOING, null);
     
      //第一次尝试不用验证码登录
      LoginState state = this.signAction.signIn(this.context.getFetionUser());
      //如果结果为需要验证或者验证失败,重复验证,直到SSI的结果为其他结果为止
      while(state==LoginState.SSI_NEED_VERIFY || state==LoginState.SSI_VERIFY_FAIL) {
            try {
              VerifyImage img = this.ssiVerifyWaiter.waitObject();
              state = this.signAction.signIn(this.context.getFetionUser(), img);
            } catch (ExecutionException e) {
               this.checkCanceledLogin();
               throw new LoginException(LoginState.OTHER_ERROR, e);
            } catch (TimeoutException e) {
               this.checkCanceledLogin();
               throw new LoginException(LoginState.LOGIN_TIMEOUT, e);
            } catch (InterruptedException e) {
               this.checkCanceledLogin();
               throw new LoginException(LoginState.OTHER_ERROR, e);
            }
      }
     
      //如果SSI登录出错,抛出登录异常,结束登录流程
      //因为上面判断了SSI_NEED_VERIFY和SSI_VERIFY_FAIL事件,所以这里只需判断是否SSI登录成功即可
      if(state!=LoginState.SSI_SIGN_IN_SUCCESSthrow new LoginException(state);
       
    this.updateLoginState(state, null);
    }
   
    /**
     * 建立服务器会话
     * @throws DialogException
     * @throws RequestTimeoutException
     * @throws TransferException
     * @throws InterruptedException
     * @throws SystemException
     */
    private void openServerDialog() throws LoginException, TransferException, RequestTimeoutException, DialogException, SystemException, InterruptedException
    {
      //判断是否有飞信号
      if(this.context.getFetionUser().getFetionId()==0){
        throw new IllegalArgumentException("Invalid fetion id. if disabled SSI sign, you must login with fetion id..");
      }
     
      this.updateLoginState(LoginState.SIPC_REGISTER_DOING, null);
    ServerDialog serverDialog = this.context.getDialogFactory().createServerDialog();
        serverDialog.openDialog();
       
        ActionEventFuture future = new ActionEventFuture();
      ActionEventListener listener = new FutureActionEventListener(future);
     
      //注册服务器
      serverDialog.register(presence, listener);
      Dialog.assertActionEvent(future.waitActionEventWithException(), ActionEventType.SUCCESS);
     
      //用户验证
      future.clear();
      serverDialog.userAuth(presence, listener);
      ActionEvent event = future.waitActionEventWithoutException();
      if(event.getEventType()==ActionEventType.SUCCESS){
        this.updateLoginState(LoginState.SIPC_REGISGER_SUCCESS, null);
      }else if(event.getEventType()==ActionEventType.FAILURE){
        FailureEvent evt = (FailureEvent) event;
        FailureType type =  evt.getFailureType();
        if(type==FailureType.REGISTER_FORBIDDEN){
          throw new LoginException(LoginState.SIPC_ACCOUNT_FORBIDDEN); //帐号限制登录,可能存在不安全因素,请修改密码后再登录
        }else if(type==FailureType.AUTHORIZATION_FAIL){
          throw new LoginException(LoginState.SIPC_AUTH_FAIL);      //登录验证失败
        }else{
          Dialog.assertActionEvent(event, ActionEventType.SUCCESS);
        }
      }
    }
   
    /**
     * 获取联系人信息
     * @throws InterruptedException
     * @throws SystemException
     * @throws TransferException
     * @throws RequestTimeoutException
     * @throws IllegalResponseException
     */
    private void getContactsInfo() throws IllegalResponseException, RequestTimeoutException, TransferException, SystemException, InterruptedException
    {
      ActionEventFuture future = new ActionEventFuture();
      ActionEventListener listener = new FutureActionEventListener(future);
      ServerDialog dialog = this.context.getDialogFactory().getServerDialog();
    this.updateLoginState(LoginState.GET_CONTACTS_INFO_DOING, null);
   
       
        //订阅异步通知
    if(this.context.getFetionStore().getBuddyList().size()>0){
          future.clear();
          dialog.subscribeBuddyNotify(listener);
          Dialog.assertActionEvent(future.waitActionEventWithException(), ActionEventType.SUCCESS);
    }
       
        this.updateLoginState(LoginState.GET_CONTACTS_INFO_SUCCESS, null);
    }
   
    /**
     * 获取群信息
     * @throws InterruptedException
     * @throws SystemException
     * @throws TransferException
     * @throws RequestTimeoutException
     * @throws IllegalResponseException
     */
    private void getGroupsInfo() throws IllegalResponseException, RequestTimeoutException, TransferException, SystemException, InterruptedException
    {
      ActionEventFuture future = new ActionEventFuture();
      ActionEventListener listener = new FutureActionEventListener(future);
      ServerDialog dialog = this.context.getDialogFactory().getServerDialog();
      StoreVersion storeVersion   = this.context.getFetionStore().getStoreVersion();
      StoreVersion userVersion    = this.context.getFetionUser().getStoreVersion();

      this.updateLoginState(LoginState.GET_GROUPS_INFO_DOING, null);
        //获取群列表
        future.clear();
        dialog.getGroupList(listener);
        Dialog.assertActionEvent(future.waitActionEventWithException(), ActionEventType.SUCCESS);
       
    //如果群列表为空,就不发送下面的一些请求了
    FetionStore store = this.context.getFetionStore();
    if(store.getGroupList().size()==0){
      logger.debug("The group list is empty, group dialog login is skipped.");
      return;
    }

        //如果当前存储版本和服务器相同,就不获取群信息和群成员列表,
        //TODO ..这里只是解决了重新登录的问题,事实上这里问题很大,群信息分成很多
        //用户加入的群列表 groupListVersion
        //某群的信息      groupInfoVersion
        //群成员列表      groupMemberListVersion
        //暂时就这样,逐步完善中.....
        logger.debug("GroupListVersion: server="+userVersion.getGroupVersion()+", local="+storeVersion.getGroupVersion());
        if(storeVersion.getGroupVersion()!=userVersion.getGroupVersion()) {
      //更新存储版本
      storeVersion.setGroupVersion(userVersion.getGroupVersion());
          //获取群信息
          future.clear();
          dialog.getGroupsInfo(this.context.getFetionStore().getGroupList(), listener);
          Dialog.assertActionEvent(future.waitActionEventWithException(), ActionEventType.SUCCESS);
       
          //获取群成员
          future.clear();
          dialog.getMemberList(this.context.getFetionStore().getGroupList(), listener);
          Dialog.assertActionEvent(future.waitActionEventWithException(), ActionEventType.SUCCESS);
         
          storeVersion.setGroupVersion(userVersion.getGroupVersion());
        }
       
      this.updateLoginState(LoginState.GET_GROUPS_INFO_SUCCESS,null);
    }
   
    /**
     * 打开群会话
     * @throws DialogException
     * @throws RequestTimeoutException
     * @throws TransferException
     */
    private void openGroupDialogs() throws TransferException, RequestTimeoutException, DialogException
    {
      this.updateLoginState(LoginState.GROUPS_REGISTER_DOING, null);
    Iterator<Group> it = this.context.getFetionStore().getGroupList().iterator();
        while (it.hasNext()) {
          GroupDialog groupDialog = this.context.getDialogFactory().createGroupDialog(it.next());
          groupDialog.openDialog();
        }
       
        this.updateLoginState(LoginState.GROUPS_REGISTER_SUCCESS, null);
}
   
    /**
     * 更新登录状态
     * @param status
     */
    private void updateLoginState(LoginState state, Throwable t)
    {
      logger.debug("Login state:"+state+", canceledLogin="+isCanceledLogin, t);
     
      if(this.context.getNotifyEventListener()!=null && !this.isCanceledLogin)
        this.context.getNotifyEventListener().fireEvent(new LoginStateEvent(state));
       
      if(state.getValue()>400 && !this.isCanceledLogin) {  //大于400都是登录出错
        this.context.handleException(new LoginException(state));
        this.loginWaiter.objectArrive(state);
      }else if(state==LoginState.LOGIN_SUCCESS) {
        this.context.updateState(ClientState.ONLINE);
        this.context.getDialogFactory().getServerDialog().startKeepAlive();
        this.loginWaiter.objectArrive(state);    //最后才通知同步登陆线程,防止客户端状态没有刷新
      }else {
        //logger.warn("Unhandled login state="+state);
      }
    }
    ////////////////////////////////////////////////////////////////////

  /**
     * @param verifyImage the verifyImage to set
     */
    public void setVerifyImage(VerifyImage verifyImage)
    {
      this.ssiVerifyWaiter.objectArrive(verifyImage);
    }

  /**
     * @param presence the presence to set
     */
    public void setPresence(int presence)
    {
      if(Presence.isValidPresenceValue(presence)) {
        this.presence = presence;
      }else {
        throw new IllegalArgumentException("presence "+presence+" is invalid. Presense const is defined in Presence class.");
      }
    }
   
    /**
     * @return the presence
     */
    public int getPresence()
    {
      return presence;
    }


  /**
     * 释放资源
     */
    public void dispose()
    {
    }
   
    /**
     * 等待登陆结果通知
     * 事实上这个方法不会永远超时,因为客户端登陆已经包含了超时控制
     * @return
     */
    public LoginState waitLoginState(long timeout)
    {
      try {
          return this.loginWaiter.waitObject(timeout);
        } catch (ExecutionException e) {
          return LoginState.OTHER_ERROR;
        } catch (TimeoutException e) {
          return LoginState.LOGIN_TIMEOUT;
        } catch (InterruptedException e) {
          return LoginState.OTHER_ERROR;
        }
    }
   
    /**
     * 取消登录
     */
    public void cancelLogin() {
      this.isCanceledLogin = true;
      this.loginWaiter.objectArrive(LoginState.LOGIN_CANCELED);
      this.ssiVerifyWaiter.objectException(new LoginException(LoginState.LOGIN_CANCELED));
    }
}
TOP

Related Classes of net.solosky.maplefetion.client.LoginWork

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.