0
# Base Classes
1
2
Abstract base classes providing common functionality for data source implementations, including automatic refresh capabilities and configuration parsing patterns.
3
4
## Capabilities
5
6
### AbstractDataSource Class
7
8
The `AbstractDataSource` class provides basic functionality for loading and parsing configurations, implementing the common patterns shared by most readable data sources.
9
10
```java { .api }
11
/**
12
* The abstract readable data source provides basic functionality for loading and parsing config.
13
* @param <S> source data type
14
* @param <T> target data type
15
*/
16
public abstract class AbstractDataSource<S, T> implements ReadableDataSource<S, T> {
17
18
protected final Converter<S, T> parser;
19
protected final SentinelProperty<T> property;
20
21
/**
22
* Constructor with converter/parser.
23
* @param parser the converter to transform source data to target type
24
* @throws IllegalArgumentException if parser is null
25
*/
26
public AbstractDataSource(Converter<S, T> parser);
27
28
/**
29
* Load config from readSource().
30
* @return the target data
31
* @throws Exception IO or other error occurs
32
*/
33
public T loadConfig() throws Exception;
34
35
/**
36
* Load config from provided source data.
37
* @param conf the source data
38
* @return the target data converted from source
39
* @throws Exception conversion error occurs
40
*/
41
public T loadConfig(S conf) throws Exception;
42
43
/**
44
* Get SentinelProperty of the data source.
45
* @return the property for dynamic updates
46
*/
47
public SentinelProperty<T> getProperty();
48
}
49
```
50
51
**Usage Examples:**
52
53
```java
54
// Custom implementation extending AbstractDataSource
55
public class HttpReadableDataSource<T> extends AbstractDataSource<String, T> {
56
private final String url;
57
private final Map<String, String> headers;
58
59
public HttpReadableDataSource(String url, Converter<String, T> parser) {
60
super(parser);
61
this.url = url;
62
this.headers = new HashMap<>();
63
}
64
65
@Override
66
public String readSource() throws Exception {
67
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
68
69
// Add headers
70
for (Map.Entry<String, String> header : headers.entrySet()) {
71
conn.setRequestProperty(header.getKey(), header.getValue());
72
}
73
74
// Read response
75
try (BufferedReader reader = new BufferedReader(
76
new InputStreamReader(conn.getInputStream()))) {
77
StringBuilder response = new StringBuilder();
78
String line;
79
while ((line = reader.readLine()) != null) {
80
response.append(line);
81
}
82
return response.toString();
83
}
84
}
85
86
@Override
87
public void close() throws Exception {
88
// Cleanup HTTP connections if needed
89
}
90
91
public void addHeader(String name, String value) {
92
headers.put(name, value);
93
}
94
}
95
96
// Usage
97
Converter<String, List<FlowRule>> converter = source ->
98
JSON.parseArray(source, FlowRule.class);
99
100
HttpReadableDataSource<List<FlowRule>> httpDs =
101
new HttpReadableDataSource<>("http://config-server/flow-rules", converter);
102
103
httpDs.addHeader("Authorization", "Bearer token123");
104
105
List<FlowRule> rules = httpDs.loadConfig();
106
```
107
108
### AutoRefreshDataSource Class
109
110
The `AutoRefreshDataSource` class extends `AbstractDataSource` to provide automatic refresh capabilities with configurable intervals.
111
112
```java { .api }
113
/**
114
* A ReadableDataSource automatically fetches the backend data.
115
* @param <S> source data type
116
* @param <T> target data type
117
*/
118
public abstract class AutoRefreshDataSource<S, T> extends AbstractDataSource<S, T> {
119
120
protected long recommendRefreshMs;
121
122
/**
123
* Constructor with default refresh interval (3000ms).
124
* @param configParser the converter to transform source data
125
*/
126
public AutoRefreshDataSource(Converter<S, T> configParser);
127
128
/**
129
* Constructor with custom refresh interval.
130
* @param configParser the converter to transform source data
131
* @param recommendRefreshMs refresh interval in milliseconds (must be > 0)
132
* @throws IllegalArgumentException if recommendRefreshMs <= 0
133
*/
134
public AutoRefreshDataSource(Converter<S, T> configParser, long recommendRefreshMs);
135
136
/**
137
* Close the data source and shutdown the refresh scheduler.
138
* @throws Exception if cleanup fails
139
*/
140
public void close() throws Exception;
141
142
/**
143
* Check if the source has been modified since last read.
144
* Override this method to implement custom modification detection.
145
* @return true if source is modified, false otherwise
146
*/
147
protected boolean isModified();
148
}
149
```
150
151
**Usage Examples:**
152
153
```java
154
// Custom auto-refresh implementation
155
public class DatabaseAutoRefreshDataSource<T> extends AutoRefreshDataSource<ResultSet, T> {
156
private final String connectionUrl;
157
private final String query;
158
private long lastChecksum = 0;
159
160
public DatabaseAutoRefreshDataSource(
161
String connectionUrl,
162
String query,
163
Converter<ResultSet, T> converter) {
164
super(converter, 5000); // Refresh every 5 seconds
165
this.connectionUrl = connectionUrl;
166
this.query = query;
167
}
168
169
@Override
170
public ResultSet readSource() throws Exception {
171
Connection conn = DriverManager.getConnection(connectionUrl);
172
PreparedStatement stmt = conn.prepareStatement(query);
173
return stmt.executeQuery();
174
}
175
176
@Override
177
protected boolean isModified() {
178
try {
179
// Check if database has updates using a checksum query
180
Connection conn = DriverManager.getConnection(connectionUrl);
181
PreparedStatement stmt = conn.prepareStatement(
182
"SELECT CHECKSUM(*) FROM configuration_table");
183
ResultSet rs = stmt.executeQuery();
184
185
if (rs.next()) {
186
long currentChecksum = rs.getLong(1);
187
if (currentChecksum != lastChecksum) {
188
lastChecksum = currentChecksum;
189
return true;
190
}
191
}
192
return false;
193
} catch (Exception e) {
194
// On error, assume modified to trigger refresh
195
return true;
196
}
197
}
198
199
@Override
200
public void close() throws Exception {
201
super.close();
202
// Close database connections
203
}
204
}
205
206
// Usage with property listener
207
Converter<ResultSet, List<FlowRule>> dbConverter = rs -> {
208
List<FlowRule> rules = new ArrayList<>();
209
while (rs.next()) {
210
FlowRule rule = new FlowRule();
211
rule.setResource(rs.getString("resource"));
212
rule.setCount(rs.getDouble("qps"));
213
rules.add(rule);
214
}
215
return rules;
216
};
217
218
DatabaseAutoRefreshDataSource<List<FlowRule>> dbDs =
219
new DatabaseAutoRefreshDataSource<>(
220
"jdbc:mysql://localhost/sentinel",
221
"SELECT * FROM flow_rules WHERE enabled = 1",
222
dbConverter
223
);
224
225
// Auto-refresh will update rules every 5 seconds
226
dbDs.getProperty().addListener(rules -> {
227
FlowRuleManager.loadRules(rules);
228
System.out.println("Rules refreshed from database: " + rules.size());
229
});
230
```
231
232
## Refresh Mechanism Details
233
234
The `AutoRefreshDataSource` uses a scheduled executor service to periodically check for updates:
235
236
- **Default refresh interval**: 3000ms (3 seconds)
237
- **Thread management**: Uses `NamedThreadFactory` for proper thread naming
238
- **Error handling**: Logs errors but continues refresh attempts
239
- **Modification detection**: Calls `isModified()` before each refresh
240
- **Property updates**: Only updates the property when new data is loaded
241
242
## Lifecycle Management
243
244
Both base classes integrate with Sentinel's property system for proper lifecycle management:
245
246
```java
247
// Proper setup and cleanup
248
ReadableDataSource<String, List<FlowRule>> ds =
249
new FileRefreshableDataSource<>("/config/rules.json", converter);
250
251
// Register for updates
252
ds.getProperty().addListener(FlowRuleManager::loadRules);
253
254
// Always close when done
255
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
256
try {
257
ds.close();
258
} catch (Exception e) {
259
System.err.println("Error closing data source: " + e.getMessage());
260
}
261
}));
262
```
263
264
## Thread Safety
265
266
- **AbstractDataSource**: Thread-safe for read operations
267
- **AutoRefreshDataSource**: Uses thread-safe scheduled execution
268
- **Property updates**: Properly synchronized through Sentinel's property system
269
- **Custom implementations**: Should ensure thread safety in `readSource()` and `isModified()` methods