or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

base-classes.mdcore-interfaces.mdfile-datasources.mdindex.mdspecialized-datasources.md

specialized-datasources.mddocs/

0

# Specialized Data Sources

1

2

Specialized data source implementations for specific use cases including JAR-embedded resources and empty data sources for default configurations.

3

4

## Capabilities

5

6

### FileInJarReadableDataSource Class

7

8

The `FileInJarReadableDataSource` class provides read-only access to files embedded within JAR archives, useful for loading default configurations packaged with applications.

9

10

```java { .api }

11

/**

12

* A ReadableDataSource based on jar file. This class can only read file initially

13

* when it loads file - no auto-refresh capability.

14

* Limitations: Default read buffer size is 1 MB, while max allowed buffer size is 4MB.

15

* File size should not exceed the buffer size, or exception will be thrown. Default charset is UTF-8.

16

* @param <T> target data type

17

*/

18

public class FileInJarReadableDataSource<T> extends AbstractDataSource<String, T> {

19

20

// Constants

21

public static final int DEFAULT_BUF_SIZE = 1024 * 1024; // 1 MB

22

public static final int MAX_SIZE = 1024 * 1024 * 4; // 4 MB

23

24

/**

25

* Create a JAR file reader with default settings.

26

* @param jarName the jar file path to read from

27

* @param fileInJarName the file path within the JAR

28

* @param configParser the config decoder (parser)

29

* @throws IOException if JAR file cannot be accessed or file not found in JAR

30

* @throws IllegalArgumentException if jarName or fileInJarName is blank

31

*/

32

public FileInJarReadableDataSource(String jarName, String fileInJarName,

33

Converter<String, T> configParser) throws IOException;

34

35

/**

36

* Create a JAR file reader with custom buffer size.

37

* @param jarName the jar file path to read from

38

* @param fileInJarName the file path within the JAR

39

* @param configParser the config decoder (parser)

40

* @param bufSize buffer size for reading (must be between 0 and MAX_SIZE)

41

* @throws IOException if JAR file cannot be accessed or file not found in JAR

42

* @throws IllegalArgumentException if parameters are invalid

43

*/

44

public FileInJarReadableDataSource(String jarName, String fileInJarName,

45

Converter<String, T> configParser, int bufSize) throws IOException;

46

47

/**

48

* Create a JAR file reader with custom charset.

49

* @param jarName the jar file path to read from

50

* @param fileInJarName the file path within the JAR

51

* @param configParser the config decoder (parser)

52

* @param charset character encoding for reading the file

53

* @throws IOException if JAR file cannot be accessed or file not found in JAR

54

* @throws IllegalArgumentException if charset is null

55

*/

56

public FileInJarReadableDataSource(String jarName, String fileInJarName,

57

Converter<String, T> configParser, Charset charset) throws IOException;

58

59

/**

60

* Create a JAR file reader with full customization.

61

* @param jarName the jar file path to read from

62

* @param fileInJarName the file path within the JAR

63

* @param configParser the config decoder (parser)

64

* @param bufSize buffer size for reading (must be between 0 and MAX_SIZE)

65

* @param charset character encoding for reading the file

66

* @throws IOException if JAR file cannot be accessed or file not found in JAR

67

* @throws IllegalArgumentException if parameters are invalid

68

*/

69

public FileInJarReadableDataSource(String jarName, String fileInJarName,

70

Converter<String, T> configParser, int bufSize, Charset charset) throws IOException;

71

72

/**

73

* Read file content from JAR as string.

74

* @return file content from JAR with specified charset

75

* @throws Exception if JAR reading fails or file size exceeds buffer

76

*/

77

public String readSource() throws Exception;

78

79

/**

80

* Close data source and clean up resources.

81

* @throws Exception if cleanup fails

82

*/

83

public void close() throws Exception;

84

}

85

```

86

87

**Usage Examples:**

88

89

