Coverage Report - com.ctrip.apollo.internals.LocalFileConfigRepository
 
Classes in this File Line Coverage Branch Coverage Complexity
LocalFileConfigRepository
82%
89/108
72%
16/22
3.6
 
 1  
 package com.ctrip.apollo.internals;
 2  
 
 3  
 import com.google.common.base.Joiner;
 4  
 import com.google.common.base.Preconditions;
 5  
 
 6  
 import com.ctrip.apollo.core.ConfigConsts;
 7  
 import com.ctrip.apollo.util.ConfigUtil;
 8  
 import com.ctrip.apollo.util.ExceptionUtil;
 9  
 import com.dianping.cat.Cat;
 10  
 import com.dianping.cat.message.Message;
 11  
 import com.dianping.cat.message.Transaction;
 12  
 
 13  
 import org.codehaus.plexus.PlexusContainer;
 14  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 15  
 import org.slf4j.Logger;
 16  
 import org.slf4j.LoggerFactory;
 17  
 import org.unidal.lookup.ContainerLoader;
 18  
 
 19  
 import java.io.File;
 20  
 import java.io.FileInputStream;
 21  
 import java.io.FileOutputStream;
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.io.OutputStream;
 25  
 import java.util.Properties;
 26  
 
 27  
 /**
 28  
  * @author Jason Song(song_s@ctrip.com)
 29  
  */
 30  
 public class LocalFileConfigRepository extends AbstractConfigRepository
 31  
     implements RepositoryChangeListener {
 32  1
   private static final Logger logger = LoggerFactory.getLogger(LocalFileConfigRepository.class);
 33  
   private final PlexusContainer m_container;
 34  
   private final String m_namespace;
 35  
   private final File m_baseDir;
 36  
   private final ConfigUtil m_configUtil;
 37  
   private volatile Properties m_fileProperties;
 38  
   private volatile ConfigRepository m_fallback;
 39  
 
 40  
   /**
 41  
    * Constructor.
 42  
    *
 43  
    * @param baseDir   the base dir for this local file config repository
 44  
    * @param namespace the namespace
 45  
    */
 46  14
   public LocalFileConfigRepository(File baseDir, String namespace) {
 47  14
     m_baseDir = baseDir;
 48  14
     m_namespace = namespace;
 49  14
     m_container = ContainerLoader.getDefaultContainer();
 50  
     try {
 51  14
       m_configUtil = m_container.lookup(ConfigUtil.class);
 52  0
     } catch (ComponentLookupException ex) {
 53  0
       Cat.logError(ex);
 54  0
       throw new IllegalStateException("Unable to load component!", ex);
 55  14
     }
 56  14
     this.trySync();
 57  14
   }
 58  
 
 59  
   @Override
 60  
   public Properties getConfig() {
 61  14
     if (m_fileProperties == null) {
 62  1
       sync();
 63  
     }
 64  13
     Properties result = new Properties();
 65  13
     result.putAll(m_fileProperties);
 66  13
     return result;
 67  
   }
 68  
 
 69  
   @Override
 70  
   public void setFallback(ConfigRepository fallbackConfigRepository) {
 71  
     //clear previous listener
 72  12
     if (m_fallback != null) {
 73  0
       m_fallback.removeChangeListener(this);
 74  
     }
 75  12
     m_fallback = fallbackConfigRepository;
 76  12
     trySyncFromFallback();
 77  12
     fallbackConfigRepository.addChangeListener(this);
 78  12
   }
 79  
 
 80  
   @Override
 81  
   public void onRepositoryChange(String namespace, Properties newProperties) {
 82  11
     if (newProperties.equals(m_fileProperties)) {
 83  7
       return;
 84  
     }
 85  4
     Properties newFileProperties = new Properties();
 86  4
     newFileProperties.putAll(newProperties);
 87  4
     updateFileProperties(newFileProperties);
 88  4
     this.fireRepositoryChange(namespace, newProperties);
 89  4
   }
 90  
 
 91  
   @Override
 92  
   protected void sync() {
 93  15
     Transaction transaction = Cat.newTransaction("Apollo.ConfigService", "queryLocalConfigFile");
 94  15
     Throwable exception = null;
 95  
     try {
 96  15
       transaction.addData("Basedir", m_baseDir.getAbsolutePath());
 97  15
       m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
 98  5
       transaction.setStatus(Message.SUCCESS);
 99  10
     } catch (Throwable ex) {
 100  10
       Cat.logError(ex);
 101  10
       transaction.setStatus(ex);
 102  10
       exception = ex;
 103  
       //ignore
 104  
     } finally {
 105  15
       transaction.complete();
 106  15
     }
 107  
 
 108  
     //sync with fallback immediately
 109  15
     trySyncFromFallback();
 110  
 
 111  15
     if (m_fileProperties == null) {
 112  10
       throw new RuntimeException(
 113  
           "Load config from local config failed!", exception);
 114  
     }
 115  5
   }
 116  
 
 117  
   private void trySyncFromFallback() {
 118  27
     if (m_fallback == null) {
 119  14
       return;
 120  
     }
 121  
     try {
 122  13
       Properties properties = m_fallback.getConfig();
 123  10
       updateFileProperties(properties);
 124  3
     } catch (Throwable ex) {
 125  3
       Cat.logError(ex);
 126  3
       logger
 127  6
           .warn("Sync config from fallback repository {} failed, reason: {}", m_fallback.getClass(),
 128  3
               ExceptionUtil.getDetailMessage(ex));
 129  10
     }
 130  13
   }
 131  
 
 132  
   private synchronized void updateFileProperties(Properties newProperties) {
 133  14
     if (newProperties.equals(m_fileProperties)) {
 134  0
       return;
 135  
     }
 136  14
     this.m_fileProperties = newProperties;
 137  14
     persistLocalCacheFile(m_baseDir, m_namespace);
 138  14
   }
 139  
 
 140  
   private Properties loadFromLocalCacheFile(File baseDir, String namespace) throws IOException {
 141  15
     Preconditions.checkNotNull(baseDir, "Basedir cannot be null");
 142  
 
 143  15
     File file = assembleLocalCacheFile(baseDir, namespace);
 144  15
     Properties properties = null;
 145  
 
 146  15
     if (file.isFile() && file.canRead()) {
 147  5
       InputStream in = null;
 148  
 
 149  
       try {
 150  5
         in = new FileInputStream(file);
 151  
 
 152  5
         properties = new Properties();
 153  5
         properties.load(in);
 154  0
       } catch (IOException ex) {
 155  0
         Cat.logError(ex);
 156  0
         throw new RuntimeException(String
 157  0
             .format("Loading config from local cache file %s failed", file.getAbsolutePath()), ex);
 158  
       } finally {
 159  0
         try {
 160  5
           if (in != null) {
 161  5
             in.close();
 162  
           }
 163  0
         } catch (IOException ex) {
 164  
           // ignore
 165  5
         }
 166  0
       }
 167  5
     } else {
 168  10
       throw new RuntimeException(
 169  10
           String.format("Cannot read from local cache file %s", file.getAbsolutePath()));
 170  
     }
 171  
 
 172  5
     return properties;
 173  
   }
 174  
 
 175  
   void persistLocalCacheFile(File baseDir, String namespace) {
 176  14
     if (baseDir == null) {
 177  0
       return;
 178  
     }
 179  14
     File file = assembleLocalCacheFile(baseDir, namespace);
 180  
 
 181  14
     OutputStream out = null;
 182  
 
 183  14
     Transaction transaction = Cat.newTransaction("Apollo.ConfigService", "persistLocalConfigFile");
 184  14
     transaction.addData("LocalConfigFile", file.getAbsolutePath());
 185  
     try {
 186  14
       out = new FileOutputStream(file);
 187  14
       m_fileProperties.store(out, "Persisted by DefaultConfig");
 188  14
       transaction.setStatus(Message.SUCCESS);
 189  0
     } catch (IOException ex) {
 190  0
       Cat.logError(ex);
 191  0
       transaction.setStatus(ex);
 192  0
       logger.warn("Persist local cache file {} failed, reason: {}.", file.getAbsolutePath(),
 193  0
           ExceptionUtil.getDetailMessage(ex));
 194  
     } finally {
 195  14
       if (out != null) {
 196  
         try {
 197  14
           out.close();
 198  0
         } catch (IOException ex) {
 199  
           //ignore
 200  14
         }
 201  
       }
 202  14
       transaction.complete();
 203  14
     }
 204  14
   }
 205  
 
 206  
   File assembleLocalCacheFile(File baseDir, String namespace) {
 207  
 
 208  29
     String fileName =
 209  58
         String.format("%s.properties", Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
 210  29
             .join(m_configUtil.getAppId(), m_configUtil.getCluster(), namespace));
 211  29
     return new File(baseDir, fileName);
 212  
   }
 213  
 }