/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package aipo.batch.userinfo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.jetspeed.om.security.Group;
import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.om.security.Role;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;
import org.apache.jetspeed.services.security.GroupException;
import org.apache.jetspeed.services.security.JetspeedSecurityException;
import org.apache.turbine.services.TurbineServices;

import aipo.batch.utils.ALBatchAccountUtils;
import aipo.batch.utils.ALBatchGroupUtils;
import aipo.batch.utils.ALBatchMailUtils;
import aipo.batch.utils.ALBatchUserUtils;
import aipo.batch.utils.BatchUtils;
import aipo.webservice.util.WsUtils;

import com.aimluck.eip.account.util.AccountUtils;
import com.aimluck.eip.cayenne.om.account.EipMPosition;
import com.aimluck.eip.cayenne.om.account.EipMUserPosition;
import com.aimluck.eip.cayenne.om.portlet.EipMMailAccount;
import com.aimluck.eip.cayenne.om.security.TurbineGroup;
import com.aimluck.eip.cayenne.om.security.TurbineRole;
import com.aimluck.eip.cayenne.om.security.TurbineUser;
import com.aimluck.eip.cayenne.om.security.TurbineUserGroupRole;
import com.aimluck.eip.mail.util.ALMailUtils;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.orm.query.SelectQuery;
import com.aimluck.eip.services.accessctl.ALAccessControlFactoryService;
import com.aimluck.eip.services.accessctl.ALAccessControlHandler;
import com.aimluck.eip.services.datasync.ALDataSyncFactoryService;
import com.aimluck.eip.user.beans.UserGroupLiteBean;
import com.aimluck.eip.util.ALEipUtils;

/**
 * <HR>
 * <p>
 * 
 * ユーザー情報のファイルを読み込み、Aipoのデータベースにインポートします。<br>
 * <P>
 * <HR>
 * <P>
 */
public class UserImporter {
  /** ロガー */
  private static final JetspeedLogger logger =
    JetspeedLogFactoryService.getLogger(UserImporter.class.getName());

  /** プロパティファイル */
  private static final String PROPERTY_FILE =
    JetspeedResources.getString("aipo.conf", "")
      + File.separator
      + "Batch.properties";

  /** ユーザーファイル名 */
  private static final String USER_FILE_NAME =
    (String) BatchUtils.getProperties(PROPERTY_FILE).get(
      "import.user.file.name");

  /** パスワード初期設定 */
  private static final String INITIAL_PASSWORD_ADD_STRING =
    (String) BatchUtils.getProperties(PROPERTY_FILE).get(
      "password.initial.addstring");

  /** ユーザー削除フラグ：False */
  private static final String USER_DISABLED_FALSE = "F";

  /** デフォルトグループ名：LoginUser */
  private static final String DEFAULT_GROUP_NAME = "LoginUser";

  /** デフォルトロール名：user */
  private static final String DEFAULT_ROLE_NAME = "user";

  /** Webメール設定ファイル */
  private static final String SETTING_FILE_PATH =
    JetspeedResources.getString("aipo.conf", "")
      + File.separator
      + "WebMailAdminSettings.properties";

