content: /*
   * Copyright 1999-2012 Alibaba Group.
   *  
   * Licensed 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.
   */
  
  package com.alibaba.cobar.manager.dao.xml;
  
  import static com.alibaba.cobar.manager.util.ConstantDefine.CLUSTER_ADMIN;
  import static com.alibaba.cobar.manager.util.ConstantDefine.FORBIDDEN;
  import static com.alibaba.cobar.manager.util.ConstantDefine.ID;
  import static com.alibaba.cobar.manager.util.ConstantDefine.NORMAL;
  import static com.alibaba.cobar.manager.util.ConstantDefine.PASSWORD;
  import static com.alibaba.cobar.manager.util.ConstantDefine.REALNAME;
  import static com.alibaba.cobar.manager.util.ConstantDefine.STATUS;
  import static com.alibaba.cobar.manager.util.ConstantDefine.SYSTEM_ADMIN;
  import static com.alibaba.cobar.manager.util.ConstantDefine.USERNAME;
  import static com.alibaba.cobar.manager.util.ConstantDefine.USER_ROLE;
  
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.concurrent.locks.ReentrantLock;
  
  import org.apache.log4j.Logger;
  import org.springframework.beans.factory.InitializingBean;
  import org.xmlpull.mxp1.MXParser;
  import org.xmlpull.mxp1_serializer.MXSerializer;
  import org.xmlpull.v1.XmlPullParser;
  import org.xmlpull.v1.XmlPullParserException;
  
  import com.alibaba.cobar.manager.dao.UserDAO;
  import com.alibaba.cobar.manager.dataobject.xml.UserDO;
  import com.alibaba.cobar.manager.util.EncryptUtil;
  
  /**
   * @author haiqing.zhuhq 2011-6-17
   */
  public class UserDAOImple extends AbstractDAOImple implements UserDAO, InitializingBean {
  
      private static final Logger logger = Logger.getLogger(UserDAOImple.class);
  
      private Map<Long, UserDO> map;
      private static long maxId;
      private static final ReentrantLock lock = new ReentrantLock();
      private static final Map<String, Integer> typeMap = new HashMap<String, Integer>();
  
      static {
          typeMap.put("id", ID);
          typeMap.put("user_role", USER_ROLE);
          typeMap.put("realname", REALNAME);
          typeMap.put("password", PASSWORD);
          typeMap.put("username", USERNAME);
          typeMap.put("status", STATUS);
      }
  
      @SuppressWarnings("static-access")
      public UserDAOImple() {
          this.map = new HashMap<Long, UserDO>();
          this.xpp = new MXParser();
          this.xsl = new MXSerializer();
          this.maxId = Long.MIN_VALUE;
      }
  
      private boolean read() {
          FileInputStream is = null;
          lock.lock();
          try {
              map.clear();
              is = new FileInputStream(xmlPath);
              xpp.setInput(is, "UTF-8");
              while (!(xpp.getEventType() == XmlPullParser.END_TAG && "users".equals(xpp.getName()))) {
                  if (xpp.getEventType() == XmlPullParser.START_TAG && "user".equals(xpp.getName())) {
                      UserDO user = read(xpp);
                      if (null == user) {
                          throw new XmlPullParserException("User read error");
                      }
                      maxId = (maxId < user.getId()) ? user.getId() : maxId;
                      map.put(user.getId(), user);
                  }
                  xpp.next();
              }
              is.close();
              return true;
          } catch (FileNotFoundException e) {
              logger.error(e.getMessage(), e);
          } catch (XmlPullParserException e) {
              logger.error(e.getMessage(), e);
          } catch (IOException e) {
              logger.error(e.getMessage(), e);
          } finally {
              maxId = maxId < 0 ? 0 : maxId;
              lock.unlock();
          }
          if (null != is) {
              try {
                  is.close();
              } catch (IOException e) {
                  logger.error(e.getMessage(), e);
              }
          }
          return false;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      private boolean write() {
          FileOutputStream os = null;
          lock.lock();
          try {
              if (!backup(xmlPath)) {
                  logger.error("user backup fail!");
              }
              os = new FileOutputStream(xmlPath);
              xsl.setOutput(os, "UTF-8");
              xsl.startDocument("UTF-8", null);
              xsl.text("\n");
              xsl.startTag(null, "users");
              xsl.text("\n");
              Iterator it = map.entrySet().iterator();
              while (it.hasNext()) {
                  Map.Entry<Long, UserDO> entry = (Entry<Long, UserDO>) it.next();
                  UserDO user = entry.getValue();
                  if (!write(user)) {
                      throw new IOException("User write error!");
                  }
              }
              xsl.endTag(null, "users");
              xsl.endDocument();
              os.close();
              return true;
          } catch (IOException e) {
              logger.error(e.getMessage(), e);
          } finally {
              lock.unlock();
          }
          if (null != os) {
              try {
                  os.close();
              } catch (IOException e) {
                  logger.error(e.getMessage(), e);
              }
          }
          return false;
      }
  
      private UserDO read(XmlPullParser xpp) {
          UserDO user = new UserDO();
          try {
              while (!(xpp.getEventType() == XmlPullParser.END_TAG && "user".equals(xpp.getName()))) {
                  if (xpp.getEventType() == XmlPullParser.START_TAG && "property".equals(xpp.getName())) {
                      int type = typeMap.get(xpp.getAttributeValue(0).trim());
                      switch (type) {
                      case ID:
                          user.setId(Long.parseLong(xpp.nextText().trim()));
                          break;
                      case USER_ROLE:
                          String value = xpp.nextText().trim();
                          if (SYSTEM_ADMIN.equals(value)) {
                              user.setUser_role(SYSTEM_ADMIN);
                          } else {
                              user.setUser_role(CLUSTER_ADMIN);
                          }
                          break;
                      case STATUS:
                          String value1 = xpp.nextText().trim();
                          if (NORMAL.equals(value1)) {
                              user.setStatus(NORMAL);
                          } else {
                              user.setStatus(FORBIDDEN);
                          }
                          break;
                      case PASSWORD:
                          user.setPassword(xpp.nextText().trim());
                          break;
                      case REALNAME:
                          user.setRealname(xpp.nextText().trim());
                          break;
                      case USERNAME:
                          user.setUsername(xpp.nextText().trim());
                          break;
                      default:
                          break;
                      }
                  }
                  xpp.next();
              }
              return user;
          } catch (NumberFormatException e) {
              logger.error(e.getMessage(), e);
          } catch (XmlPullParserException e) {
              logger.error(e.getMessage(), e);
          } catch (IOException e) {
              logger.error(e.getMessage(), e);
          }
          return null;
      }
  
      private boolean write(UserDO user) {
          try {
              writePrefix(false);
              xsl.startTag(null, "user");
              xsl.text("\n");
              writeProperty("id", String.valueOf(user.getId()));
              writeProperty("realname", user.getRealname());
              writeProperty("username", user.getUsername());
              writeProperty("password", user.getPassword());
              writeProperty("user_role", user.getUser_role());
              writeProperty("status", user.getStatus());
              writePrefix(true);
              xsl.endTag(null, "user");
              xsl.text("\n");
              return true;
          } catch (IOException e) {
              logger.error(e.getMessage(), e);
          }
          return false;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public boolean checkName(String username) {
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, UserDO> entry = (Entry<Long, UserDO>) it.next();
              UserDO user = entry.getValue();
              if (username.equals(user.getUsername())) {
                  return false;
              }
          }
          return true;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public UserDO validateUser(String username, String password) {
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, UserDO> entry = (Entry<Long, UserDO>) it.next();
              UserDO user = entry.getValue();
              if (username.equals(user.getUsername()) && EncryptUtil.encrypt(password).equals(user.getPassword())) {
                  //encrypt check success
                  return user;
              } else if (username.equals(user.getUsername()) && password.equals(user.getPassword())) {
                  //no encrypt check success,then change pwd to encrypted
                  user.setPassword(EncryptUtil.encrypt(password));
                  modifyUser(user);
                  return user;
              }
          }
          return null;
      }
  
      @Override
      public void afterPropertiesSet() throws Exception {
          xmlPath = xmlFileLoader.getFilePath();
          if (null == xmlPath) {
              logger.error("user xmlpath doesn't set!");
              throw new IllegalArgumentException("user xmlpath doesn't set!");
          } else {
              if (xmlPath.endsWith(System.getProperty("file.separator"))) {
                  xmlPath = new StringBuilder(xmlPath).append("user.xml").toString();
              } else {
                  xmlPath =
                          new StringBuilder(xmlPath).append(System.getProperty("file.separator"))
                                                    .append("user.xml")
                                                    .toString();
              }
              read();
          }
      }
  
      @Override
      public List<UserDO> getUserList() {
          List<UserDO> list = new ArrayList<UserDO>(map.values());
          //        Iterator it = map.entrySet().iterator();
          //        while (it.hasNext()) {
          //            Map.Entry<Long, UserDO> entry = (Entry<Long, UserDO>) it.next();
          //            UserDO user = entry.getValue();
          //            list.add(user);
          //        }
          return list;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public boolean checkName(String username, long userId) {
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, UserDO> entry = (Entry<Long, UserDO>) it.next();
              UserDO user = entry.getValue();
              if (userId == user.getId()) {
                  continue;
              }
              if (username.equals(user.getUsername())) {
                  return false;
              }
          }
          return true;
      }
  
      @Override
      public boolean addUser(UserDO user) {
          lock.lock();
          try {
              if (!checkName(user.getUsername())) {
                  return false;
              }
              user.setId(++maxId);
              map.put(user.getId(), user);
              if (!write()) {
                  logger.error("Fail to add user!");
                  recovery(xmlPath);
                  read();
                  return false;
              }
              return true;
          } finally {
              lock.unlock();
          }
      }
  
      @Override
      public boolean modifyUser(UserDO user) {
          lock.lock();
          try {
              if (!checkName(user.getUsername(), user.getId())) {
                  return false;
              }
              map.put(user.getId(), user);
              if (!write()) {
                  logger.error("Fail to modify user!");
                  recovery(xmlPath);
                  read();
                  return false;
              }
              return true;
          } finally {
              lock.unlock();
          }
      }
  
      @Override
      public UserDO getUserById(long id) {
          return map.get(id);
      }
  
  }
  