0
# Plugin Architecture
1
2
Extensible plugin system for customizing build processes and analysis, enabling powerful extensions to the bndlib functionality.
3
4
## Capabilities
5
6
### Plugin
7
8
Base interface for all BND plugins providing common configuration and reporting capabilities.
9
10
```java { .api }
11
/**
12
* Base interface for all BND plugins
13
*/
14
public interface Plugin {
15
/** Set plugin properties from configuration */
16
public void setProperties(Map<String,String> map);
17
18
/** Set reporter for logging and error reporting */
19
public void setReporter(Reporter processor);
20
}
21
```
22
23
### AnalyzerPlugin
24
25
Plugin interface for custom class analysis during bundle creation and JAR processing.
26
27
```java { .api }
28
/**
29
* Plugin that can analyze classes during bundle creation
30
*/
31
public interface AnalyzerPlugin extends Plugin {
32
/** Analyze JAR and modify analyzer state */
33
public boolean analyzeJar(Analyzer analyzer) throws Exception;
34
35
/** Get plugin priority for execution order */
36
default int getPriority() { return 0; }
37
38
/** Check if plugin applies to current context */
39
default boolean applies(Analyzer analyzer) { return true; }
40
}
41
```
42
43
**Usage Examples:**
44
45
```java
46
import aQute.bnd.service.AnalyzerPlugin;
47
import aQute.bnd.osgi.Analyzer;
48
import aQute.bnd.osgi.Clazz;
49
50
// Custom analyzer plugin example
51
public class CustomAnalyzerPlugin implements AnalyzerPlugin {
52
private String prefix = "custom";
53
54
@Override
55
public void setProperties(Map<String, String> map) {
56
prefix = map.getOrDefault("prefix", "custom");
57
}
58
59
@Override
60
public void setReporter(Reporter reporter) {
61
this.reporter = reporter;
62
}
63
64
@Override
65
public boolean analyzeJar(Analyzer analyzer) throws Exception {
66
// Custom analysis logic
67
for (Clazz clazz : analyzer.getClassspace().values()) {
68
if (clazz.getClassName().toString().startsWith(prefix)) {
69
// Mark for special processing
70
analyzer.setProperty("Custom-Classes",
71
analyzer.getProperty("Custom-Classes", "") + "," + clazz.getClassName());
72
}
73
}
74
return false; // Don't stop processing other plugins
75
}
76
}
77
78
// Register and use plugin
79
Analyzer analyzer = new Analyzer();
80
analyzer.getPlugins().add(new CustomAnalyzerPlugin());
81
analyzer.analyze();
82
```
83
84
### BuilderPlugin
85
86
Plugin interface for custom build processing and bundle manipulation.
87
88
```java { .api }
89
/**
90
* Plugin for custom build processing
91
*/
92
public interface BuilderPlugin extends Plugin {
93
/** Process builder before build */
94
public void processBuild(Builder builder) throws Exception;
95
96
/** Post-process built JAR */
97
public void postProcess(Builder builder, Jar jar) throws Exception;
98
99
/** Get plugin execution phase */
100
default Phase getPhase() { return Phase.BUILD; }
101
}
102
103
/**
104
* Build phases for plugin execution
105
*/
106
public enum Phase {
107
PRE_BUILD, // Before build starts
108
BUILD, // During build
109
POST_BUILD // After build completes
110
}
111
```
112
113
### LauncherPlugin
114
115
Plugin interface for custom OSGi framework launchers and runtime environments.
116
117
```java { .api }
118
/**
119
* Plugin for custom OSGi launchers
120
*/
121
public interface LauncherPlugin extends Plugin {
122
/** Launch OSGi framework with configuration */
123
public int launch(ProjectLauncher launcher) throws Exception;
124
125
/** Get launcher type identifier */
126
public String getType();
127
128
/** Check if launcher is available in current environment */
129
public boolean isAvailable();
130
131
/** Get launcher configuration */
132
public Map<String, String> getConfiguration();
133
}
134
135
/**
136
* Project launcher configuration
137
*/
138
public class ProjectLauncher {
139
/** Get project being launched */
140
public Project getProject();
141
142
/** Get run bundles */
143
public Collection<String> getRunBundles();
144
145
/** Get framework */
146
public String getRunFramework();
147
148
/** Get JVM arguments */
149
public List<String> getRunJVMArgs();
150
151
/** Get program arguments */
152
public List<String> getRunProgramArgs();
153
154
/** Get run properties */
155
public Map<String, String> getRunProperties();
156
}
157
```
158
159
### VerifierPlugin
160
161
Plugin interface for custom bundle verification and validation.
162
163
```java { .api }
164
/**
165
* Plugin for custom bundle verification
166
*/
167
public interface VerifierPlugin extends Plugin {
168
/** Verify bundle and report issues */
169
public void verify(Analyzer analyzer) throws Exception;
170
171
/** Get verification severity level */
172
default Severity getSeverity() { return Severity.ERROR; }
173
174
/** Check if verifier applies to bundle */
175
default boolean applies(Analyzer analyzer) { return true; }
176
}
177
178
/**
179
* Verification severity levels
180
*/
181
public enum Severity {
182
INFO, // Informational
183
WARNING, // Warning but not blocking
184
ERROR // Error that blocks build
185
}
186
```
187
188
**Usage Examples:**
189
190
```java
191
import aQute.bnd.service.VerifierPlugin;
192
import aQute.bnd.osgi.Analyzer;
193
194
// Custom verifier plugin example
195
public class BundleNamingVerifier implements VerifierPlugin {
196
private String requiredPrefix;
197
198
@Override
199
public void setProperties(Map<String, String> map) {
200
requiredPrefix = map.get("required.prefix");
201
}
202
203
@Override
204
public void setReporter(Reporter reporter) {
205
this.reporter = reporter;
206
}
207
208
@Override
209
public void verify(Analyzer analyzer) throws Exception {
210
String bsn = analyzer.getBsn();
211
if (requiredPrefix != null && !bsn.startsWith(requiredPrefix)) {
212
analyzer.error("Bundle symbolic name must start with: " + requiredPrefix);
213
}
214
215
// Verify version format
216
String version = analyzer.getProperty("Bundle-Version");
217
if (version != null && !isValidVersion(version)) {
218
analyzer.warning("Bundle version format may not be semantic: " + version);
219
}
220
}
221
222
private boolean isValidVersion(String version) {
223
return version.matches("\\d+\\.\\d+\\.\\d+(\\..+)?");
224
}
225
}
226
```
227
228
### Command Plugin
229
230
Plugin interface for adding custom commands to bnd tools.
231
232
```java { .api }
233
/**
234
* Plugin for custom bnd commands
235
*/
236
public interface CommandPlugin extends Plugin {
237
/** Execute command with arguments */
238
public void execute(String command, String[] args) throws Exception;
239
240
/** Get supported commands */
241
public String[] getCommands();
242
243
/** Get command help text */
244
public String getHelp(String command);
245
246
/** Get command usage syntax */
247
public String getUsage(String command);
248
}
249
```
250
251
### Generate Plugin
252
253
Plugin interface for generating additional resources during build.
254
255
```java { .api }
256
/**
257
* Plugin for generating additional resources
258
*/
259
public interface GeneratePlugin extends Plugin {
260
/** Generate resources for builder */
261
public void generate(Builder builder) throws Exception;
262
263
/** Get generator type */
264
public String getType();
265
266
/** Check if generator should run */
267
default boolean shouldGenerate(Builder builder) { return true; }
268
}
269
```
270
271
### Plugin Registry and Management
272
273
Classes for managing plugin registration and lifecycle.
274
275
```java { .api }
276
/**
277
* Registry for managing plugins
278
*/
279
public interface Registry {
280
/** Get plugins of specified type */
281
public <T extends Plugin> List<T> getPlugins(Class<T> type);
282
283
/** Add plugin to registry */
284
public void addPlugin(Plugin plugin);
285
286
/** Remove plugin from registry */
287
public void removePlugin(Plugin plugin);
288
289
/** Get plugin by name */
290
public Plugin getPlugin(String name);
291
292
/** List all registered plugins */
293
public List<Plugin> getPlugins();
294
}
295
296
/**
297
* Plugin manager for loading and configuring plugins
298
*/
299
public class PluginManager {
300
/** Load plugin from class name */
301
public static Plugin loadPlugin(String className, ClassLoader loader) throws Exception;
302
303
/** Configure plugin with properties */
304
public static void configurePlugin(Plugin plugin, Map<String, String> properties);
305
306
/** Validate plugin configuration */
307
public static List<String> validatePlugin(Plugin plugin);
308
309
/** Get plugin dependencies */
310
public static List<String> getPluginDependencies(Plugin plugin);
311
}
312
```
313
314
### Built-in Plugin Examples
315
316
Common built-in plugins that demonstrate plugin patterns.
317
318
```java { .api }
319
/**
320
* Spring XML processing plugin
321
*/
322
public class SpringXMLType implements AnalyzerPlugin {
323
public boolean analyzeJar(Analyzer analyzer) throws Exception;
324
}
325
326
/**
327
* JPA entity analysis plugin
328
*/
329
public class JPAComponent implements AnalyzerPlugin {
330
public boolean analyzeJar(Analyzer analyzer) throws Exception;
331
}
332
333
/**
334
* Maven dependency plugin
335
*/
336
public class MavenDependencyPlugin implements BuilderPlugin {
337
public void processBuild(Builder builder) throws Exception;
338
}
339
340
/**
341
* Felix launcher plugin
342
*/
343
public class FelixLauncher implements LauncherPlugin {
344
public int launch(ProjectLauncher launcher) throws Exception;
345
public String getType() { return "felix"; }
346
}
347
```
348
349
**Complete Plugin Development Example:**
350
351
```java
352
import aQute.bnd.service.*;
353
import aQute.bnd.osgi.*;
354
355
// Complete custom plugin example
356
public class ComprehensivePlugin implements AnalyzerPlugin, BuilderPlugin, VerifierPlugin {
357
358
private Reporter reporter;
359
private Map<String, String> properties = new HashMap<>();
360
361
@Override
362
public void setProperties(Map<String, String> map) {
363
this.properties.putAll(map);
364
}
365
366
@Override
367
public void setReporter(Reporter reporter) {
368
this.reporter = reporter;
369
}
370
371
// AnalyzerPlugin implementation
372
@Override
373
public boolean analyzeJar(Analyzer analyzer) throws Exception {
374
reporter.progress(0.1f, "Starting custom analysis");
375
376
// Analyze classes for custom annotations
377
for (Clazz clazz : analyzer.getClassspace().values()) {
378
if (hasCustomAnnotation(clazz)) {
379
String packageName = clazz.getClassName().getPackage();
380
analyzer.getContained().put(
381
analyzer.getPackageRef(packageName),
382
new Attrs()
383
);
384
}
385
}
386
387
reporter.progress(1.0f, "Custom analysis complete");
388
return false;
389
}
390
391
// BuilderPlugin implementation
392
@Override
393
public void processBuild(Builder builder) throws Exception {
394
// Add custom manifest headers
395
String customValue = properties.getOrDefault("custom.header", "default");
396
builder.setProperty("X-Custom-Header", customValue);
397
398
// Generate additional resources
399
generateCustomResources(builder);
400
}
401
402
@Override
403
public void postProcess(Builder builder, Jar jar) throws Exception {
404
// Post-process the built JAR
405
addCustomResources(jar);
406
}
407
408
// VerifierPlugin implementation
409
@Override
410
public void verify(Analyzer analyzer) throws Exception {
411
// Verify custom requirements
412
String requiredHeader = properties.get("required.header");
413
if (requiredHeader != null) {
414
String value = analyzer.getProperty(requiredHeader);
415
if (value == null) {
416
analyzer.error("Required header missing: " + requiredHeader);
417
}
418
}
419
420
// Verify custom package exports
421
verifyCustomExports(analyzer);
422
}
423
424
private boolean hasCustomAnnotation(Clazz clazz) throws Exception {
425
// Check for custom annotation
426
return clazz.annotations() != null &&
427
clazz.annotations().contains("Lcom/example/CustomAnnotation;");
428
}
429
430
private void generateCustomResources(Builder builder) throws Exception {
431
// Generate custom configuration files
432
String config = generateConfiguration();
433
builder.getJar().putResource("META-INF/custom-config.properties",
434
new EmbeddedResource(config, System.currentTimeMillis()));
435
}
436
437
private void addCustomResources(Jar jar) throws Exception {
438
// Add additional resources to final JAR
439
String readme = generateReadme();
440
jar.putResource("README.txt",
441
new EmbeddedResource(readme, System.currentTimeMillis()));
442
}
443
444
private void verifyCustomExports(Analyzer analyzer) throws Exception {
445
// Verify that API packages are properly exported
446
Packages exports = analyzer.getExports();
447
Packages contained = analyzer.getContained();
448
449
for (PackageRef pkg : contained.keySet()) {
450
if (pkg.getFQN().contains(".api.") && !exports.containsKey(pkg)) {
451
analyzer.warning("API package not exported: " + pkg.getFQN());
452
}
453
}
454
}
455
456
private String generateConfiguration() {
457
StringBuilder config = new StringBuilder();
458
config.append("# Generated configuration\n");
459
config.append("plugin.name=").append(getClass().getSimpleName()).append("\n");
460
config.append("plugin.version=1.0.0\n");
461
462
for (Map.Entry<String, String> entry : properties.entrySet()) {
463
config.append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
464
}
465
466
return config.toString();
467
}
468
469
private String generateReadme() {
470
return "This bundle was processed by " + getClass().getSimpleName() +
471
" plugin.\nGenerated at: " + new Date();
472
}
473
}
474
475
// Plugin usage in workspace/project
476
public class PluginUsageExample {
477
478
public void useCustomPlugin() throws Exception {
479
// In a workspace or project context
480
Workspace workspace = Workspace.getWorkspace(new File("."));
481
482
// Configure and add plugin
483
ComprehensivePlugin plugin = new ComprehensivePlugin();
484
Map<String, String> config = new HashMap<>();
485
config.put("custom.header", "MyCustomValue");
486
config.put("required.header", "Bundle-Category");
487
plugin.setProperties(config);
488
plugin.setReporter(workspace);
489
490
// Add to project
491
Project project = workspace.getProject("my.bundle");
492
project.addPlugin(plugin);
493
494
// Build with plugin
495
File[] built = project.build();
496
System.out.println("Built with custom plugin: " + Arrays.toString(built));
497
}
498
}
499
```