0
# Asset Management
1
2
Asset management provides implementations for various content types that can be stored within archives, including service provider configurations, ZIP file entries, and other specialized asset types.
3
4
## Core Asset Implementations
5
6
### ServiceProviderAsset
7
8
Creates service provider configuration files for the Java ServiceLoader mechanism (META-INF/services/).
9
10
```java { .api }
11
public class ServiceProviderAsset implements Asset
12
```
13
14
**Constructors:**
15
```java { .api }
16
public ServiceProviderAsset(Class<?>... providerImpls)
17
public ServiceProviderAsset(String... providerImpls)
18
```
19
20
**Key Methods:**
21
```java { .api }
22
public InputStream openStream()
23
public long getSize()
24
```
25
26
**Usage Examples:**
27
28
**Single Service Implementation:**
29
```java
30
// Create service provider file for single implementation
31
ServiceProviderAsset asset = new ServiceProviderAsset(MyServiceImpl.class);
32
archive.addAsManifestResource(asset, "services/com.example.MyService");
33
```
34
35
**Multiple Service Implementations:**
36
```java
37
// Create service provider file for multiple implementations
38
ServiceProviderAsset asset = new ServiceProviderAsset(
39
DatabasePlugin.class,
40
CachePlugin.class,
41
LoggingPlugin.class
42
);
43
archive.addAsManifestResource(asset, "services/com.example.Plugin");
44
```
45
46
**String-Based Configuration:**
47
```java
48
// Using class names as strings
49
ServiceProviderAsset asset = new ServiceProviderAsset(
50
"com.example.impl.DefaultService",
51
"com.example.impl.AlternativeService"
52
);
53
```
54
55
**Generated Content Format:**
56
The asset generates content in the standard ServiceLoader format:
57
```
58
com.example.impl.DefaultService
59
com.example.impl.AlternativeService
60
# Comments are supported
61
```
62
63
### ZipFileEntryAsset
64
65
Represents an asset backed by an entry from a ZIP file, enabling direct access to ZIP file contents.
66
67
```java { .api }
68
public class ZipFileEntryAsset implements Asset
69
```
70
71
**Constructors:**
72
```java { .api }
73
public ZipFileEntryAsset(ZipFile zipFile, ZipEntry entry)
74
public ZipFileEntryAsset(File zipFile, String entryName)
75
```
76
77
**Key Methods:**
78
```java { .api }
79
public InputStream openStream()
80
public long getSize()
81
public long getLastModified()
82
```
83
84
**Usage Examples:**
85
86
**Direct ZIP Entry Access:**
87
```java
88
ZipFile sourceZip = new ZipFile("source.zip");
89
ZipEntry entry = sourceZip.getEntry("config/app.properties");
90
ZipFileEntryAsset asset = new ZipFileEntryAsset(sourceZip, entry);
91
92
archive.addAsResource(asset, "app.properties");
93
```
94
95
**File-Based ZIP Access:**
96
```java
97
ZipFileEntryAsset asset = new ZipFileEntryAsset(
98
new File("dependencies.jar"),
99
"META-INF/MANIFEST.MF"
100
);
101
archive.addAsManifestResource(asset, "MANIFEST.MF");
102
```
103
104
## Asset Utility Classes
105
106
### IOUtilDelegator
107
108
Utility delegator for I/O operations on assets and streams.
109
110
```java { .api }
111
public class IOUtilDelegator
112
```
113
114
**Key Methods:**
115
```java { .api }
116
public static byte[] asByteArray(InputStream in)
117
public static String asUTF8String(InputStream in)
118
public static void copy(InputStream input, OutputStream output)
119
```
120
121
**Usage:**
122
```java
123
// Convert asset to byte array
124
Asset asset = // ... get asset
125
byte[] content = IOUtilDelegator.asByteArray(asset.openStream());
126
127
// Convert asset to string
128
String textContent = IOUtilDelegator.asUTF8String(asset.openStream());
129
```
130
131
## Advanced Asset Operations
132
133
### Asset Composition
134
135
Creating composite assets from multiple sources:
136
137
```java
138
public class CompositeAsset implements Asset {
139
private final List<Asset> assets;
140
141
public CompositeAsset(Asset... assets) {
142
this.assets = Arrays.asList(assets);
143
}
144
145
@Override
146
public InputStream openStream() {
147
List<InputStream> streams = assets.stream()
148
.map(Asset::openStream)
149
.collect(Collectors.toList());
150
return new SequenceInputStream(Collections.enumeration(streams));
151
}
152
153
@Override
154
public long getSize() {
155
return assets.stream().mapToLong(Asset::getSize).sum();
156
}
157
}
158
159
// Usage: Combine multiple configuration files
160
CompositeAsset combined = new CompositeAsset(
161
new ClassLoaderAsset("config-base.properties"),
162
new ClassLoaderAsset("config-env.properties"),
163
new FileAsset("config-local.properties")
164
);
165
archive.addAsResource(combined, "application.properties");
166
```
167
168
### Asset Filtering
169
170
Filtering asset content during addition:
171
172
```java
173
public class FilteredAsset implements Asset {
174
private final Asset delegate;
175
private final Predicate<String> lineFilter;
176
177
public FilteredAsset(Asset delegate, Predicate<String> lineFilter) {
178
this.delegate = delegate;
179
this.lineFilter = lineFilter;
180
}
181
182
@Override
183
public InputStream openStream() {
184
BufferedReader reader = new BufferedReader(
185
new InputStreamReader(delegate.openStream())
186
);
187
188
String filtered = reader.lines()
189
.filter(lineFilter)
190
.collect(Collectors.joining("\n"));
191
192
return new ByteArrayInputStream(filtered.getBytes());
193
}
194
}
195
196
// Usage: Filter out comments and empty lines
197
Asset original = new FileAsset("config.properties");
198
Asset filtered = new FilteredAsset(original,
199
line -> !line.trim().isEmpty() && !line.startsWith("#"));
200
archive.addAsResource(filtered, "config.properties");
201
```
202
203
### Asset Transformation
204
205
Transforming asset content during processing:
206
207
```java
208
public class TransformingAsset implements Asset {
209
private final Asset source;
210
private final Function<String, String> transformer;
211
212
public TransformingAsset(Asset source, Function<String, String> transformer) {
213
this.source = source;
214
this.transformer = transformer;
215
}
216
217
@Override
218
public InputStream openStream() {
219
try (Scanner scanner = new Scanner(source.openStream())) {
220
String content = scanner.useDelimiter("\\A").next();
221
String transformed = transformer.apply(content);
222
return new ByteArrayInputStream(transformed.getBytes());
223
}
224
}
225
}
226
227
// Usage: Replace placeholders in configuration files
228
Asset template = new ClassLoaderAsset("config-template.xml");
229
Asset configured = new TransformingAsset(template, content ->
230
content.replace("${app.name}", "MyApplication")
231
.replace("${app.version}", "1.0.0"));
232
archive.addAsResource(configured, "config.xml");
233
```
234
235
## Service Provider Management
236
237
### Advanced Service Provider Configuration
238
239
Creating complex service provider configurations:
240
241
```java
242
public class AdvancedServiceProviderAsset implements Asset {
243
private final Map<String, List<String>> serviceProviders;
244
245
public AdvancedServiceProviderAsset() {
246
this.serviceProviders = new HashMap<>();
247
}
248
249
public AdvancedServiceProviderAsset addProvider(Class<?> service, Class<?>... implementations) {
250
String serviceName = service.getName();
251
List<String> impls = Arrays.stream(implementations)
252
.map(Class::getName)
253
.collect(Collectors.toList());
254
serviceProviders.put(serviceName, impls);
255
return this;
256
}
257
258
public void addToArchive(Archive<?> archive) {
259
serviceProviders.forEach((service, implementations) -> {
260
ServiceProviderAsset asset = new ServiceProviderAsset(
261
implementations.toArray(new String[0])
262
);
263
archive.addAsManifestResource(asset, "services/" + service);
264
});
265
}
266
}
267
268
// Usage: Configure multiple services
269
AdvancedServiceProviderAsset serviceConfig = new AdvancedServiceProviderAsset()
270
.addProvider(DatabaseService.class, MySQLService.class, PostgreSQLService.class)
271
.addProvider(CacheService.class, RedisService.class, MemcachedService.class)
272
.addProvider(LoggingService.class, Log4jService.class);
273
274
serviceConfig.addToArchive(archive);
275
```
276
277
### Service Provider Discovery
278
279
Utilities for discovering existing service providers:
280
281
```java
282
public class ServiceProviderDiscovery {
283
public static Set<String> discoverProviders(Class<?> serviceClass, ClassLoader classLoader) {
284
ServiceLoader<?> loader = ServiceLoader.load(serviceClass, classLoader);
285
return StreamSupport.stream(loader.spliterator(), false)
286
.map(provider -> provider.getClass().getName())
287
.collect(Collectors.toSet());
288
}
289
290
public static ServiceProviderAsset createFromDiscovered(Class<?> serviceClass) {
291
Set<String> providers = discoverProviders(serviceClass,
292
Thread.currentThread().getContextClassLoader());
293
return new ServiceProviderAsset(providers.toArray(new String[0]));
294
}
295
}
296
297
// Usage: Auto-discover and configure services
298
ServiceProviderAsset auto = ServiceProviderDiscovery.createFromDiscovered(Plugin.class);
299
archive.addAsManifestResource(auto, "services/com.example.Plugin");
300
```
301
302
## Asset Validation
303
304
### Content Validation
305
306
Validating asset content before inclusion:
307
308
```java
309
public class ValidatingAsset implements Asset {
310
private final Asset delegate;
311
private final Predicate<byte[]> validator;
312
313
public ValidatingAsset(Asset delegate, Predicate<byte[]> validator) {
314
this.delegate = delegate;
315
this.validator = validator;
316
}
317
318
@Override
319
public InputStream openStream() {
320
byte[] content = IOUtilDelegator.asByteArray(delegate.openStream());
321
322
if (!validator.test(content)) {
323
throw new IllegalStateException("Asset content validation failed");
324
}
325
326
return new ByteArrayInputStream(content);
327
}
328
}
329
330
// Usage: Validate XML content
331
Predicate<byte[]> xmlValidator = content -> {
332
try {
333
DocumentBuilder builder = DocumentBuilderFactory.newInstance()
334
.newDocumentBuilder();
335
builder.parse(new ByteArrayInputStream(content));
336
return true;
337
} catch (Exception e) {
338
return false;
339
}
340
};
341
342
Asset xmlAsset = new ValidatingAsset(
343
new FileAsset("config.xml"),
344
xmlValidator
345
);
346
```
347
348
## Performance Considerations
349
350
### Lazy Loading
351
352
Assets support lazy loading to minimize memory usage:
353
354
```java
355
// Assets are loaded on-demand
356
Asset asset = new FileAsset("large-file.dat");
357
archive.add(asset, "/data/large-file.dat");
358
359
// Content is not loaded until openStream() is called
360
InputStream stream = archive.get("/data/large-file.dat")
361
.getAsset()
362
.openStream();
363
```
364
365
### Memory Management
366
367
Efficient memory usage with streaming operations:
368
369
```java
370
// Stream large assets without loading entirely into memory
371
public static void copyLargeAsset(Asset source, OutputStream target) {
372
try (InputStream input = source.openStream()) {
373
IOUtilDelegator.copy(input, target);
374
}
375
}
376
```
377
378
### Asset Caching
379
380
Caching for frequently accessed assets:
381
382
```java
383
public class CachedAsset implements Asset {
384
private final Asset delegate;
385
private byte[] cachedContent;
386
387
public CachedAsset(Asset delegate) {
388
this.delegate = delegate;
389
}
390
391
@Override
392
public synchronized InputStream openStream() {
393
if (cachedContent == null) {
394
cachedContent = IOUtilDelegator.asByteArray(delegate.openStream());
395
}
396
return new ByteArrayInputStream(cachedContent);
397
}
398
}
399
```