0
# Dependency Management
1
2
Grape dependency management system for dynamic dependency resolution and loading, including @Grab annotation support for declaring dependencies in scripts. This system allows Groovy scripts to automatically download and use external libraries at runtime.
3
4
## Capabilities
5
6
### Grape Core API
7
8
Main API for programmatic dependency management.
9
10
```java { .api }
11
class Grape {
12
/**
13
* Grabs a dependency using string notation.
14
*/
15
static void grab(String endorsedModule);
16
17
/**
18
* Grabs a dependency using map notation.
19
*/
20
static void grab(Map<String, Object> dependency);
21
22
/**
23
* Grabs multiple dependencies.
24
*/
25
static void grab(Map<String, Object>... dependencies);
26
27
/**
28
* Grabs dependencies with custom class loader.
29
*/
30
static void grab(ClassLoader loader, Map<String, Object>... dependencies);
31
32
/**
33
* Adds a repository resolver.
34
*/
35
static void addResolver(Map<String, Object> args);
36
37
/**
38
* Resolves dependencies to URIs without loading.
39
*/
40
static URI[] resolve(Map<String, Object> args);
41
42
/**
43
* Resolves dependencies to URIs with custom settings.
44
*/
45
static URI[] resolve(Map<String, Object> args, URI... uris);
46
47
/**
48
* Lists available dependencies in the grape cache.
49
*/
50
static Map<String, Map<String, List<String>>> enumerateGrapes();
51
52
/**
53
* Gets the instance of the grape engine.
54
*/
55
static GrapeEngine getInstance();
56
57
/**
58
* Sets the instance of the grape engine.
59
*/
60
static void setInstance(GrapeEngine engine);
61
}
62
```
63
64
### Grape Engine Interface
65
66
Interface for custom grape implementations.
67
68
```java { .api }
69
interface GrapeEngine {
70
/**
71
* Grabs a dependency.
72
*/
73
void grab(Map<String, Object> dependency);
74
75
/**
76
* Grabs dependencies with custom class loader.
77
*/
78
void grab(ClassLoader loader, Map<String, Object> dependency);
79
80
/**
81
* Adds a repository resolver.
82
*/
83
void addResolver(Map<String, Object> args);
84
85
/**
86
* Resolves dependencies to URIs.
87
*/
88
URI[] resolve(Map<String, Object> args);
89
90
/**
91
* Resolves dependencies with custom URIs.
92
*/
93
URI[] resolve(Map<String, Object> args, URI... uris);
94
95
/**
96
* Lists available dependencies.
97
*/
98
Map<String, Map<String, List<String>>> enumerateGrapes();
99
}
100
```
101
102
### Grab Annotations
103
104
Annotations for declarative dependency management in scripts.
105
106
```java { .api }
107
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
108
@Retention(RetentionPolicy.SOURCE)
109
@interface Grab {
110
/**
111
* The group ID of the dependency.
112
*/
113
String group() default "";
114
115
/**
116
* The module/artifact ID of the dependency.
117
*/
118
String module();
119
120
/**
121
* The version of the dependency.
122
*/
123
String version();
124
125
/**
126
* The classifier of the dependency.
127
*/
128
String classifier() default "";
129
130
/**
131
* The extension/type of the dependency.
132
*/
133
String ext() default "";
134
135
/**
136
* The configuration to use.
137
*/
138
String conf() default "";
139
140
/**
141
* Whether to force the version.
142
*/
143
boolean force() default false;
144
145
/**
146
* Whether to include transitive dependencies.
147
*/
148
boolean transitive() default true;
149
150
/**
151
* Whether to change the class loader.
152
*/
153
boolean changing() default false;
154
155
/**
156
* Modules to exclude from transitive dependencies.
157
*/
158
GrabExclude[] excludes() default {};
159
}
160
161
@Target({})
162
@Retention(RetentionPolicy.SOURCE)
163
@interface GrabExclude {
164
/**
165
* The group ID to exclude.
166
*/
167
String group() default "";
168
169
/**
170
* The module ID to exclude.
171
*/
172
String module() default "";
173
}
174
175
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
176
@Retention(RetentionPolicy.SOURCE)
177
@interface Grapes {
178
/**
179
* Array of @Grab annotations.
180
*/
181
Grab[] value();
182
}
183
184
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
185
@Retention(RetentionPolicy.SOURCE)
186
@interface GrabResolver {
187
/**
188
* The name of the repository.
189
*/
190
String name() default "";
191
192
/**
193
* The root URL of the repository.
194
*/
195
String root() default "";
196
197
/**
198
* Whether to use M2 compatible layout.
199
*/
200
boolean m2Compatible() default true;
201
}
202
203
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
204
@Retention(RetentionPolicy.SOURCE)
205
@interface GrabConfig {
206
/**
207
* System properties to set.
208
*/
209
String[] systemProperties() default {};
210
211
/**
212
* Whether to validate signatures.
213
*/
214
boolean disableChecksums() default false;
215
216
/**
217
* Whether to auto download sources.
218
*/
219
boolean autoDownload() default true;
220
}
221
```
222
223
### AST Transformation Support
224
225
AST transformations that process Grab annotations.
226
227
```java { .api }
228
class GrabAnnotationTransformation implements ASTTransformation {
229
/**
230
* Processes @Grab annotations during compilation.
231
*/
232
void visit(ASTNode[] nodes, SourceUnit source);
233
234
/**
235
* Extracts grab information from annotations.
236
*/
237
Map<String, Object> extractGrabInfo(AnnotationNode annotation);
238
239
/**
240
* Resolves and loads dependencies.
241
*/
242
void processGrabs(List<Map<String, Object>> grabs, ClassLoader loader);
243
}
244
245
class GrabResolverTransformation implements ASTTransformation {
246
/**
247
* Processes @GrabResolver annotations during compilation.
248
*/
249
void visit(ASTNode[] nodes, SourceUnit source);
250
}
251
```
252
253
## Usage Examples
254
255
### Basic Dependency Grabbing
256
257
```java
258
import groovy.grape.Grape;
259
260
// Grab a dependency programmatically using map notation
261
Map<String, Object> dependency = new HashMap<>();
262
dependency.put("group", "commons-lang");
263
dependency.put("module", "commons-lang");
264
dependency.put("version", "2.6");
265
266
Grape.grab(dependency);
267
268
// Now use the grabbed library
269
// This would work in a Groovy script context
270
// import org.apache.commons.lang.StringUtils;
271
// System.out.println(StringUtils.capitalize("hello world"));
272
273
// Grab using string notation (Groovy style)
274
// Grape.grab("commons-lang:commons-lang:2.6");
275
276
// Grab multiple dependencies at once
277
Map<String, Object> httpClient = new HashMap<>();
278
httpClient.put("group", "org.apache.httpcomponents");
279
httpClient.put("module", "httpclient");
280
httpClient.put("version", "4.5.13");
281
282
Map<String, Object> gson = new HashMap<>();
283
gson.put("group", "com.google.code.gson");
284
gson.put("module", "gson");
285
gson.put("version", "2.8.9");
286
287
Grape.grab(httpClient, gson);
288
```
289
290
### Using Grab Annotations in Scripts
291
292
```java
293
// In a Groovy script (.groovy file):
294
295
@Grab('commons-lang:commons-lang:2.6')
296
@Grab('com.google.code.gson:gson:2.8.9')
297
import org.apache.commons.lang.StringUtils
298
import com.google.gson.Gson
299
300
// Multiple grabs can be combined
301
@Grapes([
302
@Grab('org.apache.httpcomponents:httpclient:4.5.13'),
303
@Grab('org.apache.commons:commons-csv:1.9.0')
304
])
305
import org.apache.http.client.HttpClient
306
import org.apache.commons.csv.CSVFormat
307
308
// Script code using the grabbed dependencies
309
String text = "hello world"
310
String capitalized = StringUtils.capitalize(text)
311
println capitalized
312
313
Gson gson = new Gson()
314
String json = gson.toJson([name: "John", age: 30])
315
println json
316
```
317
318
### Advanced Dependency Configuration
319
320
```java
321
import groovy.grape.Grape;
322
import java.util.Map;
323
import java.util.HashMap;
324
325
// Grab with exclusions
326
Map<String, Object> springDep = new HashMap<>();
327
springDep.put("group", "org.springframework");
328
springDep.put("module", "spring-context");
329
springDep.put("version", "5.3.21");
330
springDep.put("transitive", true);
331
332
// Add exclusions for transitive dependencies
333
Map<String, Object> exclude1 = new HashMap<>();
334
exclude1.put("group", "commons-logging");
335
exclude1.put("module", "commons-logging");
336
337
springDep.put("excludes", new Map[]{exclude1});
338
339
Grape.grab(springDep);
340
341
// Grab with specific classifier
342
Map<String, Object> sourceDep = new HashMap<>();
343
sourceDep.put("group", "org.apache.commons");
344
sourceDep.put("module", "commons-lang3");
345
sourceDep.put("version", "3.12.0");
346
sourceDep.put("classifier", "sources");
347
348
Grape.grab(sourceDep);
349
350
// Force a specific version
351
Map<String, Object> forcedDep = new HashMap<>();
352
forcedDep.put("group", "junit");
353
forcedDep.put("module", "junit");
354
forcedDep.put("version", "4.13.2");
355
forcedDep.put("force", true);
356
357
Grape.grab(forcedDep);
358
```
359
360
### Custom Repository Configuration
361
362
```java
363
import groovy.grape.Grape;
364
365
// Add a custom Maven repository
366
Map<String, Object> customRepo = new HashMap<>();
367
customRepo.put("name", "spring-milestone");
368
customRepo.put("root", "https://repo.spring.io/milestone");
369
customRepo.put("m2Compatible", true);
370
371
Grape.addResolver(customRepo);
372
373
// Add a custom Ivy repository
374
Map<String, Object> ivyRepo = new HashMap<>();
375
ivyRepo.put("name", "my-ivy-repo");
376
ivyRepo.put("root", "http://my-company.com/ivy-repo");
377
ivyRepo.put("m2Compatible", false);
378
379
Grape.addResolver(ivyRepo);
380
381
// Now grab dependencies from custom repositories
382
Map<String, Object> springDep = new HashMap<>();
383
springDep.put("group", "org.springframework");
384
springDep.put("module", "spring-core");
385
springDep.put("version", "6.0.0-M5");
386
387
Grape.grab(springDep);
388
```
389
390
### Annotation-based Repository Configuration
391
392
```java
393
// In a Groovy script:
394
395
@GrabResolver(name='spring-milestones', root='https://repo.spring.io/milestone')
396
@GrabResolver(name='jcenter', root='https://jcenter.bintray.com/')
397
@Grab('org.springframework:spring-context:5.3.21')
398
@Grab(group='org.apache.commons', module='commons-lang3', version='3.12.0')
399
import org.springframework.context.ApplicationContext
400
import org.apache.commons.lang3.StringUtils
401
402
// Use the grabbed dependencies
403
println StringUtils.isBlank("") // true
404
println StringUtils.capitalize("hello") // Hello
405
```
406
407
### Dependency Resolution Without Loading
408
409
```java
410
import groovy.grape.Grape;
411
import java.net.URI;
412
413
// Resolve dependencies to get their file locations without loading
414
Map<String, Object> resolveArgs = new HashMap<>();
415
resolveArgs.put("group", "commons-lang");
416
resolveArgs.put("module", "commons-lang");
417
resolveArgs.put("version", "2.6");
418
419
URI[] resolvedUris = Grape.resolve(resolveArgs);
420
421
System.out.println("Resolved dependencies:");
422
for (URI uri : resolvedUris) {
423
System.out.println(" " + uri.toString());
424
}
425
426
// Resolve with multiple dependencies
427
Map<String, Object> dep1 = new HashMap<>();
428
dep1.put("group", "org.apache.commons");
429
dep1.put("module", "commons-csv");
430
dep1.put("version", "1.9.0");
431
432
Map<String, Object> dep2 = new HashMap<>();
433
dep2.put("group", "com.fasterxml.jackson.core");
434
dep2.put("module", "jackson-core");
435
dep2.put("version", "2.13.3");
436
437
Map<String, Object> resolveMultiple = new HashMap<>();
438
resolveMultiple.put("dependencies", new Map[]{dep1, dep2});
439
440
URI[] multipleUris = Grape.resolve(resolveMultiple);
441
System.out.println("Multiple resolved dependencies: " + multipleUris.length);
442
```
443
444
### Grape Cache Management
445
446
```java
447
import groovy.grape.Grape;
448
import java.util.Map;
449
450
// List all grapes in the cache
451
Map<String, Map<String, List<String>>> grapeCache = Grape.enumerateGrapes();
452
453
System.out.println("Grapes in cache:");
454
for (Map.Entry<String, Map<String, List<String>>> groupEntry : grapeCache.entrySet()) {
455
String group = groupEntry.getKey();
456
System.out.println("Group: " + group);
457
458
Map<String, List<String>> modules = groupEntry.getValue();
459
for (Map.Entry<String, List<String>> moduleEntry : modules.entrySet()) {
460
String module = moduleEntry.getKey();
461
List<String> versions = moduleEntry.getValue();
462
System.out.println(" Module: " + module);
463
System.out.println(" Versions: " + versions);
464
}
465
}
466
```
467
468
### Error Handling and Debugging
469
470
```java
471
import groovy.grape.Grape;
472
473
try {
474
// Attempt to grab a dependency that might not exist
475
Map<String, Object> badDep = new HashMap<>();
476
badDep.put("group", "com.nonexistent");
477
badDep.put("module", "fake-library");
478
badDep.put("version", "1.0.0");
479
480
Grape.grab(badDep);
481
482
} catch (Exception e) {
483
System.err.println("Failed to grab dependency: " + e.getMessage());
484
e.printStackTrace();
485
}
486
487
// Set system properties for debugging
488
System.setProperty("groovy.grape.report.downloads", "true");
489
System.setProperty("ivy.message.logger.level", "3"); // Verbose logging
490
491
// Grab with debugging enabled
492
Map<String, Object> debugDep = new HashMap<>();
493
debugDep.put("group", "org.slf4j");
494
debugDep.put("module", "slf4j-simple");
495
debugDep.put("version", "1.7.36");
496
497
Grape.grab(debugDep);
498
```
499
500
### Integration with Build Tools
501
502
```java
503
// Example of programmatic grape usage in a build script context
504
import groovy.grape.Grape;
505
506
// Configure grape for build environment
507
Map<String, Object> mavenCentral = new HashMap<>();
508
mavenCentral.put("name", "central");
509
mavenCentral.put("root", "https://repo1.maven.org/maven2/");
510
mavenCentral.put("m2Compatible", true);
511
512
Grape.addResolver(mavenCentral);
513
514
// Grab build-time dependencies
515
Map<String, Object> antDep = new HashMap<>();
516
antDep.put("group", "org.apache.ant");
517
antDep.put("module", "ant");
518
antDep.put("version", "1.10.12");
519
antDep.put("transitive", false);
520
521
Grape.grab(antDep);
522
523
// Now use Ant tasks programmatically
524
// This would be in a Groovy context where you could import and use Ant
525
```
526
527
### Dynamic Class Loading with Grabbed Dependencies
528
529
```java
530
import groovy.grape.Grape;
531
import groovy.lang.GroovyClassLoader;
532
533
// Create a custom class loader
534
GroovyClassLoader classLoader = new GroovyClassLoader();
535
536
// Grab dependencies into the custom class loader
537
Map<String, Object> commonsIo = new HashMap<>();
538
commonsIo.put("group", "commons-io");
539
commonsIo.put("module", "commons-io");
540
commonsIo.put("version", "2.11.0");
541
542
Grape.grab(classLoader, commonsIo);
543
544
// Now classes from commons-io should be available in the classLoader
545
// This enables isolation of dependencies per class loader
546
547
try {
548
Class<?> fileUtilsClass = classLoader.loadClass("org.apache.commons.io.FileUtils");
549
System.out.println("Successfully loaded: " + fileUtilsClass.getName());
550
551
// You could now use reflection to call methods on this class
552
// or compile and execute Groovy code that uses it
553
554
} catch (ClassNotFoundException e) {
555
System.err.println("Failed to load class: " + e.getMessage());
556
}
557
```