0
# Extensions
1
2
The JAX-RS extension APIs provide customization points for message body processing, parameter conversion, exception handling, and context resolution. These provider interfaces enable deep integration with JAX-RS runtime for serialization, custom parameter types, error handling, and dependency injection.
3
4
## Core Imports
5
6
```java
7
import javax.ws.rs.ext.Provider;
8
import javax.ws.rs.ext.MessageBodyReader;
9
import javax.ws.rs.ext.MessageBodyWriter;
10
import javax.ws.rs.ext.ReaderInterceptor;
11
import javax.ws.rs.ext.ReaderInterceptorContext;
12
import javax.ws.rs.ext.WriterInterceptor;
13
import javax.ws.rs.ext.WriterInterceptorContext;
14
import javax.ws.rs.ext.InterceptorContext;
15
16
import javax.ws.rs.ext.ExceptionMapper;
17
import javax.ws.rs.ext.ParamConverter;
18
import javax.ws.rs.ext.ParamConverterProvider;
19
import javax.ws.rs.ext.ContextResolver;
20
import javax.ws.rs.ext.Providers;
21
import javax.ws.rs.ext.RuntimeDelegate;
22
23
import javax.ws.rs.NameBinding;
24
import javax.ws.rs.Priorities;
25
import javax.annotation.Priority;
26
27
import javax.ws.rs.core.MediaType;
28
import javax.ws.rs.core.MultivaluedMap;
29
import javax.ws.rs.WebApplicationException;
30
31
import java.io.IOException;
32
import java.io.InputStream;
33
import java.io.OutputStream;
34
import java.lang.annotation.Annotation;
35
import java.lang.reflect.Type;
36
```
37
38
## Provider Registration
39
40
### @Provider Annotation
41
42
Marks an implementation of an extension interface for automatic discovery.
43
44
```java { .api }
45
@Target({ElementType.TYPE})
46
@Retention(RetentionPolicy.RUNTIME)
47
@Documented
48
public @interface Provider {
49
}
50
```
51
52
**Provider Registration Example:**
53
54
```java
55
// Automatic registration via @Provider
56
@Provider
57
public class CustomJsonProvider implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
58
// Implementation...
59
}
60
61
// Programmatic registration
62
public class MyApplication extends Application {
63
64
@Override
65
public Set<Class<?>> getClasses() {
66
return Set.of(
67
UserResource.class,
68
CustomJsonProvider.class,
69
CustomExceptionMapper.class
70
);
71
}
72
}
73
74
// Client-side registration
75
Client client = ClientBuilder.newClient()
76
.register(CustomJsonProvider.class)
77
.register(LoggingFilter.class);
78
```
79
80
### @NameBinding Annotation
81
82
Meta-annotation for creating name binding annotations that selectively bind filters and interceptors to specific resource methods or classes.
83
84
```java { .api }
85
@Target({ElementType.ANNOTATION_TYPE})
86
@Retention(RetentionPolicy.RUNTIME)
87
@Documented
88
public @interface NameBinding {
89
}
90
```
91
92
**Name Binding Usage Example:**
93
94
```java
95
// Create a custom name binding annotation
96
@NameBinding
97
@Target({ElementType.TYPE, ElementType.METHOD})
98
@Retention(RetentionPolicy.RUNTIME)
99
public @interface Authenticated {
100
}
101
102
// Use name binding on resource methods
103
@Path("/users")
104
public class UserResource {
105
106
@GET
107
@Path("/public")
108
public Response getPublicInfo() {
109
// No authentication required
110
return Response.ok("Public information").build();
111
}
112
113
@GET
114
@Path("/private")
115
@Authenticated // This method requires authentication
116
public Response getPrivateInfo() {
117
return Response.ok("Private information").build();
118
}
119
}
120
121
// Create a filter bound to the name binding
122
@Provider
123
@Authenticated // Only applies to methods/classes annotated with @Authenticated
124
@Priority(Priorities.AUTHENTICATION)
125
public class AuthenticationFilter implements ContainerRequestFilter {
126
127
@Override
128
public void filter(ContainerRequestContext requestContext) throws IOException {
129
String authHeader = requestContext.getHeaderString("Authorization");
130
if (authHeader == null || !isValidToken(authHeader)) {
131
requestContext.abortWith(
132
Response.status(Response.Status.UNAUTHORIZED).build()
133
);
134
}
135
}
136
137
private boolean isValidToken(String token) {
138
// Token validation logic
139
return token.startsWith("Bearer ") && token.length() > 7;
140
}
141
}
142
```
143
144
### @Priority and Priorities
145
146
Control the execution order of filters and interceptors.
147
148
```java { .api }
149
@Target({ElementType.TYPE, ElementType.PARAMETER})
150
@Retention(RetentionPolicy.RUNTIME)
151
@Documented
152
public @interface Priority {
153
int value();
154
}
155
156
public final class Priorities {
157
public static final int AUTHENTICATION = 1000;
158
public static final int AUTHORIZATION = 2000;
159
public static final int HEADER_DECORATOR = 3000;
160
public static final int ENTITY_CODER = 4000;
161
public static final int USER = 5000;
162
}
163
```
164
165
## Message Body Processing
166
167
### MessageBodyReader Interface
168
169
Converts input streams to Java objects.
170
171
```java { .api }
172
@Provider
173
public interface MessageBodyReader<T> {
174
175
boolean isReadable(Class<?> type, Type genericType,
176
Annotation[] annotations, MediaType mediaType);
177
178
T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
179
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
180
InputStream entityStream) throws IOException, WebApplicationException;
181
}
182
```
183
184
### MessageBodyWriter Interface
185
186
Converts Java objects to output streams.
187
188
```java { .api }
189
@Provider
190
public interface MessageBodyWriter<T> {
191
192
boolean isWriteable(Class<?> type, Type genericType,
193
Annotation[] annotations, MediaType mediaType);
194
195
long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations,
196
MediaType mediaType);
197
198
void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
199
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
200
OutputStream entityStream) throws IOException, WebApplicationException;
201
}
202
```
203
204
**Message Body Provider Examples:**
205
206
```java
207
// Custom JSON provider using Jackson
208
@Provider
209
@Consumes(MediaType.APPLICATION_JSON)
210
@Produces(MediaType.APPLICATION_JSON)
211
public class JacksonJsonProvider implements MessageBodyReader<Object>, MessageBodyWriter<Object> {
212
213
private final ObjectMapper objectMapper = new ObjectMapper();
214
215
@Override
216
public boolean isReadable(Class<?> type, Type genericType,
217
Annotation[] annotations, MediaType mediaType) {
218
return mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE);
219
}
220
221
@Override
222
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations,
223
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
224
InputStream entityStream) throws IOException {
225
226
try {
227
if (genericType != null) {
228
JavaType javaType = objectMapper.getTypeFactory().constructType(genericType);
229
return objectMapper.readValue(entityStream, javaType);
230
} else {
231
return objectMapper.readValue(entityStream, type);
232
}
233
} catch (JsonProcessingException e) {
234
throw new WebApplicationException("Invalid JSON", Response.Status.BAD_REQUEST);
235
}
236
}
237
238
@Override
239
public boolean isWriteable(Class<?> type, Type genericType,
240
Annotation[] annotations, MediaType mediaType) {
241
return mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE);
242
}
243
244
@Override
245
public long getSize(Object t, Class<?> type, Type genericType,
246
Annotation[] annotations, MediaType mediaType) {
247
return -1; // Unknown size
248
}
249
250
@Override
251
public void writeTo(Object t, Class<?> type, Type genericType, Annotation[] annotations,
252
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
253
OutputStream entityStream) throws IOException {
254
255
objectMapper.writeValue(entityStream, t);
256
}
257
}
258
259
// CSV provider for specific types
260
@Provider
261
@Consumes("text/csv")
262
@Produces("text/csv")
263
public class CsvProvider implements MessageBodyReader<List<User>>, MessageBodyWriter<List<User>> {
264
265
@Override
266
public boolean isReadable(Class<?> type, Type genericType,
267
Annotation[] annotations, MediaType mediaType) {
268
return List.class.isAssignableFrom(type) &&
269
genericType instanceof ParameterizedType &&
270
((ParameterizedType) genericType).getActualTypeArguments()[0] == User.class;
271
}
272
273
@Override
274
public List<User> readFrom(Class<List<User>> type, Type genericType,
275
Annotation[] annotations, MediaType mediaType,
276
MultivaluedMap<String, String> httpHeaders,
277
InputStream entityStream) throws IOException {
278
279
List<User> users = new ArrayList<>();
280
try (BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream))) {
281
String line;
282
boolean firstLine = true;
283
while ((line = reader.readLine()) != null) {
284
if (firstLine) {
285
firstLine = false; // Skip header
286
continue;
287
}
288
String[] parts = line.split(",");
289
users.add(new User(parts[0], parts[1]));
290
}
291
}
292
return users;
293
}
294
295
@Override
296
public boolean isWriteable(Class<?> type, Type genericType,
297
Annotation[] annotations, MediaType mediaType) {
298
return isReadable(type, genericType, annotations, mediaType);
299
}
300
301
@Override
302
public long getSize(List<User> users, Class<?> type, Type genericType,
303
Annotation[] annotations, MediaType mediaType) {
304
return -1;
305
}
306
307
@Override
308
public void writeTo(List<User> users, Class<?> type, Type genericType,
309
Annotation[] annotations, MediaType mediaType,
310
MultivaluedMap<String, Object> httpHeaders,
311
OutputStream entityStream) throws IOException {
312
313
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(entityStream))) {
314
writer.println("name,email"); // Header
315
for (User user : users) {
316
writer.printf("%s,%s%n", user.getName(), user.getEmail());
317
}
318
}
319
}
320
}
321
322
// Binary data provider
323
@Provider
324
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
325
@Produces(MediaType.APPLICATION_OCTET_STREAM)
326
public class BinaryDataProvider implements MessageBodyReader<byte[]>, MessageBodyWriter<byte[]> {
327
328
@Override
329
public boolean isReadable(Class<?> type, Type genericType,
330
Annotation[] annotations, MediaType mediaType) {
331
return byte[].class == type;
332
}
333
334
@Override
335
public byte[] readFrom(Class<byte[]> type, Type genericType, Annotation[] annotations,
336
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
337
InputStream entityStream) throws IOException {
338
339
return entityStream.readAllBytes();
340
}
341
342
@Override
343
public boolean isWriteable(Class<?> type, Type genericType,
344
Annotation[] annotations, MediaType mediaType) {
345
return byte[].class == type;
346
}
347
348
@Override
349
public long getSize(byte[] data, Class<?> type, Type genericType,
350
Annotation[] annotations, MediaType mediaType) {
351
return data.length;
352
}
353
354
@Override
355
public void writeTo(byte[] data, Class<?> type, Type genericType, Annotation[] annotations,
356
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
357
OutputStream entityStream) throws IOException {
358
359
entityStream.write(data);
360
}
361
}
362
```
363
364
## Message Body Interceptors
365
366
### InterceptorContext Interface
367
368
Base context for interceptor operations.
369
370
```java { .api }
371
public interface InterceptorContext {
372
373
Object getProperty(String name);
374
Collection<String> getPropertyNames();
375
void setProperty(String name, Object object);
376
void removeProperty(String name);
377
378
Annotation[] getAnnotations();
379
void setAnnotations(Annotation[] annotations);
380
381
Class<?> getType();
382
void setType(Class<?> type);
383
384
Type getGenericType();
385
void setGenericType(Type genericType);
386
387
MediaType getMediaType();
388
void setMediaType(MediaType mediaType);
389
}
390
```
391
392
### ReaderInterceptor and WriterInterceptor Interfaces
393
394
```java { .api }
395
@Provider
396
public interface ReaderInterceptor {
397
398
Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException;
399
}
400
401
public interface ReaderInterceptorContext extends InterceptorContext {
402
403
Object proceed() throws IOException, WebApplicationException;
404
InputStream getInputStream();
405
void setInputStream(InputStream is);
406
MultivaluedMap<String, String> getHeaders();
407
}
408
409
@Provider
410
public interface WriterInterceptor {
411
412
void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException;
413
}
414
415
public interface WriterInterceptorContext extends InterceptorContext {
416
417
void proceed() throws IOException, WebApplicationException;
418
Object getEntity();
419
void setEntity(Object entity);
420
OutputStream getOutputStream();
421
void setOutputStream(OutputStream os);
422
MultivaluedMap<String, Object> getHeaders();
423
}
424
```
425
426
**Interceptor Examples:**
427
428
```java
429
// Compression reader interceptor
430
@Provider
431
@Priority(Priorities.ENTITY_CODER)
432
public class GZipReaderInterceptor implements ReaderInterceptor {
433
434
@Override
435
public Object aroundReadFrom(ReaderInterceptorContext context)
436
throws IOException, WebApplicationException {
437
438
MultivaluedMap<String, String> headers = context.getHeaders();
439
String contentEncoding = headers.getFirst("Content-Encoding");
440
441
if ("gzip".equals(contentEncoding)) {
442
InputStream originalStream = context.getInputStream();
443
context.setInputStream(new GZIPInputStream(originalStream));
444
}
445
446
return context.proceed();
447
}
448
}
449
450
// Compression writer interceptor
451
@Provider
452
@Priority(Priorities.ENTITY_CODER)
453
public class GZipWriterInterceptor implements WriterInterceptor {
454
455
@Override
456
public void aroundWriteTo(WriterInterceptorContext context)
457
throws IOException, WebApplicationException {
458
459
MultivaluedMap<String, Object> headers = context.getHeaders();
460
String acceptEncoding = (String) headers.getFirst("Accept-Encoding");
461
462
if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
463
headers.putSingle("Content-Encoding", "gzip");
464
465
OutputStream originalStream = context.getOutputStream();
466
GZIPOutputStream gzipStream = new GZIPOutputStream(originalStream);
467
context.setOutputStream(gzipStream);
468
469
try {
470
context.proceed();
471
} finally {
472
gzipStream.finish();
473
}
474
} else {
475
context.proceed();
476
}
477
}
478
}
479
480
// Logging interceptor
481
@Provider
482
public class LoggingInterceptor implements ReaderInterceptor, WriterInterceptor {
483
484
private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
485
486
@Override
487
public Object aroundReadFrom(ReaderInterceptorContext context)
488
throws IOException, WebApplicationException {
489
490
logger.info("Reading entity of type: {} with media type: {}",
491
context.getType().getSimpleName(), context.getMediaType());
492
493
long startTime = System.currentTimeMillis();
494
try {
495
return context.proceed();
496
} finally {
497
long duration = System.currentTimeMillis() - startTime;
498
logger.info("Entity reading completed in {}ms", duration);
499
}
500
}
501
502
@Override
503
public void aroundWriteTo(WriterInterceptorContext context)
504
throws IOException, WebApplicationException {
505
506
logger.info("Writing entity of type: {} with media type: {}",
507
context.getType().getSimpleName(), context.getMediaType());
508
509
long startTime = System.currentTimeMillis();
510
try {
511
context.proceed();
512
} finally {
513
long duration = System.currentTimeMillis() - startTime;
514
logger.info("Entity writing completed in {}ms", duration);
515
}
516
}
517
}
518
```
519
520
## Parameter Conversion
521
522
### ParamConverter Interface
523
524
Converts string parameters to Java types.
525
526
```java { .api }
527
public interface ParamConverter<T> {
528
529
T fromString(String value);
530
String toString(T value);
531
}
532
```
533
534
### ParamConverterProvider Interface
535
536
Provider for creating ParamConverter instances.
537
538
```java { .api }
539
@Provider
540
public interface ParamConverterProvider {
541
542
<T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType,
543
Annotation[] annotations);
544
}
545
```
546
547
**Parameter Converter Examples:**
548
549
```java
550
// Date parameter converter
551
public class DateParamConverter implements ParamConverter<Date> {
552
553
private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
554
555
@Override
556
public Date fromString(String value) {
557
if (value == null || value.trim().isEmpty()) {
558
return null;
559
}
560
try {
561
LocalDateTime localDateTime = LocalDateTime.parse(value, formatter);
562
return Date.from(localDateTime.atZone(ZoneOffset.UTC).toInstant());
563
} catch (DateTimeParseException e) {
564
throw new IllegalArgumentException("Invalid date format: " + value, e);
565
}
566
}
567
568
@Override
569
public String toString(Date value) {
570
if (value == null) {
571
return null;
572
}
573
LocalDateTime localDateTime = value.toInstant()
574
.atZone(ZoneOffset.UTC)
575
.toLocalDateTime();
576
return formatter.format(localDateTime);
577
}
578
}
579
580
// UUID parameter converter
581
public class UUIDParamConverter implements ParamConverter<UUID> {
582
583
@Override
584
public UUID fromString(String value) {
585
if (value == null || value.trim().isEmpty()) {
586
return null;
587
}
588
try {
589
return UUID.fromString(value);
590
} catch (IllegalArgumentException e) {
591
throw new IllegalArgumentException("Invalid UUID format: " + value, e);
592
}
593
}
594
595
@Override
596
public String toString(UUID value) {
597
return value != null ? value.toString() : null;
598
}
599
}
600
601
// Parameter converter provider
602
@Provider
603
public class CustomParamConverterProvider implements ParamConverterProvider {
604
605
@Override
606
@SuppressWarnings("unchecked")
607
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType,
608
Annotation[] annotations) {
609
610
if (rawType == Date.class) {
611
return (ParamConverter<T>) new DateParamConverter();
612
}
613
614
if (rawType == UUID.class) {
615
return (ParamConverter<T>) new UUIDParamConverter();
616
}
617
618
// Custom enum converter
619
if (rawType.isEnum()) {
620
return (ParamConverter<T>) new EnumParamConverter<>((Class<Enum>) rawType);
621
}
622
623
return null; // No converter available
624
}
625
}
626
627
// Generic enum converter
628
public class EnumParamConverter<T extends Enum<T>> implements ParamConverter<T> {
629
630
private final Class<T> enumClass;
631
632
public EnumParamConverter(Class<T> enumClass) {
633
this.enumClass = enumClass;
634
}
635
636
@Override
637
public T fromString(String value) {
638
if (value == null || value.trim().isEmpty()) {
639
return null;
640
}
641
try {
642
return Enum.valueOf(enumClass, value.toUpperCase());
643
} catch (IllegalArgumentException e) {
644
throw new IllegalArgumentException(
645
"Invalid " + enumClass.getSimpleName() + " value: " + value, e);
646
}
647
}
648
649
@Override
650
public String toString(T value) {
651
return value != null ? value.name() : null;
652
}
653
}
654
655
// Usage in resource
656
@Path("/examples")
657
public class ParamConverterExamples {
658
659
@GET
660
@Path("/events")
661
public List<Event> getEvents(@QueryParam("start") Date startDate,
662
@QueryParam("end") Date endDate,
663
@QueryParam("status") EventStatus status,
664
@QueryParam("id") UUID eventId) {
665
666
return eventService.findEvents(startDate, endDate, status, eventId);
667
}
668
}
669
```
670
671
## Exception Handling
672
673
### ExceptionMapper Interface
674
675
Maps Java exceptions to HTTP responses.
676
677
```java { .api }
678
@Provider
679
public interface ExceptionMapper<E extends Throwable> {
680
681
Response toResponse(E exception);
682
}
683
```
684
685
**Exception Mapper Examples:**
686
687
```java
688
// Validation exception mapper
689
@Provider
690
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
691
692
@Override
693
public Response toResponse(ValidationException exception) {
694
695
ErrorResponse error = new ErrorResponse(
696
"VALIDATION_ERROR",
697
"Request validation failed",
698
exception.getErrors()
699
);
700
701
return Response.status(Response.Status.BAD_REQUEST)
702
.entity(error)
703
.type(MediaType.APPLICATION_JSON)
704
.build();
705
}
706
}
707
708
// Generic throwable mapper
709
@Provider
710
public class GeneralExceptionMapper implements ExceptionMapper<Throwable> {
711
712
private static final Logger logger = LoggerFactory.getLogger(GeneralExceptionMapper.class);
713
714
@Override
715
public Response toResponse(Throwable exception) {
716
717
// Log the exception
718
logger.error("Unhandled exception", exception);
719
720
// Don't expose internal details in production
721
ErrorResponse error = new ErrorResponse(
722
"INTERNAL_ERROR",
723
"An internal server error occurred"
724
);
725
726
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
727
.entity(error)
728
.type(MediaType.APPLICATION_JSON)
729
.build();
730
}
731
}
732
733
// Business exception mapper
734
@Provider
735
public class BusinessExceptionMapper implements ExceptionMapper<BusinessException> {
736
737
@Override
738
public Response toResponse(BusinessException exception) {
739
740
Response.Status status = mapToHttpStatus(exception.getErrorCode());
741
742
ErrorResponse error = new ErrorResponse(
743
exception.getErrorCode(),
744
exception.getMessage(),
745
exception.getDetails()
746
);
747
748
return Response.status(status)
749
.entity(error)
750
.type(MediaType.APPLICATION_JSON)
751
.build();
752
}
753
754
private Response.Status mapToHttpStatus(String errorCode) {
755
switch (errorCode) {
756
case "NOT_FOUND":
757
return Response.Status.NOT_FOUND;
758
case "UNAUTHORIZED":
759
return Response.Status.UNAUTHORIZED;
760
case "FORBIDDEN":
761
return Response.Status.FORBIDDEN;
762
case "CONFLICT":
763
return Response.Status.CONFLICT;
764
default:
765
return Response.Status.BAD_REQUEST;
766
}
767
}
768
}
769
770
// Security exception mapper
771
@Provider
772
public class SecurityExceptionMapper implements ExceptionMapper<SecurityException> {
773
774
@Override
775
public Response toResponse(SecurityException exception) {
776
777
if (exception instanceof AuthenticationException) {
778
return Response.status(Response.Status.UNAUTHORIZED)
779
.header("WWW-Authenticate", "Bearer")
780
.entity("Authentication required")
781
.build();
782
}
783
784
if (exception instanceof AuthorizationException) {
785
return Response.status(Response.Status.FORBIDDEN)
786
.entity("Insufficient privileges")
787
.build();
788
}
789
790
return Response.status(Response.Status.FORBIDDEN)
791
.entity("Access denied")
792
.build();
793
}
794
}
795
796
// Error response model
797
public class ErrorResponse {
798
799
private String code;
800
private String message;
801
private List<String> details;
802
private long timestamp;
803
804
public ErrorResponse(String code, String message) {
805
this(code, message, null);
806
}
807
808
public ErrorResponse(String code, String message, List<String> details) {
809
this.code = code;
810
this.message = message;
811
this.details = details;
812
this.timestamp = System.currentTimeMillis();
813
}
814
815
// Getters and setters...
816
}
817
```
818
819
## Context Resolution
820
821
### ContextResolver Interface
822
823
Provides context information to resource classes and providers.
824
825
```java { .api }
826
@Provider
827
public interface ContextResolver<T> {
828
829
T getContext(Class<?> type);
830
}
831
```
832
833
**Context Resolver Examples:**
834
835
```java
836
// ObjectMapper context resolver for Jackson
837
@Provider
838
@Produces(MediaType.APPLICATION_JSON)
839
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
840
841
private final ObjectMapper objectMapper;
842
843
public ObjectMapperContextResolver() {
844
this.objectMapper = new ObjectMapper();
845
846
// Configure mapper
847
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
848
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
849
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"));
850
851
// Register modules
852
objectMapper.registerModule(new JavaTimeModule());
853
}
854
855
@Override
856
public ObjectMapper getContext(Class<?> type) {
857
return objectMapper;
858
}
859
}
860
861
// Database context resolver
862
@Provider
863
public class DatabaseContextResolver implements ContextResolver<EntityManager> {
864
865
@PersistenceContext
866
private EntityManager entityManager;
867
868
@Override
869
public EntityManager getContext(Class<?> type) {
870
return entityManager;
871
}
872
}
873
874
// Configuration context resolver
875
@Provider
876
public class ConfigurationContextResolver implements ContextResolver<AppConfig> {
877
878
private final AppConfig appConfig;
879
880
public ConfigurationContextResolver() {
881
this.appConfig = loadConfiguration();
882
}
883
884
@Override
885
public AppConfig getContext(Class<?> type) {
886
return appConfig;
887
}
888
889
private AppConfig loadConfiguration() {
890
// Load configuration from properties, environment, etc.
891
return new AppConfig();
892
}
893
}
894
895
// Usage in resource or provider
896
@Path("/configured")
897
public class ConfiguredResource {
898
899
@Context
900
private Providers providers;
901
902
@GET
903
public Response getConfiguredResponse() {
904
905
// Get context resolver
906
ContextResolver<AppConfig> resolver =
907
providers.getContextResolver(AppConfig.class, MediaType.APPLICATION_JSON_TYPE);
908
909
if (resolver != null) {
910
AppConfig config = resolver.getContext(AppConfig.class);
911
return Response.ok(config.getSomeValue()).build();
912
}
913
914
return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
915
}
916
}
917
```
918
919
## Provider Access
920
921
### Providers Interface
922
923
Injectable interface for accessing provider instances.
924
925
```java { .api }
926
public interface Providers {
927
928
<T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType,
929
Annotation[] annotations,
930
MediaType mediaType);
931
932
<T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType,
933
Annotation[] annotations,
934
MediaType mediaType);
935
936
<T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> type);
937
938
<T> ContextResolver<T> getContextResolver(Class<T> contextType,
939
MediaType mediaType);
940
}
941
```
942
943
## Runtime Delegate
944
945
### RuntimeDelegate Class
946
947
Central class for JAX-RS runtime integration.
948
949
```java { .api }
950
public abstract class RuntimeDelegate {
951
952
public static RuntimeDelegate getInstance();
953
public static void setInstance(RuntimeDelegate rd);
954
955
public abstract UriBuilder createUriBuilder();
956
public abstract Response.ResponseBuilder createResponseBuilder();
957
public abstract Variant.VariantListBuilder createVariantListBuilder();
958
public abstract <T> T createEndpoint(Application application, Class<T> endpointType)
959
throws IllegalArgumentException, UnsupportedOperationException;
960
961
public abstract <T> HeaderDelegate<T> createHeaderDelegate(Class<T> type);
962
963
public static abstract class HeaderDelegate<T> {
964
public abstract T fromString(String value);
965
public abstract String toString(T value);
966
}
967
}
968
```
969
970
**Provider Usage Examples:**
971
972
```java
973
@Path("/provider-usage")
974
public class ProviderUsageExample {
975
976
@Context
977
private Providers providers;
978
979
@POST
980
@Path("/transform")
981
public Response transformData(@Context HttpHeaders headers,
982
InputStream inputStream) throws IOException {
983
984
MediaType inputType = headers.getMediaType();
985
986
// Get appropriate reader
987
MessageBodyReader<Object> reader = providers.getMessageBodyReader(
988
Object.class, Object.class, new Annotation[0], inputType);
989
990
if (reader == null) {
991
return Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).build();
992
}
993
994
// Read input
995
Object data = reader.readFrom(Object.class, Object.class, new Annotation[0],
996
inputType, headers.getRequestHeaders(), inputStream);
997
998
// Transform data
999
Object transformed = transformationService.transform(data);
1000
1001
// Get appropriate writer for JSON output
1002
MessageBodyWriter<Object> writer = providers.getMessageBodyWriter(
1003
Object.class, Object.class, new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
1004
1005
if (writer == null) {
1006
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
1007
}
1008
1009
return Response.ok(transformed, MediaType.APPLICATION_JSON).build();
1010
}
1011
}
1012
```