```java

90

// Load default rules from application JAR

91

Converter<String, List<FlowRule>> defaultRuleConverter = source -> {

92

return JSON.parseArray(source, FlowRule.class);

93

};

94

95

// Read default flow rules packaged in the application JAR

96

FileInJarReadableDataSource<List<FlowRule>> defaultRulesDs =

97

new FileInJarReadableDataSource<>(

98

"/path/to/application.jar",

99

"config/default-flow-rules.json",

100

defaultRuleConverter

101

);

102

103

// Load default configuration on startup

104

List<FlowRule> defaultRules = defaultRulesDs.loadConfig();

105

FlowRuleManager.loadRules(defaultRules);

106

107

// Library configuration pattern

108

public class SentinelConfigurationManager {

109

private static final String CONFIG_JAR = "sentinel-config-1.0.jar";

110

111

public static void loadDefaultConfiguration() throws IOException {

112

// Load different rule types from embedded JAR

113

loadDefaultFlowRules();

114

loadDefaultDegradeRules();

115

loadDefaultSystemRules();

116

}

117

118

private static void loadDefaultFlowRules() throws IOException {

119

FileInJarReadableDataSource<List<FlowRule>> ds =

120

new FileInJarReadableDataSource<>(

121

CONFIG_JAR,

122

"defaults/flow-rules.json",

123

source -> JSON.parseArray(source, FlowRule.class)

124

);

125

126

FlowRuleManager.loadRules(ds.loadConfig());

127

ds.close();

128

}

129

130

private static void loadDefaultDegradeRules() throws IOException {

131

FileInJarReadableDataSource<List<DegradeRule>> ds =

132

new FileInJarReadableDataSource<>(

133

CONFIG_JAR,

134

"defaults/degrade-rules.xml",

135

xmlConverter,

136

2 * 1024 * 1024 // 2MB buffer for larger XML files

137

);

138

139

DegradeRuleManager.loadRules(ds.loadConfig());

140

ds.close();

141

}

142

}

143

144

// Reading configuration templates

145

FileInJarReadableDataSource<Map<String, Object>> templateDs =

146

new FileInJarReadableDataSource<>(

147

"config-templates.jar",

148

"templates/microservice-config.yaml",

149

source -> {

150

Yaml yaml = new Yaml();

151

return yaml.load(source);

152

}

153

);

154

155

Map<String, Object> template = templateDs.loadConfig();

156

```

157

158

### EmptyDataSource Class

159

160

The `EmptyDataSource` class provides a no-op data source implementation for default scenarios where no external configuration is needed.

161

162

```java { .api }

163

/**

164

* A ReadableDataSource based on nothing. getProperty() will always return the same cached

165

* SentinelProperty that does nothing.

166

* This class is used when we want to use default settings instead of configs from ReadableDataSource.

167

*/

168

public final class EmptyDataSource implements ReadableDataSource<Object, Object> {

169

170

/**

171

* Singleton instance of empty data source.

172

*/

173

public static final ReadableDataSource<Object, Object> EMPTY_DATASOURCE;

174

175

/**

176

* Load config (always returns null).

177

* @return null

178

* @throws Exception never throws

179

*/

180

public Object loadConfig() throws Exception;

181

182

/**

183

* Read source (always returns null).

184

* @return null

185

* @throws Exception never throws

186

*/

187

public Object readSource() throws Exception;

188

189

/**

190

* Get property (returns no-op property).

191

* @return SentinelProperty that performs no operations

192

*/

193

public SentinelProperty<Object> getProperty();

194

195

/**

196

* Close data source (no-op).

197

* @throws Exception never throws

198

*/

199

public void close() throws Exception;

200

}

201

```

202

203

**Usage Examples:**

204

205

```java

206

// Default configuration pattern

207

public class ConfigurationBuilder {

208

private ReadableDataSource<String, List<FlowRule>> flowRuleSource = EmptyDataSource.EMPTY_DATASOURCE;

209

private ReadableDataSource<String, List<DegradeRule>> degradeRuleSource = EmptyDataSource.EMPTY_DATASOURCE;

210

211

public ConfigurationBuilder withFlowRuleSource(ReadableDataSource<String, List<FlowRule>> source) {

212

this.flowRuleSource = source;

213

return this;

214

}

215

216

public ConfigurationBuilder withDegradeRuleSource(ReadableDataSource<String, List<DegradeRule>> source) {

217

this.degradeRuleSource = source;

218

return this;

219

}

220

221

public void build() {

222

// Use empty data source as fallback

223

if (flowRuleSource == EmptyDataSource.EMPTY_DATASOURCE) {

224

System.out.println("Using default flow rule configuration");

225

} else {

226

flowRuleSource.getProperty().addListener(FlowRuleManager::loadRules);

227

}

228

229

if (degradeRuleSource == EmptyDataSource.EMPTY_DATASOURCE) {

230

System.out.println("Using default degrade rule configuration");

231

} else {

232

degradeRuleSource.getProperty().addListener(DegradeRuleManager::loadRules);

233

}

234

}

235

}

236

237

// Conditional data source setup

238

public class DataSourceFactory {

239

public static ReadableDataSource<String, List<FlowRule>> createFlowRuleSource(String configPath) {

240

if (configPath == null || configPath.trim().isEmpty()) {

241

System.out.println("No flow rule configuration specified, using empty data source");

242

return EmptyDataSource.EMPTY_DATASOURCE;

243

}

244

245

try {

246

return new FileRefreshableDataSource<>(

247

new File(configPath),

248

source -> JSON.parseArray(source, FlowRule.class)

249

);

250

} catch (FileNotFoundException e) {

251

System.err.println("Flow rule file not found: " + configPath + ", using empty data source");

252

return EmptyDataSource.EMPTY_DATASOURCE;

253

}

254

}

255

}

256

257

// Testing scenarios

258

@Test

259

public void testWithoutExternalConfiguration() {

260

ReadableDataSource<Object, Object> emptyDs = EmptyDataSource.EMPTY_DATASOURCE;

261

262

// Should not throw exceptions

263

Object config = emptyDs.loadConfig();

264

Object source = emptyDs.readSource();

265

SentinelProperty<Object> property = emptyDs.getProperty();

266

267

assertNull(config);

268

assertNull(source);

269

assertNotNull(property);

270

271

// Property operations should be no-ops

272

property.addListener(value -> fail("Should not be called"));

273

property.updateValue("test"); // Should not trigger listener

274

275

emptyDs.close(); // Should not throw

276

}

277

```

