##### 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;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 |  |
     }