Dynamic execution of the methods of bean object - spring

Currently i have one requirment our backend spring rest api will receive the data in encrypted json format (few fields are encrypted and few fields are plain text)
and then applies the decryption ,then applies some business logic on the data ,finally stores data into database.
This decryption logic is repeatining muliple service implementations methods .
So we desided to isolate the decryption logic from actual business logic.
i am using spring aop to decrypting the data and after decrypting same
object i am passing to service layer methods.
But my service layer methods contains the different types of objects as arguments
Ex :
processEmployee(EmployeeRequest request)
procesStudent(StudentRequest request)
I was looking for a way how can i dynamically change
the data on the same object fields it self ( Ex: EmployeeRequest,StudentRequest )
The following approach i have tried and stuck with 4th step.
1.Introduced a new annotation.
2.Annotate those fields which are having the encrypted data.
3.Retrieve all the annotated fields.
4.Each field data we will apply the decryption logic and
decrypted data will be injected on the same field again
i was looking for the api to achieve 4th step ?
Is there any api available to dynamically execute
the methods on the same object
or any reference please point me .

My suggestion is to not use the same POJO for both encrypted / decrypted class. It make confusing for future usage (as if I received an EmployeeRequest instance, is it decrypted or not?), and also limit the type (as your encrypted/decrypted data must be the same type).
Now, for the implementation, you have two choices:
Using explicit ConversionService
Register a converter:
#Component
public class EmployeeRequestConverter implements Converter<EmployeeRequest, EmployeeRequest> {
#Override
public EmployeeRequest convert(EmployeeRequest source) {
// Apply your decryption logic
}
}
Make the similar converters for other request objects.
Now in your controller:
public class MyController {
private ConversionService conversionService;
private MyService myService;
#RequestMapping(...)
public void aRequest(#RequestBody EmployeeRequest request) {
myService.execute(conversionService.convert(request, EmployeeRequest.class));
}
}
Using reflection.
Precondition: You have #Encrypted annotation on encrypted fields.
Unlike the first solution, you do not create explicit converter for each type of request.
#Service
public class DecryptionService {
public void <T> T decrypt(T input) {
Field[] fields = input.getClass().getDeclaredFields();
for (Field field : fields) {
Encrypted encrypted = field.getAnnotation(Encrypted.class);
if (encrypted != null) {
try{
field.setAccessible(true);
Object val = field.get(input);
// Base on #Encrypted annotation in your val, do the decryption
Object decryptedVal = ...;
field.set(input, decryptedVal);
} catch (Exception ex) {
}
}
}
}
}
Now you can apply this service for your controllers.
You might want to cache the Class.getDeclaredFields() and the mapping between Class<?> -> #Encrypted fields for performance.

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.

Providing default values for validation in SpringBoot