278

279

## Integration Patterns

280

281

### Fallback Configuration Strategy

282

283

Use specialized data sources as fallbacks in configuration hierarchies:

284

285

```java

286

public class HierarchicalConfigurationManager {

287

288

public static ReadableDataSource<String, List<FlowRule>> createFlowRuleSource() {

289

// Try external file first

290

String externalConfigPath = System.getProperty("sentinel.flow.rules.file");

291

if (externalConfigPath != null) {

292

try {

293

return new FileRefreshableDataSource<>(

294

new File(externalConfigPath),

295

source -> JSON.parseArray(source, FlowRule.class)

296

);

297

} catch (FileNotFoundException e) {

298

System.err.println("External config file not found: " + externalConfigPath);

299

}

300

}

301

302

// Try JAR-embedded defaults

303

String jarPath = getApplicationJarPath();

304

if (jarPath != null) {

305

try {

306

return new FileInJarReadableDataSource<>(

307

jarPath,

308

"config/default-flow-rules.json",

309

source -> JSON.parseArray(source, FlowRule.class)

310

);

311

} catch (IOException e) {

312

System.err.println("Could not load default rules from JAR: " + e.getMessage());

313

}

314

}

315

316

// Final fallback to empty data source

317

System.out.println("Using empty data source - no flow rules will be loaded");

318

return EmptyDataSource.EMPTY_DATASOURCE;

319

}

320

}

321

```

322

323

### Resource Management

324

325

Properly manage resources when using JAR-based data sources:

326

327

```java

328

public class ConfigurationLoader implements AutoCloseable {

329

private final List<ReadableDataSource<?, ?>> dataSources = new ArrayList<>();

330

331

public void loadConfiguration(String applicationJar) throws IOException {

332

// Load multiple configuration files from JAR

333

String[] configFiles = {

334

"config/flow-rules.json",

335

"config/degrade-rules.json",

336

"config/system-rules.json"

337

};

338

339

for (String configFile : configFiles) {

340

try {

341

FileInJarReadableDataSource<List<Rule>> ds =

342

new FileInJarReadableDataSource<>(

343

applicationJar,

344

configFile,

345

source -> parseRules(source, configFile)

346

);

347

348

dataSources.add(ds);

349

350

// Load configuration immediately

351

List<Rule> rules = ds.loadConfig();

352

applyRules(rules, configFile);

353

354

} catch (IOException e) {

355

System.err.println("Failed to load " + configFile + ": " + e.getMessage());

356

}

357

}

358

}

359

360

@Override

361

public void close() throws Exception {

362

for (ReadableDataSource<?, ?> ds : dataSources) {

363

try {

364

ds.close();

365

} catch (Exception e) {

366

System.err.println("Error closing data source: " + e.getMessage());

367

}

368

}

369

dataSources.clear();

370

}

371

}

372

```

373

374

## Limitations and Considerations

375

376

### FileInJarReadableDataSource Limitations

377

378

- **No auto-refresh**: JAR files are read-only and don't support modification detection

379

- **Buffer size limits**: Files larger than buffer size will cause exceptions

380

- **JAR file access**: Requires proper file system access to JAR files

381

- **Resource cleanup**: Always close data sources to release JAR file handles

382

383

### EmptyDataSource Characteristics

384

385

- **Singleton pattern**: Use `EMPTY_DATASOURCE` constant, don't create new instances

386

- **No-op behavior**: All operations return null or perform no actions

387

- **Property system**: Returns a no-op property that ignores listener registrations

388

- **Testing friendly**: Safe to use in unit tests and default configurations