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.ACTIVE;
  import static com.alibaba.cobar.manager.util.ConstantDefine.CLUSTER_ID;
  import static com.alibaba.cobar.manager.util.ConstantDefine.DELETE;
  import static com.alibaba.cobar.manager.util.ConstantDefine.HOST;
  import static com.alibaba.cobar.manager.util.ConstantDefine.ID;
  import static com.alibaba.cobar.manager.util.ConstantDefine.IN_ACTIVE;
  import static com.alibaba.cobar.manager.util.ConstantDefine.NAME;
  import static com.alibaba.cobar.manager.util.ConstantDefine.PASSWORD;
  import static com.alibaba.cobar.manager.util.ConstantDefine.PORT;
  import static com.alibaba.cobar.manager.util.ConstantDefine.SERVER_PORT;
  import static com.alibaba.cobar.manager.util.ConstantDefine.STATUS;
  import static com.alibaba.cobar.manager.util.ConstantDefine.TIME_DIFF;
  import static com.alibaba.cobar.manager.util.ConstantDefine.USER;
  
  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.LinkedList;
  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.CobarDAO;
  import com.alibaba.cobar.manager.dataobject.xml.CobarDO;
  import com.alibaba.cobar.manager.util.EncryptUtil;
  
  /**
   * @author haiqing.zhuhq 2011-6-15
   */
  public class CobarDAOImple extends AbstractDAOImple implements CobarDAO, InitializingBean {
      private static final Logger logger = Logger.getLogger(CobarDAOImple.class);
      private Map<Long, CobarDO> 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("name", NAME);
          typeMap.put("status", STATUS);
          typeMap.put("clusterId", CLUSTER_ID);
          typeMap.put("host", HOST);
          typeMap.put("port", PORT);
          typeMap.put("password", PASSWORD);
          typeMap.put("time_diff", TIME_DIFF);
          typeMap.put("user", USER);
          typeMap.put("serverPort", SERVER_PORT);
      }
  
      public CobarDAOImple() {
          map = new HashMap<Long, CobarDO>();
          xpp = new MXParser();
          xsl = new MXSerializer();
          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 && "cobars".equals(xpp.getName()))) {
                  if (xpp.getEventType() == XmlPullParser.START_TAG && "cobar".equals(xpp.getName())) {
                      CobarDO cobar = read(xpp);
                      if (null == cobar) {
                          throw new XmlPullParserException("Cobar read error");
                      }
                      maxId = (maxId < cobar.getId()) ? cobar.getId() : maxId;
                      map.put(cobar.getId(), cobar);
                  }
                  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("cobar backup fail!");
              }
              os = new FileOutputStream(xmlPath);
              xsl.setOutput(os, "UTF-8");
              xsl.startDocument("UTF-8", null);
              xsl.text("\n");
              xsl.startTag(null, "cobars");
              xsl.text("\n");
              Iterator it = map.entrySet().iterator();
              while (it.hasNext()) {
                  Map.Entry<Long, CobarDO> entry = (Entry<Long, CobarDO>) it.next();
                  CobarDO cobar = entry.getValue();
                  if (!write(cobar)) {
                      throw new IOException("Cobar write error!");
                  }
              }
              xsl.endTag(null, "cobars");
              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 boolean write(CobarDO cobar) {
          try {
              writePrefix(false);
              xsl.startTag(null, "cobar");
              xsl.text("\n");
              writeProperty("id", String.valueOf(cobar.getId()));
              writeProperty("clusterId", String.valueOf(cobar.getClusterId()));
              writeProperty("name", cobar.getName());
              writeProperty("status", cobar.getStatus());
              writeProperty("host", cobar.getHost());
              writeProperty("serverPort", String.valueOf(cobar.getServerPort()));
              writeProperty("port", String.valueOf(cobar.getPort()));
              writeProperty("user", cobar.getUser());
              String password = EncryptUtil.encrypt(cobar.getPassword());
              writeProperty("password", password);
              writeProperty("time_diff", cobar.getTime_diff());
              writePrefix(true);
              xsl.endTag(null, "cobar");
              xsl.text("\n");
              return true;
          } catch (IOException e) {
              logger.error(e.getMessage(), e);
          }
          return false;
      }
  
      private CobarDO read(XmlPullParser xpp) {
          CobarDO cobar = new CobarDO();
          cobar.setServerPort(8066);
          try {
              while (!(xpp.getEventType() == XmlPullParser.END_TAG && "cobar".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:
                          cobar.setId(Long.parseLong(xpp.nextText().trim()));
                          break;
                      case NAME:
                          cobar.setName(xpp.nextText().trim());
                          break;
                      case CLUSTER_ID:
                          cobar.setClusterId(Long.parseLong(xpp.nextText().trim()));
                          break;
                      case STATUS:
                          String value = xpp.nextText().trim();
                          if (ACTIVE.equals(value)) {
                              cobar.setStatus(ACTIVE);
                          } else if (DELETE.equals(value)) {
                              cobar.setStatus(DELETE);
                          } else {
                              cobar.setStatus(IN_ACTIVE);
                          }
                          break;
                      case HOST:
                          cobar.setHost(xpp.nextText().trim());
                          break;
                      case PORT:
                          cobar.setPort(Integer.parseInt(xpp.nextText().trim()));
                          break;
                      case SERVER_PORT:
                          cobar.setServerPort(Integer.parseInt(xpp.nextText().trim()));
                          break;
                      case USER:
                          cobar.setUser(xpp.nextText().trim());
                          break;
                      case PASSWORD:
                          String password = EncryptUtil.decrypt(xpp.nextText().trim());
                          cobar.setPassword(password);
                          break;
                      case TIME_DIFF:
                          cobar.setTime_diff(xpp.nextText().trim());
                          break;
                      default:
                          break;
                      }
                  }
                  xpp.next();
              }
              return cobar;
          } 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;
      }
  
      @Override
      public CobarDO getCobarById(long id) {
          return map.get(id);
      }
  
      //    @SuppressWarnings({ "unchecked", "rawtypes" })
      //    @Override
      //    public int getCobarCountByStatus(long clusterId, String status) {
      //        int count = 0;
      //        Iterator it = map.entrySet().iterator();
      //        while (it.hasNext()) {
      //            Map.Entry<Long, CobarDO> entry = (Entry<Long, CobarDO>) it.next();
      //            CobarDO cobar = entry.getValue();
      //            if (clusterId == cobar.getClusterId() && status.equals(cobar.getStatus())) {
      //                count++;
      //            }
      //        }
      //        return count;
      //    }
  
      @Override
      public void afterPropertiesSet() throws Exception {
          xmlPath = xmlFileLoader.getFilePath();
          if (null == xmlPath) {
              logger.error("cobar xmlpath doesn't set!");
              throw new IllegalArgumentException("cobar xmlpath doesn't set!");
          } else {
              if (xmlPath.endsWith(System.getProperty("file.separator"))) {
                  xmlPath = new StringBuilder(xmlPath).append("cobar.xml").toString();
              } else {
                  xmlPath =
                          new StringBuilder(xmlPath).append(System.getProperty("file.separator"))
                                                    .append("cobar.xml")
                                                    .toString();
              }
              read();
          }
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public boolean checkName(String name, long clusterId) {
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, CobarDO> entry = (Entry<Long, CobarDO>) it.next();
              CobarDO cobar = entry.getValue();
              if (clusterId == cobar.getClusterId() && cobar.getName().equals(name)) {
                  return false;
              }
          }
          return true;
      }
  
      @Override
      public boolean addCobar(CobarDO cobar) {
          lock.lock();
          try {
              if (!checkName(cobar.getName(), cobar.getClusterId())) {
                  return false;
              }
              cobar.setId(++maxId);
              map.put(cobar.getId(), cobar);
              if (!write()) {
                  logger.error("Fail to add cobar!");
                  recovery(xmlPath);
                  read();
                  return false;
              }
          } finally {
              lock.unlock();
          }
          return true;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public List<CobarDO> getCobarList(long clusterId) {
          List<CobarDO> list = new ArrayList<CobarDO>();
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, CobarDO> entry = (Entry<Long, CobarDO>) it.next();
              CobarDO cobar = entry.getValue();
              if (clusterId == cobar.getClusterId()) {
                  list.add(cobar);
              }
          }
          return list;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public List<CobarDO> getCobarList(long clusterId, String status) {
          List<CobarDO> list = new ArrayList<CobarDO>();
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, CobarDO> entry = (Entry<Long, CobarDO>) it.next();
              CobarDO cobar = entry.getValue();
              if (clusterId == cobar.getClusterId() && status.equals(cobar.getStatus())) {
                  list.add(cobar);
              }
          }
          return list;
      }
  
      @Override
      public List<CobarDO> listCobarById(long[] cobarIds) {
          List<CobarDO> list = new LinkedList<CobarDO>();
          for (int i = 0; i < cobarIds.length; i++) {
              CobarDO cobar = map.get(cobarIds[i]);
              if (cobar != null) {
                  list.add(cobar);
              }
          }
          return list;
      }
  
      @Override
      public boolean modifyCobar(CobarDO cobar) {
          lock.lock();
          try {
              if (!checkName(cobar.getName(), cobar.getClusterId(), cobar.getId())) {
                  return false;
              }
              map.put(cobar.getId(), cobar);
              if (!write()) {
                  logger.error("Fail to modify cobar!");
                  recovery(xmlPath);
                  read();
                  return false;
              }
          } finally {
              lock.unlock();
          }
          return true;
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      @Override
      public boolean checkName(String name, long clusterId, long cobarId) {
          Iterator it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry<Long, CobarDO> entry = (Entry<Long, CobarDO>) it.next();
              CobarDO cobar = entry.getValue();
              if (cobar.getId() == cobarId) {
                  continue;
              }
              if (clusterId == cobar.getClusterId() && cobar.getName().equals(name)) {
                  return false;
              }
          }
          return true;
      }
  
      @Override
      public List<CobarDO> listAllCobar() {
          return new ArrayList<CobarDO>(map.values());
      }
  
  }
  