I want SpringBoot to be able to provide default values for fields that the user must enter. For example, I have something like this:
*Controller class*
#PostMapping("/test")
public ResponseEntity<> myMethod(#RequestBody #Valid MyContract contract) {}
*MyContract class*
#Valid
DataObject dataObject;
*DataObject class*
#Component
public class DataObject {
private #Value("${field1.default}") String field1Default;
private String field1
public String getField1() {
return (field1 == null ? field1Default : field1);
}
}
The DataObject class needs to be created on a per request basis. There are also other places in the code where it needs to be created on demand. So I imagine it needs to be a Prototype object. But I can't figure out how to get Spring to created it properly when it creates it for the request.
Update
I have read more about #RequstBody, e.g., https://www.javadevjournal.com/spring/spring-request-response-body/ and Should spring #RequestBody class be singleton or prototype?, which explains that the object is not a Component, but a simple POJO that gets the values from the Json request. So it seems that there is no way to inject #Values from the Spring application.properties file. Is there any other way around this? Or another suggested implementation?

How to validate request parameters on feign client

Is there a way to add validation to feign clients on the request parameters.
For example:
#FeignClient
public interface ZipCodeClient {
#GetMapping("/zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#PathVariable("zipCode") String zipCode);
}
It would be nice to verify that zipcode is not empty and is of certain length etc, before sending the HTTP call to the server.
If your validations are simple, apply to only headers and query string parameters, you can use a RequestInterceptor for this, as it provides you the opportunity to review the RequestTemplate before it is sent to the Client.
public class ValidatingRequestInterceptor implements RequestInterceptor {
public void apply(RequestTemplate requestTemplate) {
// use the methods on the request template to check the query and values.
// throw an exception if the request is not valid.
}
}
If you need to validate the request body, you can use a custom Encoder
public class ValidatingEncoder implements Encoder {
public void encode(Object object, Type type, RequestTemplate template) {
// validate the object
// throw an exception if the request is not valid.
}
}
Lastly, if you want to validate individual parameters, you can provide a custom Expander for the parameter and validate it there. You can look at this answer for a complete explanation on how to create a custom expander that can work with Spring Cloud.
How to custom #FeignClient Expander to convert param?
For completeness, I've included an example for how to do this with vanilla Feign.
public class ZipCodeExpander implements Expander {
public String expand(Object value) {
// validate the object
// throw an exception if the request is not valid.
}
}
public interface ZipCodeClient {
#RequestLine("GET /zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#Param(expander = ZipCodeExpander.class) ("zipCode") String zipCode);
}
As pointed out in this comment, a solution using the Bean Validation API would be nice. And indeed, I found in a Spring Boot project that merely placing #org.springframework.validation.annotation.Validated on the interface is sufficient for enabling Bean Validation.
So for example:
#FeignClient
#Validated
public interface ZipCodeClient {
#GetMapping("/zipcodes/{zipCode}")
Optional<ZipCodeView> findByZipCode(#PathVariable("zipCode") #NotEmpty String zipCode);
}
triggering a ConstraintViolationException in the case of violations.
Any standard Bean Validation feature should work here.
UDPATE Note that there seems to be a potential issue with this solution that might require setting a Hibernate Validator configuration property like this: hibernate.validator.allow_parallel_method_parameter_constraint=true

How to get certain fields from spring boot health endpoint

I have successfully created a springboot app that returns all the basic endpoints. Now I want to return just few fields from that endpoint in my request. For instance, return status from /health page to my rest call. How do I filter this or make my rest call more specific?
The actual requirement is two return few fields from /env, /health of different apps in one call. For which I am able to do it by returning all fields for both env and health. I just need to return specific fields from them. Also can I use in memory json objects, if so how should I do it?
Finally I figured out as how to create it. So the incoming json object consists of fields in LinkedHashMap type. So I consumed its field values using key
LinkedHashMap response = (LinkedHashMap)restTemplate.getForObject("http://localhost:8080/env",Object.class);
EnvProperties variables = new EnvProperties (response);
Wrapper POJO for all fields,
public EnvProperties (LinkedHashMap body) {
this.sysProperties = new SysEnvProperties((LinkedHashMap) body.get("systemProperties"));
}
POJO for this field,
public SysEnvProperties(LinkedHashMap body) {
this.javaVersion = body.get("java.version").toString();
}
later creating a new json string
#Override
public String toString() {
String s = null;
try {
s = mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return s;
}
I repeated the same for fields of interest, creating a POJO for each. Finally called these fields using similar wrapper class whose toString method returned the expected json object of desired fields only.
You can create Custom health endpoint or custom heath checker too.
For e.g.
#Component
public class CustomHealthCheck extends AbstractHealthIndicator {
#Override
protected void doHealthCheck(Health.Builder bldr) throws Exception {
// TODO implement some check
boolean running = true;
if (running) {
bldr.up();
} else {
bldr.down();
}
}
}
For further reading:
http://www.christianmenz.ch/programmieren/spring-boot-health-checks/
http://briansjavablog.blogspot.be/2017/09/health-checks-metric-s-more-with-spring.html
http://www.baeldung.com/spring-boot-actuators
You can find a tutorial here. However, the interfaces you want to look into implementing are:
org.springframework.boot.actuate.endpoint.Endpoint
Similar to creating a Controller. This is your /custom-health endpoint.
org.springframework.boot.actuate.metrics.CounterService
You can count integer-value metrics which will be available at /metrics.
org.springframework.boot.actuate.metrics.GaugeService
Or, you can measure double-value metrics which will be available at /metrics.
org.springframework.boot.actuate.health.HealthIndicator
Add metrics to the /health endpoint.

GWT : SerializationException

I'm trying to use GWT + Spring + Hibernate
When lunshing the application I get this error:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.hibernate.collection.PersistentBag' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = [com.asso.shared.model.Activite#64d6357a]
after using this method with the lists of the persistence classes:
public static <T> ArrayList<T> makeGWTSafe(List<T> list) {
if(list instanceof ArrayList) {
return (ArrayList<T>)list;
} else {
ArrayList<T> newList = new ArrayList<T>();
newList.addAll(list);
return newList;
}
}
with my lists I got this:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.hibernate.collection.PersistentBag' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = [com.asso.shared.model.Personne#75a2fb58]
==========================================
I have searched in the other subjects but I can't find any solution!
How can I solve this serialization thing!?
I'm using List in my Persistence classes
You need to send DTO object to client side (instead of original one backed by Hibernate). The problem is that your Personne object is actually a Hibernate proxy. Each time when you call some method on it Hibernate do some work (fetch collections from DB for example). There is no simple way to serialize such kind of objects.
Hibernate entities:
//Hibernate entity
public class Personne {
private String name;
private List<Address> addresses;
}
//Hibernate entity
public class Address {
}
Corresponding DTO objects:
public class PersonneDto {
private String name;
private List<AddressDto> addresses;
}
public class AddressDto {
}
Instead of sending Personne to client side you need to create new PersonneDto object, copy state to it and then send to UI. Personne cannot be used in client side because Personne.getAddresses() in most cases hit DB to fetch data (which is inpossible to do in client side JS). So each Personne must be replaced by PersonneDto on client side. As a downside you need to mantnain additional layaer of DTO objects and corresponding code to transform entities to DTOs. There are another approaches to this problem. See this article for more details.

Resources