Coverage Report - com.ctrip.framework.apollo.internals.LocalFileConfigRepository
 
Classes in this File Line Coverage Branch Coverage Complexity
LocalFileConfigRepository
78%
110/140
71%
23/32
3.846
 
 1  
 package com.ctrip.framework.apollo.internals;
 2  
 
 3  
 import com.google.common.base.Joiner;
 4  
 import com.google.common.base.Preconditions;
 5  
 
 6  
 import com.ctrip.framework.apollo.core.ConfigConsts;
 7  
 import com.ctrip.framework.apollo.core.utils.ClassLoaderUtil;
 8  
 import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
 9  
 import com.ctrip.framework.apollo.util.ConfigUtil;
 10  
 import com.ctrip.framework.apollo.util.ExceptionUtil;
 11  
 import com.dianping.cat.Cat;
 12  
 import com.dianping.cat.message.Message;
 13  
 import com.dianping.cat.message.Transaction;
 14  
 
 15  
 import org.codehaus.plexus.PlexusContainer;
 16  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 17  
 import org.slf4j.Logger;
 18  
 import org.slf4j.LoggerFactory;
 19  
 import org.unidal.lookup.ContainerLoader;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.FileInputStream;
 23  
 import java.io.FileOutputStream;
 24  
 import java.io.IOException;
 25  
 import java.io.InputStream;
 26  
 import java.io.OutputStream;
 27  
 import java.nio.file.Files;
 28  
 import java.nio.file.Path;
 29  
 import java.nio.file.Paths;
 30  
 import java.util.Properties;
 31  
 
 32  
 /**
 33  
  * @author Jason Song(song_s@ctrip.com)
 34  
  */
 35  
 public class LocalFileConfigRepository extends AbstractConfigRepository
 36  
     implements RepositoryChangeListener {
 37  1
   private static final Logger logger = LoggerFactory.getLogger(LocalFileConfigRepository.class);
 38  
   private static final String CONFIG_DIR = "/config-cache";
 39  
   private final PlexusContainer m_container;
 40  
   private final String m_namespace;
 41  
   private File m_baseDir;
 42  
   private final ConfigUtil m_configUtil;
 43  
   private volatile Properties m_fileProperties;
 44  
   private volatile ConfigRepository m_upstream;
 45  
 
 46  
   /**
 47  
    * Constructor.
 48  
    *
 49  
    * @param namespace the namespace
 50  
    */
 51  14
   public LocalFileConfigRepository(String namespace) {
 52  14
     m_namespace = namespace;
 53  14
     m_container = ContainerLoader.getDefaultContainer();
 54  
     try {
 55  14
       m_configUtil = m_container.lookup(ConfigUtil.class);
 56  0
     } catch (ComponentLookupException ex) {
 57  0
       Cat.logError(ex);
 58  0
       throw new ApolloConfigException("Unable to load component!", ex);
 59  14
     }
 60  14
     this.setLocalCacheDir(findLocalCacheDir());
 61  14
   }
 62  
 
 63  
   void setLocalCacheDir(File baseDir) {
 64  20
     m_baseDir = baseDir;
 65  20
     this.checkLocalConfigCacheDir(m_baseDir);
 66  20
     this.trySync();
 67  20
   }
 68  
 
 69  
   private File findLocalCacheDir() {
 70  
     try {
 71  14
       String defaultCacheDir = m_configUtil.getDefaultLocalCacheDir();
 72  14
       Path path = Paths.get(defaultCacheDir);
 73  14
       if (!Files.exists(path)) {
 74  6
         Files.createDirectories(path);
 75  
       }
 76  8
       if (Files.exists(path) && Files.isWritable(path)) {
 77  8
         return new File(defaultCacheDir, CONFIG_DIR);
 78  
       }
 79  6
     } catch (Throwable ex) {
 80  
       //ignore
 81  0
     }
 82  
 
 83  6
     return new File(ClassLoaderUtil.getClassPath(), CONFIG_DIR);
 84  
   }
 85  
 
 86  
   @Override
 87  
   public Properties getConfig() {
 88  14
     if (m_fileProperties == null) {
 89  1
       sync();
 90  
     }
 91  13
     Properties result = new Properties();
 92  13
     result.putAll(m_fileProperties);
 93  13
     return result;
 94  
   }
 95  
 
 96  
   @Override
 97  
   public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
 98  
     //clear previous listener
 99  12
     if (m_upstream != null) {
 100  0
       m_upstream.removeChangeListener(this);
 101  
     }
 102  12
     m_upstream = upstreamConfigRepository;
 103  12
     trySyncFromUpstream();
 104  12
     upstreamConfigRepository.addChangeListener(this);
 105  12
   }
 106  
 
 107  
   @Override
 108  
   public void onRepositoryChange(String namespace, Properties newProperties) {
 109  11
     if (newProperties.equals(m_fileProperties)) {
 110  7
       return;
 111  
     }
 112  4
     Properties newFileProperties = new Properties();
 113  4
     newFileProperties.putAll(newProperties);
 114  4
     updateFileProperties(newFileProperties);
 115  4
     this.fireRepositoryChange(namespace, newProperties);
 116  4
   }
 117  
 
 118  
   @Override
 119  
   protected void sync() {
 120  
     //sync with upstream immediately
 121  21
    boolean syncFromUpstreamResultSuccess = trySyncFromUpstream();
 122  
 
 123  21
     if (syncFromUpstreamResultSuccess) {
 124  0
       return;
 125  
     }
 126  
 
 127  21
     Transaction transaction = Cat.newTransaction("Apollo.ConfigService", "syncLocalConfig");
 128  21
     Throwable exception = null;
 129  
     try {
 130  21
       transaction.addData("Basedir", m_baseDir.getAbsolutePath());
 131  21
       m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
 132  5
       transaction.setStatus(Message.SUCCESS);
 133  16
     } catch (Throwable ex) {
 134  16
       Cat.logError(ex);
 135  16
       transaction.setStatus(ex);
 136  16
       exception = ex;
 137  
       //ignore
 138  
     } finally {
 139  21
       transaction.complete();
 140  21
     }
 141  
 
 142  21
     if (m_fileProperties == null) {
 143  16
       throw new ApolloConfigException(
 144  
           "Load config from local config failed!", exception);
 145  
     }
 146  5
   }
 147  
 
 148  
   private boolean trySyncFromUpstream() {
 149  33
     if (m_upstream == null) {
 150  20
       return false;
 151  
     }
 152  
     try {
 153  13
       Properties properties = m_upstream.getConfig();
 154  10
       updateFileProperties(properties);
 155  10
       return true;
 156  3
     } catch (Throwable ex) {
 157  3
       Cat.logError(ex);
 158  3
       logger
 159  6
           .warn("Sync config from upstream repository {} failed, reason: {}", m_upstream.getClass(),
 160  3
               ExceptionUtil.getDetailMessage(ex));
 161  
     }
 162  3
     return false;
 163  
   }
 164  
 
 165  
   private synchronized void updateFileProperties(Properties newProperties) {
 166  14
     if (newProperties.equals(m_fileProperties)) {
 167  0
       return;
 168  
     }
 169  14
     this.m_fileProperties = newProperties;
 170  14
     persistLocalCacheFile(m_baseDir, m_namespace);
 171  14
   }
 172  
 
 173  
   private Properties loadFromLocalCacheFile(File baseDir, String namespace) throws IOException {
 174  21
     Preconditions.checkNotNull(baseDir, "Basedir cannot be null");
 175  
 
 176  21
     File file = assembleLocalCacheFile(baseDir, namespace);
 177  21
     Properties properties = null;
 178  
 
 179  21
     if (file.isFile() && file.canRead()) {
 180  5
       InputStream in = null;
 181  
 
 182  
       try {
 183  5
         in = new FileInputStream(file);
 184  
 
 185  5
         properties = new Properties();
 186  5
         properties.load(in);
 187  0
       } catch (IOException ex) {
 188  0
         Cat.logError(ex);
 189  0
         throw new ApolloConfigException(String
 190  0
             .format("Loading config from local cache file %s failed", file.getAbsolutePath()), ex);
 191  
       } finally {
 192  0
         try {
 193  5
           if (in != null) {
 194  5
             in.close();
 195  
           }
 196  0
         } catch (IOException ex) {
 197  
           // ignore
 198  5
         }
 199  0
       }
 200  5
     } else {
 201  16
       throw new ApolloConfigException(
 202  16
           String.format("Cannot read from local cache file %s", file.getAbsolutePath()));
 203  
     }
 204  
 
 205  5
     return properties;
 206  
   }
 207  
 
 208  
   void persistLocalCacheFile(File baseDir, String namespace) {
 209  14
     if (baseDir == null) {
 210  0
       return;
 211  
     }
 212  14
     File file = assembleLocalCacheFile(baseDir, namespace);
 213  
 
 214  14
     OutputStream out = null;
 215  
 
 216  14
     Transaction transaction = Cat.newTransaction("Apollo.ConfigService", "persistLocalConfigFile");
 217  14
     transaction.addData("LocalConfigFile", file.getAbsolutePath());
 218  
     try {
 219  14
       out = new FileOutputStream(file);
 220  14
       m_fileProperties.store(out, "Persisted by DefaultConfig");
 221  14
       transaction.setStatus(Message.SUCCESS);
 222  0
     } catch (IOException ex) {
 223  0
       ApolloConfigException exception =
 224  
           new ApolloConfigException(
 225  0
               String.format("Persist local cache file %s failed", file.getAbsolutePath()), ex);
 226  0
       Cat.logError(exception);
 227  0
       transaction.setStatus(exception);
 228  0
       logger.warn("Persist local cache file {} failed, reason: {}.", file.getAbsolutePath(),
 229  0
           ExceptionUtil.getDetailMessage(ex));
 230  
     } finally {
 231  14
       if (out != null) {
 232  
         try {
 233  14
           out.close();
 234  0
         } catch (IOException ex) {
 235  
           //ignore
 236  14
         }
 237  
       }
 238  14
       transaction.complete();
 239  14
     }
 240  14
   }
 241  
 
 242  
   private void checkLocalConfigCacheDir(File baseDir) {
 243  20
     if (baseDir.exists()) {
 244  19
       return;
 245  
     }
 246  1
     Transaction transaction = Cat.newTransaction("Apollo.ConfigService", "createLocalConfigDir");
 247  1
     transaction.addData("BaseDir", baseDir.getAbsolutePath());
 248  
     try {
 249  1
       Files.createDirectory(baseDir.toPath());
 250  1
       transaction.setStatus(Message.SUCCESS);
 251  0
     } catch (IOException ex) {
 252  0
       ApolloConfigException exception =
 253  
           new ApolloConfigException(
 254  0
               String.format("Create local config directory %s failed", baseDir.getAbsolutePath()), ex);
 255  0
       Cat.logError(exception);
 256  0
       transaction.setStatus(exception);
 257  0
       logger.warn(
 258  
           "Unable to create local config cache directory {}, reason: {}. Will not able to cache config file.",
 259  0
           baseDir.getAbsolutePath(), ExceptionUtil.getDetailMessage(ex));
 260  
     } finally {
 261  1
       transaction.complete();
 262  1
     }
 263  1
   }
 264  
 
 265  
   File assembleLocalCacheFile(File baseDir, String namespace) {
 266  35
     String fileName =
 267  70
         String.format("%s.properties", Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
 268  35
             .join(m_configUtil.getAppId(), m_configUtil.getCluster(), namespace));
 269  35
     return new File(baseDir, fileName);
 270  
   }
 271  
 }