#Produces/provider media type matching - jersey

I am experimenting with api verioning and have a very peculiar requirement to work against. We are going to use content-negotiation i.e #Produces annotation for this and I want to a custom media type in a format like #Produces({"th/v1-v10+xml"}), where v1-v10 tells that this api will serve any request with Accept header of "th/v1+xml", "th/v2+xml" all the way to "th/v10+xml".
I know this is a bit strange, but the idea is that each drop we make in production will be a new version for the client, but not every service will be modified. So I want to annotate the service with a range so that I don’t have to duplicate it for every drop even if it’s not changed.
So what i want to find out is there any way I can intercept the login in Jersey while it matched the #Path and #Produces annotations? I know I can’t use regex to match media types.
.......
A bit more research tells me that the Jersey calls the MediaType.isCompatible(MediaType other) method to determine the compatibility between the requests accept header and the services provider media type.
Is may be able to leverage this a bit if I can create a custom MediaType and override the isCompatible method. Does Jersey allows such extension??
Any help is much appreciated.

You probabily should have to use a custom response mapper.
1.- Create a class implementing MessageBodyWriter in charge of writing the response
#Provider
public class MyResponseTypeMapper
implements MessageBodyWriter<MyResponseObjectType> {
#Override
public boolean isWriteable(final Class<?> type,final Type genericType,
final Annotation[] annotations,
final MediaType mediaType) {
... use one of the arguments (either the type, an annotation or the MediaType)
to guess if the object shoud be written with this class
}
#Override
public long getSize(final MyResponseObjectType myObjectTypeInstance,
final Class<?> type,final Type genericType,
final Annotation[] annotations,
final MediaType mediaType) {
// return the exact response length if you know it... -1 otherwise
return -1;
}
#Override
public void writeTo(final MyResponseObjectType myObjectTypeInstance,
final Class<?> type,final Type genericType,
final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String,Object> httpHeaders,
final OutputStream entityStream) throws IOException, WebApplicationException {
... serialize / marshall the MyResponseObjectType instance using
whatever you like (jaxb, etC)
entityStream.write(serializedObj.getBytes());
}
}
2.- Register the Mappers in your app
public class MyRESTApp
extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(MyResponseTypeMapper.class);
return s;
}
}
Jersey will scan all registered Mappers calling their isWriteable() method until one returns true... if so, this MessageBodyWriter instance will be used to serialize the content to the client

Related

How to link a Vaadin Grid with the result of Spring Mono WebClient data

