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