Coverage Report - com.ctrip.framework.apollo.internals.DefaultConfig
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultConfig
85%
66/77
85%
34/40
5.333
DefaultConfig$1
100%
1/1
N/A
5.333
 
 1  
 package com.ctrip.framework.apollo.internals;
 2  
 
 3  
 import com.google.common.collect.ImmutableMap;
 4  
 
 5  
 import com.ctrip.framework.apollo.core.utils.ClassLoaderUtil;
 6  
 import com.ctrip.framework.apollo.enums.PropertyChangeType;
 7  
 import com.ctrip.framework.apollo.model.ConfigChange;
 8  
 import com.ctrip.framework.apollo.model.ConfigChangeEvent;
 9  
 import com.ctrip.framework.apollo.util.ExceptionUtil;
 10  
 import com.dianping.cat.Cat;
 11  
 
 12  
 import org.slf4j.Logger;
 13  
 import org.slf4j.LoggerFactory;
 14  
 
 15  
 import java.io.IOException;
 16  
 import java.io.InputStream;
 17  
 import java.util.List;
 18  
 import java.util.Map;
 19  
 import java.util.Objects;
 20  
 import java.util.Properties;
 21  
 import java.util.concurrent.atomic.AtomicReference;
 22  
 
 23  
 
 24  
 /**
 25  
  * @author Jason Song(song_s@ctrip.com)
 26  
  */
 27  
 public class DefaultConfig extends AbstractConfig implements RepositoryChangeListener {
 28  1
   private static final Logger logger = LoggerFactory.getLogger(DefaultConfig.class);
 29  
   private final String m_namespace;
 30  
   private Properties m_resourceProperties;
 31  
   private AtomicReference<Properties> m_configProperties;
 32  
   private ConfigRepository m_configRepository;
 33  
 
 34  
   /**
 35  
    * Constructor.
 36  
    *
 37  
    * @param namespace        the namespace of this config instance
 38  
    * @param configRepository the config repository for this config instance
 39  
    */
 40  11
   public DefaultConfig(String namespace, ConfigRepository configRepository) {
 41  11
     m_namespace = namespace;
 42  11
     m_resourceProperties = loadFromResource(m_namespace);
 43  11
     m_configRepository = configRepository;
 44  11
     m_configProperties = new AtomicReference<>();
 45  11
     initialize();
 46  11
   }
 47  
 
 48  
   private void initialize() {
 49  
     try {
 50  11
       m_configProperties.set(m_configRepository.getConfig());
 51  1
     } catch (Throwable ex) {
 52  1
       Cat.logError(ex);
 53  2
       logger.warn("Init Apollo Local Config failed - namespace: {}, reason: {}.",
 54  1
           m_namespace, ExceptionUtil.getDetailMessage(ex));
 55  
     } finally {
 56  
       //register the change listener no matter config repository is working or not
 57  
       //so that whenever config repository is recovered, config could get changed
 58  11
       m_configRepository.addChangeListener(this);
 59  11
     }
 60  11
   }
 61  
 
 62  
   @Override
 63  
   public String getProperty(String key, String defaultValue) {
 64  
     // step 1: check system properties, i.e. -Dkey=value
 65  30
     String value = System.getProperty(key);
 66  
 
 67  
     // step 2: check local cached properties file
 68  30
     if (value == null && m_configProperties.get() != null) {
 69  26
       value = m_configProperties.get().getProperty(key);
 70  
     }
 71  
 
 72  
     /**
 73  
      * step 3: check env variable, i.e. PATH=...
 74  
      * normally system environment variables are in UPPERCASE, however there might be exceptions.
 75  
      * so the caller should provide the key in the right case
 76  
      */
 77  30
     if (value == null) {
 78  6
       value = System.getenv(key);
 79  
     }
 80  
 
 81  
     // step 4: check properties file from classpath
 82  30
     if (value == null && m_resourceProperties != null) {
 83  4
       value = (String) m_resourceProperties.get(key);
 84  
     }
 85  
 
 86  30
     if (value == null && m_configProperties.get() == null) {
 87  1
       logger.error("Config initialization failed, always return default value!");
 88  
     }
 89  
 
 90  30
     return value == null ? defaultValue : value;
 91  
   }
 92  
 
 93  
   @Override
 94  
   public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
 95  4
     if (newProperties.equals(m_configProperties.get())) {
 96  0
       return;
 97  
     }
 98  4
     Properties newConfigProperties = new Properties();
 99  4
     newConfigProperties.putAll(newProperties);
 100  
 
 101  4
     Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties);
 102  
 
 103  
     //check double checked result
 104  4
     if (actualChanges.isEmpty()) {
 105  0
       return;
 106  
     }
 107  
 
 108  4
     this.fireConfigChange(new ConfigChangeEvent(m_namespace, actualChanges));
 109  
 
 110  4
     Cat.logEvent("Apollo.Client.ConfigChanges", m_namespace);
 111  4
   }
 112  
 
 113  
   private Map<String, ConfigChange> updateAndCalcConfigChanges(Properties newConfigProperties) {
 114  4
     List<ConfigChange> configChanges =
 115  4
         calcPropertyChanges(m_namespace, m_configProperties.get(), newConfigProperties);
 116  
 
 117  4
     ImmutableMap.Builder<String, ConfigChange> actualChanges =
 118  
         new ImmutableMap.Builder<>();
 119  
 
 120  
     /** === Double check since DefaultConfig has multiple config sources ==== **/
 121  
 
 122  
     //1. use getProperty to update configChanges's old value
 123  4
     for (ConfigChange change : configChanges) {
 124  8
       change.setOldValue(this.getProperty(change.getPropertyName(), change.getOldValue()));
 125  8
     }
 126  
 
 127  
     //2. update m_configProperties
 128  4
     m_configProperties.set(newConfigProperties);
 129  
 
 130  
     //3. use getProperty to update configChange's new value and calc the final changes
 131  4
     for (ConfigChange change : configChanges) {
 132  8
       change.setNewValue(this.getProperty(change.getPropertyName(), change.getNewValue()));
 133  1
       switch (change.getChangeType()) {
 134  
         case ADDED:
 135  1
           if (Objects.equals(change.getOldValue(), change.getNewValue())) {
 136  0
             break;
 137  
           }
 138  1
           if (change.getOldValue() != null) {
 139  0
             change.setChangeType(PropertyChangeType.MODIFIED);
 140  
           }
 141  1
           actualChanges.put(change.getPropertyName(), change);
 142  1
           break;
 143  
         case MODIFIED:
 144  5
           if (!Objects.equals(change.getOldValue(), change.getNewValue())) {
 145  4
             actualChanges.put(change.getPropertyName(), change);
 146  
           }
 147  
           break;
 148  
         case DELETED:
 149  2
           if (Objects.equals(change.getOldValue(), change.getNewValue())) {
 150  0
             break;
 151  
           }
 152  2
           if (change.getNewValue() != null) {
 153  1
             change.setChangeType(PropertyChangeType.MODIFIED);
 154  
           }
 155  2
           actualChanges.put(change.getPropertyName(), change);
 156  2
           break;
 157  
         default:
 158  
           //do nothing
 159  
           break;
 160  
       }
 161  8
     }
 162  4
     return actualChanges.build();
 163  
   }
 164  
 
 165  
   private Properties loadFromResource(String namespace) {
 166  11
     String name = String.format("META-INF/config/%s.properties", namespace);
 167  11
     InputStream in = ClassLoaderUtil.getLoader().getResourceAsStream(name);
 168  11
     Properties properties = null;
 169  
 
 170  11
     if (in != null) {
 171  2
       properties = new Properties();
 172  
 
 173  
       try {
 174  2
         properties.load(in);
 175  0
       } catch (IOException ex) {
 176  0
         Cat.logError(ex);
 177  0
         logger.error("Load resource config for namespace {} failed", namespace, ex);
 178  
       } finally {
 179  0
         try {
 180  2
           in.close();
 181  0
         } catch (IOException ex) {
 182  
           // ignore
 183  2
         }
 184  0
       }
 185  
     }
 186  
 
 187  11
     return properties;
 188  
   }
 189  
 }