This seems to be a missing part in the documentation of Vaadin...
I call an API to get data in my UI like this:
#Override
public URI getUri(String url, PageRequest page) {
return UriComponentsBuilder.fromUriString(url)
.queryParam("page", page.getPageNumber())
.queryParam("size", page.getPageSize())
.queryParam("sort", (page.getSort().isSorted() ? page.getSort() : ""))
.build()
.toUri();
}
#Override
public Mono<Page<SomeDto>> getDataByPage(PageRequest pageRequest) {
return webClient.get()
.uri(getUri(URL_API + "/page", pageRequest))
.retrieve()
.bodyToMono(new ParameterizedTypeReference<>() {
});
}
In the Vaadin documentation (https://vaadin.com/docs/v10/flow/binding-data/tutorial-flow-data-provider), I found an example with DataProvider.fromCallbacks but this expects streams and that doesn't feel like the correct approach as I need to block on the requests to get the streams...
DataProvider<SomeDto, Void> lazyProvider = DataProvider.fromCallbacks(
q -> service.getData(PageRequest.of(q.getOffset(), q.getLimit())).block().stream(),
q -> service.getDataCount().block().intValue()
);
When trying this implementation, I get the following error:
org.springframework.core.codec.CodecException: Type definition error: [simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
grid.setItems(lazyProvider);
I don't have experience with vaadin, so i'll talk about the deserialization problem.
Jackson needs a Creator when deserializing. That's either:
the default no-arg constructor
another constructor annotated with #JsonCreator
static factory method annotated with #JsonCreator
If we take a look at spring's implementations of Page - PageImpl and GeoPage, they have neither of those. So you have two options:
Write your custom deserializer and register it with the ObjectMapper instance
The deserializer:
public class PageDeserializer<T> extends StdDeserializer<Page<T>> {
public PageDeserializer() {
super(Page.class);
}
#Override
public Page<T> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
//TODO implement for your case
return null;
}
}
And registration:
SimpleModule module = new SimpleModule();
module.addDeserializer(Page.class, new PageDeserializer<>());
objectMapper.registerModule(module);
Make your own classes extending PageImpl, PageRequest, etc. and annotate their constructors with #JsonCreator and arguments with #JsonProperty.
Your page:
public class MyPage<T> extends PageImpl<T> {
#JsonCreator
public MyPage(#JsonProperty("content_prop_from_json") List<T> content, #JsonProperty("pageable_obj_from_json") MyPageable pageable, #JsonProperty("total_from_json") long total) {
super(content, pageable, total);
}
}
Your pageable:
public class MyPageable extends PageRequest {
#JsonCreator
public MyPageable(#JsonProperty("page_from_json") int page, #JsonProperty("size_from_json") int size, #JsonProperty("sort_object_from_json") Sort sort) {
super(page, size, sort);
}
}
Depending on your needs for Sort object, you might need to create MySort as well, or you can remove it from constructor and supply unsorted sort, for example, to the super constructor. If you are deserializing from input manually you need to provide type parameters like this:
JavaType javaType = TypeFactory.defaultInstance().constructParametricType(MyPage.class, MyModel.class);
Page<MyModel> deserialized = objectMapper.readValue(pageString, javaType);
If the input is from request body, for example, just declaring the generic type in the variable is enough for object mapper to pick it up.
#PostMapping("/deserialize")
public ResponseEntity<String> deserialize(#RequestBody MyPage<MyModel> page) {
return ResponseEntity.ok("OK");
}
Personally i would go for the second option, even though you have to create more classes, it spares the tediousness of extracting properties and creating instances manually when writing deserializers.
There are two parts to this question.
The first one is about asynchronously loading data for a DataProvider in Vaadin. This isn't supported since Vaadin has prioritized the typical case with fetching data straight through JDBC. This means that you end up blocking a thread while the data is loading. Vaadin 23 will add support for doing that blocking on a separate thread instead of keeping the UI thread blocked, but it will still be blocking.
The other half of your problem doesn't seem to be directly related to Vaadin. The exception message says that the Jackson instance used by the REST client isn't configured to support creating instances of org.springframework.data.domain.Page. I don't have direct experience with this part of the problem, so I cannot give any advice on exactly how to fix it.

How to write a custom convertor for recieving data annotated with #RequestBody

I'm trying to write a custom convertor for receiving data POSTed to a REST application. The object I want to populate already has its own builder that accepts a string JSON so I have to use that instead of the Jackson deserializer Spring would normally use.
I've tried a number of different things but I keep getting the following exception:
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class xxx.yyy.zzz.MyType]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.yyy.zzz.MyType` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (PushbackInputStream); line: 1, column: 1]
at at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:281) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:250) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
My convertor looks like:
public class MyConverter extends AbstractHttpMessageConverter<MyType> {
public MyConverter() {
super(/*MediaType.TEXT_PLAIN*/ MediaType.APPLICATION_JSON);
}
#Override
protected boolean supports(Class<?> type) {
return MyType.class.isAssignableFrom(type);
}
#Override
protected MyType readInternal(Class<? extends MyType> type, HttpInputMessage inputMessage) throws IOException {
String str = ..... read data from inputMessage
return MyType.build(str);
}
#Override
protected void writeInternal(MyType s, HttpOutputMessage outputMessage) {
}
}
and the controller is:
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody MyType data) {
System.out.println("add:" + data.toString());
}
Even if change the MediaType in the constructor for MyConverter to 'MediaType.ALL' it will still fail. Curiously if I change it to TEXT_PLAIN and POST with Content-Type set to 'text/plain' it works!
Answering my own question. The problem turned out to be the order the HttpMessageConverter objects are processed. The inbuilt converters were being processed, and failing, before my converter had chance.
This isn't the best code, but it works:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(#Nonnull List<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> temp = new ArrayList<>();
temp.add(new MyConverter());
temp.addAll(converters);
converters.clear();
converters.addAll(temp);
}
}
I can't quite believe this is the best answer for what, I think, should be a fairly standard problem. If somebody can suggest a better answer I'll happily accept that instead

How to cache an instrumented class with an instance forwarder?

The use case is to implement a dirty field tracker. For this I have an interface:
public interface Dirtyable {
String ID = "dirty";
Set<String> getDirty();
static <T> T wrap(final T delegate) {
return DirtyableInterceptor.wrap(delegate, ReflectionUtils::getPropertyName);
}
static <T> T wrap(final T delegate, final Function<Method, String> resolver) {
return DirtyableInterceptor.wrap(delegate, resolver);
}
}
In the interceptor class the wrapping method is:
static <T> T wrap(final T delegate, final Function<Method, String> resolver) {
requireNonNull(delegate, "Delegate must be non-null");
requireNonNull(resolver, "Resolver must be non-null");
final Try<Class<T>> delegateClassTry = Try.of(() -> getClassForType(delegate.getClass()));
return delegateClassTry.flatMapTry(delegateClass ->
dirtyableFor(delegate, delegateClass, resolver))
.mapTry(Class::newInstance)
.getOrElseThrow(t -> new IllegalStateException(
"Could not wrap dirtyable for " + delegate.getClass(), t));
}
The method dirtyableFor defines a ByteBuddy which forwards to a specific instance at each call. However, instrumenting at every invocation is a bit expensive so it caches the instrumented subclass from the given instance's class. For this I use the resilience4j library (a.k.a. javaslang-circuitbreaker).
private static <T> Try<Class<? extends T>> dirtyableFor(final T delegate,
final Class<T> clazz,
final Function<Method, String> resolver) {
long start = System.currentTimeMillis();
Try<Class<? extends T>> r = Try.of(() -> ofCheckedSupplier(() ->
new ByteBuddy().subclass(clazz)
.defineField(Dirtyable.ID, Set.class, Visibility.PRIVATE)
.method(nameMatches("getDirty"))
.intercept(reference(new HashSet<>()))
.implement(Dirtyable.class)
.method(not(isDeclaredBy(Object.class))
.and(not(isAbstract()))
.and(isPublic()))
.intercept(withDefaultConfiguration()
.withBinders(Pipe.Binder.install(Function.class))
.to(new DirtyableInterceptor(delegate, resolver)))
.make().load(clazz.getClassLoader())
.getLoaded())
.withCache(getCache())
.decorate()
.apply(clazz));
System.out.println("Instrumentation time: " + (System.currentTimeMillis() - start));
return r;
}
private static <T> Cache<Class<? super T>, Class<T>> getCache() {
final CachingProvider provider = Caching.getCachingProvider();
final CacheManager manager = provider.getCacheManager();
final javax.cache.Cache<Class<? super T>, Class<T>> cache =
manager.getCache(Dirtyable.ID);
final Cache<Class<? super T>, Class<T>> dirtyCache = Cache.of(cache);
dirtyCache.getEventStream().map(Object::toString).subscribe(logger::debug);
return dirtyCache;
}
From the logs, the intrumentation time drops from 70-100ms for a cache miss to 0-2ms for a cache hit.
For completeness here is the interceptor method:
#RuntimeType
#SuppressWarnings("unused")
public Object intercept(final #Origin Method method, final #This Dirtyable dirtyable,
final #Pipe Function<Object, Object> pipe) throws Throwable {
if (ReflectionUtils.isSetter(method)) {
final String property = resolver.apply(method);
dirtyable.getDirty().add(property);
logger.debug("Intercepted setter [{}], resolved property " +
"[{}] flagged as dirty.", method, property);
}
return pipe.apply(this.delegate);
}
This solution works well, except that the DirtyableInterceptor is always the same for cache hits, so the delegate instance is also the same.
Is it possible to bind a forwarder to a supplier of an instance so that intercepted methods would forward to it? How could this be done?
You can create a stateless interceptor by making your intercept method static. To access the object's state, define two fields on your subclass which you access using the #FieldValue annotations in your now static interceptor. Instead of using the FixedValue::reference instrumentation, you would also need to use the FieldAccessor implementation to read the value. You also need to define the fields using the defineField builder method.
You can set these fields either by:
Adding setter methods in your Dirtyable interface and intercepting them using the FieldAccessor implementation.
Defining an explicit constructor to which you supply the values. This also allows you to define the fields to be final. To implement the constructor, you first need to invoke a super constructor and then call the FieldAccessor several times to set the fields.
Doing so, you have created a fully stateless class that you can reuse but one that you need to initialze. Byte Buddy already offers a built-in TypeCache for easy reuse.

OData (Olingo) "inhibit" endpoint

My question is about what is best way to inhibit an endpoint that is automatically provided by Olingo?
I am playing with a simple app based on Spring boot and using Apache Olingo.On short, this is my servlet registration:
#Configuration
public class CxfServletUtil{
#Bean
public ServletRegistrationBean getODataServletRegistrationBean() {
ServletRegistrationBean odataServletRegistrationBean = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(), "/user.svc/*");
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
initParameters.put("org.apache.olingo.odata2.service.factory", "com.olingotest.core.CustomODataJPAServiceFactory");
odataServletRegistrationBean.setInitParameters(initParameters);
return odataServletRegistrationBean;
} ...
where my ODataJPAServiceFactory is
#Component
public class CustomODataJPAServiceFactory extends ODataJPAServiceFactory implements ApplicationContextAware {
private static ApplicationContext context;
private static final String PERSISTENCE_UNIT_NAME = "myPersistenceUnit";
private static final String ENTITY_MANAGER_FACTORY_ID = "entityManagerFactory";
#Override
public ODataJPAContext initializeODataJPAContext()
throws ODataJPARuntimeException {
ODataJPAContext oDataJPAContext = this.getODataJPAContext();
try {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean(ENTITY_MANAGER_FACTORY_ID);
oDataJPAContext.setEntityManagerFactory(emf);
oDataJPAContext.setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
return oDataJPAContext;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
...
My entity is quite simple ...
#Entity
public class User {
#Id
private String id;
#Basic
private String firstName;
#Basic
private String lastName;
....
Olingo is doing its job perfectly and it helps me with the generation of all the endpoints around CRUD operations for my entity.
My question is : how can I "inhibit" some of them? Let's say for example that I don't want to enable the delete my entity.
I could try to use a Filter - but this seems a bit harsh. Are there any other, better ways to solve my problem?
Thanks for the help.
As you have said, you could use a filter, but then you are really coupled with the URI schema used by Olingo. Also, things will become complicated when you have multiple, related entity sets (because you could navigate from one to the other, making the URIs more complex).
There are two things that you can do, depending on what you want to achieve:
If you want to have a fined grained control on what operations are allowed or not, you can create a wrapper for the ODataSingleProcesor and throw ODataExceptions where you want to disallow an operation. You can either always throw exceptions (i.e. completely disabling an operation type) or you can use the URI info parameters to obtain the target entity set and decide if you should throw an exception or call the standard single processor. I have used this approach to create a read-only OData service here (basically, I just created a ODAtaSingleProcessor which delegates some calls to the standard one + overridden a method in the service factory to wrap the standard single processor in my wrapper).
If you want to completely un-expose / ignore a given entity or some properties, then you can use a JPA-EDM mapping model end exclude the desired components. You can find an example of such a mapping here: github. The mapping model is just an XML file which maps the JPA entities / properties to EDM entity type / properties. In order for olingo to pick it up, you can pass the name of the file to the setJPAEdmMappingModel method of the ODataJPAContext in your initialize method.

Options for custom serializers / deserializers with Dropwizard?

What's a good way to have custom (de)serializers that can be registered externally with dropwizard?
I was having problems with (de)serializing a composite object. I tried using #JsonUnwrapped to get the JSON I wanted, but had problems with it for deserializing - it needs special constructors that take strings and requires the composite object to have knowledge on constructing the encapsulated objects. Also, I'd like a way of not having to use Jackson annotations on my value objects.
For example, I have:
public class SubmissionModule extends SimpleModule {
public SubmissionModule() {
addDeserializer(SubmissionDetails.class, new SubmissionDeserializer());
addSerializer(SubmissionDetails.class, new SubmissionSerializer());
}
public class SubmissionSerializer extends JsonSerializer<SubmissionDetails> {
#Override
public void serialize(SubmissionDetails value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("id", "" + value.getId());
jgen.writeStringField("title", value.getTitle());
jgen.writeStringField("abstract", value.getAbstract());
jgen.writeEndObject();
}
}
public class SubmissionDeserializer extends JsonDeserializer<SubmissionDetails> {
#Override
public SubmissionDetails deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
return aSubmissionWithId(SubmissionId.from(node.get("id").asText()))
.title(node.get("title").asText())
.abstract_(node.get("abstract").asText()).create();
}
}
}
which I've registered in DropWizard like so:
bootstrap.getObjectMapper().registerModule(new SubmissionModule());
but I can't figure out if it's possible to register the (de)serializers with the Jersey Client (or the client available when using ResourceTestRule).
Any ideas?
"but I can't figure out if it's possible to register the (de)serializers with the Jersey Client (or the client available when using ResourceTestRule). "
Check out the source code for ResourceTestRule. There's a method setMapper(ObjectMapper)
You can do something like
ObjectMapper mapper = Jackson.newObjectMapper();
mapper.registerModule(new SubmissionModule());
#ClassRule
public static final ResourceTestRule RULE
= new ResourceTestRule.Builder().setMapper(mapper).addResource(...).build();
I don't know where you get your information on #JsonUnwrapped, but it does not require special constructors or dependencies between containing and encapsulated object. Otherwise it would not really add much of use.
The annotation simply indicates that Object referred to should be written as a sequence of properties (in parent object), and not as Object value.
Not saying it will necessarily work for your use case, but you may have seen bad sample code or something.
As to avoiding annotations in value objects: one way to do that is to use "mix-in annotations" (http://www.studytrails.com/java/json/java-jackson-mix-in-annotation.jsp).
With that, you could use #JsonSerialize(using=MySerializer.class) and #JsonDeserialize(using=MyDeserializer.class) to indicate handlers to use.
Registering custom (de)serializers is handled by implementing a Module (usually just construct or sub-class SimpleModule), and registering that with ObjectMapper that DropWizard uses.

Resources