or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

api-comparison-diffing.mdcore-osgi-processing.mdheader-processing.mdindex.mdjar-resource-management.mdplugin-architecture.mdrepository-system.mdversion-management.mdworkspace-project-management.md

plugin-architecture.mddocs/

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

```