  /** SMTPサーバ名 */
  private static final String DEFAULT_SMTP_SERVER =
    (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("smtpserver_name");

  /** POP3サーバ名 */
  private static final String DEFAULT_POP3_SERVER =
    (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("pop3server_name");

  /** SMTPポート */
  private static final String DEFAULT_SMTP_PORT =
    (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("smtp_port");

  /** POP3ポート */
  private static final String DEFAULT_POP3_PORT =
    (String) BatchUtils.getProperties(SETTING_FILE_PATH).get("pop3_port");

  /**
   * ユーザー情報インポート
   * <p>
   * ユーザー情報のファイルを読み込み、Aipoのデータベースにインポートします。<br>
   * 
   * @param dataContext
   * 
   * @return boolean True(処理成功)、False(処理失敗)
   * @exception DBエラー
   *                、ファイルなし 、データ件数エラーの際に発生
   */
  public boolean importUserInfo(DataContext dataContext) throws Exception {

    int updateCounter = 0;
    int insertCounter = 0;
    int deleteCounter = 0;
    BufferedReader reader = null;

    try {
      // 開始メッセージをログに出力
      logger.info("ユーザー情報更新開始");

      // CSVデータファイル
      File csvFile =
        new File(BatchUtils.IMPORT_CSV_FILE_PATH
          + File.separator
          + USER_FILE_NAME); // データファイル
      if (!csvFile.exists()) {
        throw new FileNotFoundException();
      }

      // WebAPIのDBへ接続できるか確認
      if (!ALDataSyncFactoryService
        .getInstance()
        .getDataSyncHandler()
        .checkConnect()) {
        throw new Exception("コントロールパネルWebAPIのデータベースの接続に失敗したため、処理は実行されませんでした。");
      }

      List<TurbineUser> userList = new ArrayList<TurbineUser>();
      List<String> userAddedList = new ArrayList<String>(); // 処理済みのログインID格納配列

      reader =
        new BufferedReader(new InputStreamReader(
          new FileInputStream(csvFile),
          BatchUtils.FILE_ENCODING));

      // 最終行まで読み込む
      String line = "";
      while ((line = reader.readLine()) != null) {
        if (line.length() == 0) {
          continue;
        }
        String[] s = line.split(BatchUtils.SEPARATOR);
        String loginId = BatchUtils.trimDoubleQuotes(s[0]); // ログインID
        String lastName = BatchUtils.trimDoubleQuotes(s[1]); // 氏名(姓)
        String firstName = BatchUtils.trimDoubleQuotes(s[2]); // 氏名(名)
        String lastNameKana = BatchUtils.trimDoubleQuotes(s[3]); // カナ(姓)
        String firstNameKana = BatchUtils.trimDoubleQuotes(s[4]); // カナ(名)
        String email = BatchUtils.trimDoubleQuotes(s[5]); // eメール
        String userGroupCode = BatchUtils.trimDoubleQuotes(s[6]); // ユーザグループコード
        String positionCode = BatchUtils.trimDoubleQuotes(s[7]); // 役職コード
        String category = BatchUtils.trimDoubleQuotes(s[8]); // 職種名
        String positionNo = BatchUtils.trimDoubleQuotes(s[9]).trim(); // 並び順制御コード
        String pop3UserName = "";
        if (email.length() > 0) {
          pop3UserName = email.substring(0, email.indexOf("@")); // POP3ユーザー名
        }

        logger.debug("loginId -> " + loginId);

        // ログインIDでユーザー情報を検索する
        SelectQuery<TurbineUser> userQuery = Database.query(TurbineUser.class);
        // 検索条件：ログイン名と一致
        Expression userExp =
          ExpressionFactory.matchExp(TurbineUser.LOGIN_NAME_PROPERTY, loginId);
        userQuery.setQualifier(userExp); // 検索実行
        TurbineUser user = userQuery.fetchSingle();

        // <ユーザー情報>件数が1件の場合
        if (user != null) {

          logger.debug("user_id -> " + user.getUserId() + " update start.");

          // 処理済みログインIDに存在するか判定
          // 存在しない場合は1レコード目の処理
          if (!userAddedList.contains(user.getLoginName())) {
            // 1レコード目なら所属しているグループをデフォルトグループ以外全削除
            List<UserGroupLiteBean> postList_old =
              AccountUtils.getPostBeanList(user.getUserId());
            if (postList_old != null && postList_old.size() > 0) {
              // グループからユーザーを削除
              for (UserGroupLiteBean uglb : postList_old) {
                unjoinGroup(user.getLoginName(), uglb.getGroupId());
                logger.debug("unjoin group -> " + uglb.getGroupId() + ".");
              }
            }
            dataContext.commitChanges();
          }

          // ユーザー情報を更新する。
          user.setFirstName(firstName); // 名
          user.setLastName(lastName); // 姓
          user.setEmail(email); // メールアドレス
          user.setModified(new Date()); // 更新日
          user.setFirstNameKana(firstNameKana); // 名カナ
          user.setLastNameKana(lastNameKana); // 姓カナ
          user.setCategory(category); // 職種名
          user.setDisplayName(positionNo + lastName + firstName); // 並び順制御コード +
          // 氏名
          dataContext.commitChanges();
          logger.debug("user update -> " + user.getUserId() + ".");

          // メールアカウント情報を更新する。
          EipMMailAccount account =
            ALBatchMailUtils.getPrimaryEipMMailAccount(user.getUserId(), email);
          if (account != null) {
            account.setAccountType(Integer
              .toString(ALBatchMailUtils.ACCOUNT_TYPE_DEFAULT));
            account.setPop3userName(pop3UserName); // POP3ユーザー名
            // POP3パスワードはハッシュ化する
            account.setPop3password(ALMailUtils
              .getEncryptedMailAccountPasswd(WsUtils
                .getMD5Digest(pop3UserName)
                .getBytes())); // POP3パスワード
            account.setAccountName(lastName + "　" + firstName); // アカウント名
            account.setMailAddress(email); // メールアドレス
            account.setMailUserName(positionNo + lastName + firstName); // メールユーザー名
            account.setSmtpserverName(DEFAULT_SMTP_SERVER); // SMTPサーバー名
            account.setPop3serverName(DEFAULT_POP3_SERVER); // POP3サーバー名
            account.setSmtpPort(DEFAULT_SMTP_PORT); // SMTPポート
            account.setPop3Port(DEFAULT_POP3_PORT); // POP3ポート
            account.setUpdateDate(new Date()); // 更新日

            logger.debug("account update -> " + account.getAccountId() + ".");
          } else {
            // プライマリアドレスが存在しない場合は追加する。
            ALBatchMailUtils.insertMailAccountData(dataContext, user
              .getUserId(), // ユーザーID
              lastName + "　" + firstName, // アカウント名
              // ALBatchMailUtils.ACCOUNT_TYPE_INIT, // アカウントタイプ
              ALBatchMailUtils.ACCOUNT_TYPE_DEFAULT, // アカウントタイプ
              email, // メールアドレス
              positionNo + lastName + firstName, // メールユーザー名
              DEFAULT_SMTP_SERVER, // SMTPサーバー名
              Integer.parseInt(DEFAULT_SMTP_PORT), // SMTPポート
              DEFAULT_POP3_SERVER, // POP3サーバー名
              Integer.parseInt(DEFAULT_POP3_PORT), // POP3ポート
              pop3UserName, // POP3ユーザー名
              WsUtils.getMD5Digest(pop3UserName), // POP3パスワード(insertMailAccountData内でハッシュ化)
              0, // 送信時認証フラグ
              "", // 送信時認証（SMTP認証のユーザーID）
              "", // 送信時認証（SMTP認証のパスワード）
              0, // 受信時認証フラグ
              1, // 受信時メール削除フラグ(リリース時は1)
              0, // 受信時メール削除日数指定フラグ
              0, // 受信時メール削除日数
              "1", // 既未受信フラグ
              "", // 署名
              0, // 送信時の暗号化方式
              0);
            logger.debug("account insert -> " + pop3UserName + ".");
          }
          dataContext.commitChanges();

          if (!ALDataSyncFactoryService
            .getInstance()
            .getDataSyncHandler()
            .updateUser(ALEipUtils.getBaseUser(user.getUserId()))) {
            throw new Exception("コントロールパネルWebAPIとのDB同期に失敗しました。ユーザーID：["
              + user.getUserId()
              + "]");
          }

          // 更新件数をインクリメントする。
          updateCounter++;
        } else {

          logger.debug("loginId -> " + loginId + " insert start.");

          // <ユーザー情報>件数が0件の場合
          user = Database.create(TurbineUser.class);
          Date now = new Date();
          user.setLoginName(JetspeedSecurity.convertUserName(loginId)); // ログインID
          // パスワードを暗号化
          String encrypted =
            JetspeedSecurity.encryptPassword(loginId
              + INITIAL_PASSWORD_ADD_STRING);
          user.setPasswordValue(encrypted); // パスワード
          user.setFirstName(firstName); // 名
          user.setLastName(lastName); // 姓
          user.setEmail(email); // メールアドレス
          user.setConfirmValue(JetspeedResources.CONFIRM_VALUE); // CONFIRM
          user.setModified(now); // 更新日
          user.setCreated(now); // 作成日
          user.setLastLogin(now); // 最終ログイン日
          user.setDisabled(USER_DISABLED_FALSE); // 削除フラグ
          user.setPasswordChanged(now); // パスワード変更日
          user.setCompanyId(1); // 会社ID(未使用)
          user.setPositionId(0); // 役職ID(未使用)
          user.setFirstNameKana(firstNameKana); // 名カナ
          user.setLastNameKana(lastNameKana); // 姓カナ
          user.setCategory(category); // 職種名
          user.setCreatedUserId(1); // 作成者ユーザーID
          user.setUpdatedUserId(1); // 更新者ユーザーID
          // 並び順制御コード + 氏名
          user.setDisplayName(positionNo + lastName + firstName);
          dataContext.commitChanges();
          logger.debug("user insert -> " + user.getUserId() + ".");

          // ログインユーザーにはグループ LoginUser に所属させる
          // グループへ追加
          ALBatchGroupUtils.joinGroup(user.getLoginName(), DEFAULT_GROUP_NAME);
          logger.debug("join default group.");

          // ユーザー順位情報に登録
          List<EipMUserPosition> userposlist =
            Database.query(EipMUserPosition.class).fetchList();
          int new_pos =
            (userposlist != null && userposlist.size() > 0) ? userposlist
              .size() + 1 : 1;
          EipMUserPosition userposition =
            Database.create(EipMUserPosition.class);
          userposition.setTurbineUser(user);
          userposition.setPosition(Integer.valueOf(new_pos));
          logger.debug("user position insert -> " + new_pos + ".");

          dataContext.commitChanges();

          // アクセス権限
          ALAccessControlFactoryService aclservice =
            (ALAccessControlFactoryService) ((TurbineServices) TurbineServices
              .getInstance())
              .getService(ALAccessControlFactoryService.SERVICE_NAME);
          ALAccessControlHandler aclhandler =
            aclservice.getAccessControlHandler();
          aclhandler.insertDefaultRole(user.getUserId());
          logger.debug("grant access ctrl.");
          dataContext.commitChanges();

          // メールアカウントへの登録
          ALBatchMailUtils.insertMailAccountData(dataContext, user.getUserId(), // ユーザーID
            lastName + "　" + firstName, // アカウント名
            // ALBatchMailUtils.ACCOUNT_TYPE_INIT, // アカウントタイプ
            ALBatchMailUtils.ACCOUNT_TYPE_DEFAULT, // アカウントタイプ
            email, // メールアドレス
            positionNo + lastName + firstName, // メールユーザー名
            DEFAULT_SMTP_SERVER, // SMTPサーバー名
            Integer.parseInt(DEFAULT_SMTP_PORT), // SMTPポート
            DEFAULT_POP3_SERVER, // POP3サーバー名
            Integer.parseInt(DEFAULT_POP3_PORT), // POP3ポート
            pop3UserName, // POP3ユーザー名
            WsUtils.getMD5Digest(pop3UserName), // POP3パスワード(insertMailAccountData内でハッシュ化)
            0, // 送信時認証フラグ
            "", // 送信時認証（SMTP認証のユーザーID）
            "", // 送信時認証（SMTP認証のパスワード）
            0, // 受信時認証フラグ
            1, // 受信時メール削除フラグ(リリース時は1)
            0, // 受信時メール削除日数指定フラグ
            0, // 受信時メール削除日数
            "1", // 既未受信フラグ
            "", // 署名
            0, // 送信時の暗号化方式
            0);
          dataContext.commitChanges();
          logger.debug("account insert -> " + pop3UserName + ".");

          // WebAPIとのDB同期
          if (!ALDataSyncFactoryService
            .getInstance()
            .getDataSyncHandler()
            .addUser(ALEipUtils.getBaseUser(user.getUserId()))) {
            throw new Exception("コントロールパネルWebAPIとのDB同期に失敗しました。ユーザーID：["
              + user.getUserId()
              + "]");
          }
          ALBatchUserUtils.addDefaultPSML(ALEipUtils.getBaseUser(user
            .getUserId()), false);
          logger.debug("add default psml.");

          // 登録件数をインクリメントする。
          insertCounter++;
        }

        // ユーザーグループロールを登録する。
        createUserGroup(user, userGroupCode, positionCode);
        dataContext.commitChanges();

        // 処理済みのログインID格納配列に保持
        userAddedList.add(loginId);

        // <ユーザー情報レコード>を<ユーザー情報ファイル配列>に追加で格納する。
        userList.add(user);

      }
      logger.debug("user update or insert ended.");

      // ユーザー情報ファイルをクローズする。
      reader.close();
      reader = null;

      logger.debug("user delete start.");
      // 削除されていないユーザー情報のレコードを抽出する。
      SelectQuery<TurbineUser> userQuery = Database.query(TurbineUser.class);
      Expression userExp =
        ExpressionFactory.matchExp(
          TurbineUser.DISABLED_PROPERTY,
          USER_DISABLED_FALSE);
      userQuery.setQualifier(userExp); // 検索実行
      userQuery.orderAscending(TurbineUser.LAST_NAME_PROPERTY);
      // ユーザー情報の削除用リスト
      List<TurbineUser> delList = userQuery.fetchList();
      int size = delList.size();
      String[] user_name_list = new String[size];

      // 全レコードに対して判定
      for (Iterator<TurbineUser> dbUser = delList.iterator(); dbUser.hasNext();) {
        TurbineUser user = dbUser.next();
        // ユーザーIDがシステム用データのユーザーID（1,2,3）と一致する場合はスキップ
        if (Arrays.binarySearch(BatchUtils.SystemUserId, user
          .getUserId()
          .toString()) >= 0) {
          dbUser.remove();
          continue;
        }

        for (TurbineUser fileUser : userList) {
          // ログイン名が一致したら削除リストから削除
          if (fileUser.getUserId() == user.getUserId()) {
            dbUser.remove();
            break;
          }
        }
      }
      // 削除リストのユーザーを削除
      int i = 0;
      for (TurbineUser delUser : delList) {
        logger.debug("user delete -> " + delUser.getUserId() + ".");

        user_name_list[i] = delUser.getLoginName();
        ALBatchAccountUtils.deleteUser(delUser);
        logger.debug("user's relation data delete -> "
          + delUser.getUserId()
          + ".");

        deleteCounter++;
        i++;
      }
      dataContext.commitChanges();

      // WebAPIとのDB同期
      if (!ALDataSyncFactoryService
        .getInstance()
        .getDataSyncHandler()
        .multiDeleteUser(user_name_list, size)) {
        return false;
      }

      // 表示順位の更新
      // ユーザー表示順位情報の一覧を取得
      SelectQuery<EipMUserPosition> userPosQuery =
        Database.query(EipMUserPosition.class);
      userPosQuery.orderAscending(EipMUserPosition.POSITION_PROPERTY);
      List<EipMUserPosition> userPosList = userPosQuery.fetchList();
      int counter = 1;
      for (EipMUserPosition pospos : userPosList) {
        pospos.setPosition(counter);
        counter++;
      }
      dataContext.commitChanges();
      logger.debug("user position update.");

      // 終了メッセージ
      logger.info("ユーザー情報更新完了　登録件数：["
        + insertCounter
        + "]　更新件数：["
        + updateCounter
        + "]　削除件数：["
        + deleteCounter
        + "]");

    } catch (FileNotFoundException e) {
      logger.warn("ユーザー情報ファイルが存在しません。");
    } catch (Exception e) {
      try {
        if (reader != null) {
          // ユーザー情報ファイルをクローズする。
          reader.close();
        }
      } catch (Exception ex) {

      }
      logger.error("ユーザー情報の更新に失敗しました。", e);
      return false;
    }
    return true;
  }

  /**
   * ユーザーグループロール登録
   * <p>
   * ユーザーグループコードと役職コードを条件にグループ(部署)、役職情報を検索し、<br>
   * 取得したIDでユーザーグループロールを登録します。<br>
   * 
   * @param TubineUser
   *            ユーザー情報
   * @param String
   *            ユーザーグループコード
   * @param String
   *            役職コード
   * @exception DBエラー
   *                、データ件数エラーの際に発生
   */
  public static void createUserGroup(TurbineUser user, String userGroupCode,
      String positionCode) throws Exception {

    try {

      // ユーザーグループコードでグループ情報を検索する
      if (BatchUtils.getEipMPostFromPostCode(userGroupCode).size() == 0) {
        throw new Exception("ユーザーグループコードに一致する部署情報が見つかりませんでした。ユーザーグループコード：["
          + userGroupCode
          + "]");
      }
      TurbineGroup group =
        (TurbineGroup) JetspeedSecurity.getGroup(BatchUtils
          .getEipMPostFromPostCode(userGroupCode)
          .get(0)
          .getGroupName());

      // 役職コードで役職情報を検索する
      SelectQuery<EipMPosition> positionQuery =
        Database.query(EipMPosition.class);
      // 検索条件：役職コードと一致
      Expression positionExp =
        ExpressionFactory.matchExp(
          EipMPosition.POSITION_CODE_PROPERTY,
          positionCode);
      positionQuery.setQualifier(positionExp); // 検索実行
      List<EipMPosition> position = positionQuery.fetchList();
      if (position.size() >= 2) {
        throw new Exception("ユーザーの役職コードに一致する役職情報が2件以上見つかりました。役職コード：["
          + positionCode
          + "]");
      } else if (position.size() == 0) {
        throw new Exception("ユーザーの役職コードに一致する役職情報が見つかりませんでした。役職コード：["
          + positionCode
          + "]");
      }
      logger.debug("create user group role. user-> "
        + group.getId()
        + " group-> "
        + group.getId()
        + " role-> "
        + DEFAULT_ROLE_NAME
        + ".");

      // グループへ追加
      TurbineUserGroupRole user_group_role =
        Database.create(TurbineUserGroupRole.class);
      // ユーザー
      user_group_role.setTurbineUser(user);
      // グループ
      user_group_role.setTurbineGroup(group);
      // ロール
      user_group_role.setTurbineRole((TurbineRole) JetspeedSecurity
        .getRole(DEFAULT_ROLE_NAME));
      // 役職
      user_group_role.setEipMPosition(position.get(0));

    } catch (Exception e) {
      throw new Exception(e.toString() + e.getCause());
    }
  }

  /**
   * ALGroupManagement::unjoinGroupをコピー（元のメソッドは中でコミットしてしまうため。）
   */
  private void unjoinGroup(String username, String groupname)
      throws JetspeedSecurityException {
    try {
      JetspeedUser user = JetspeedSecurity.getUser(username);
      Group group = JetspeedSecurity.getGroup(groupname);
      Role role = JetspeedSecurity.getRole(DEFAULT_ROLE_NAME);

      Expression exp1 =
        ExpressionFactory.matchDbExp(TurbineUser.USER_ID_PK_COLUMN, Integer
          .valueOf(user.getUserId()));
      Expression exp2 =
        ExpressionFactory.matchDbExp(TurbineGroup.GROUP_ID_PK_COLUMN, Integer
          .valueOf(group.getId()));
      Expression exp3 =
        ExpressionFactory.matchDbExp(TurbineRole.ROLE_ID_PK_COLUMN, Integer
          .valueOf(role.getId()));
      SelectQuery<TurbineUserGroupRole> query =
        Database.query(TurbineUserGroupRole.class);
      query.setQualifier(exp1);
      query.andQualifier(exp2);
      query.andQualifier(exp3);

      query.deleteAll();

    } catch (Exception e) {
      throw new GroupException("Unjoin group '"
        + groupname
        + "' to user '"
        + username
        + "' failed: ", e);
    }
  }

}
