spring boot UUID converter - spring-boot

Spring boot version:2.4.0
Converter class
import java.util.UUID;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
#Component
public class StringToUuidConverter implements Converter<String, UUID> {
#Override
public UUID convert(#NonNull String uuid) {
return UUID.fromString(uuid);
}
}
controller
....
#Autowired
TestRepos testRepos;
#RequestMapping("test")
public Task t( UUID uuid) {
return this.testRepos.findById(uuid).orElse(null);
}
....
call the controller with
http://localhost:9090/test?uuid=0459828a-a630-491d-80b9-ab9a412f066e
error log
"error": "Bad Request",
"trace": "org.springframework.web.method.annotation.ModelAttributeMethodProcessor$1: org.springframework.validation.BeanPropertyBindingResult: 2 errors\nField error in object 'UUID' on field 'mostSigBits': rejected value [null]; codes [typeMismatch.UUID.mostSigBits,typeMismatch.mostSigBits,typeMismatch.long,typeMismatch];
Any config missed?

Try it:
public Task t(#RequestParam UUID uuid) {
// ...
}
1.3.3. Handler Methods
Any other argument:
If a method argument is not matched to any of the earlier values in this table and it is a simple type (as determined by BeanUtils#isSimpleProperty, it is a resolved as a #RequestParam. Otherwise, it is resolved as a #ModelAttribute.
In addition, you don't need create StringToUuidConverter class yourself.
It has been already included.

Related

How to make enum parameter lowercase in SpringBoot/Swagger?

I have the following endpoint
#GetMapping(value = "/mypath/{mychoice}")
public ResponseClass generateEndpoint(
#PathVariable("mychoice") FormatEnum format,
) {
...
and the following enum annotated with Jackson
#JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class)
public enum Format {
AVRO,
ORC,
PARQUET,
PROTOBUF
}
I hoped, #JsonNaming annotation will tell swagger to display cases in lowercase, but it doesn't
Adding #JsonProperty to each case also doesn't help.
It also doesn't accept lowercase URL with error
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'FormatEnum'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#org.springframework.web.bind.annotation.RequestParam FormatEnum] for value 'avro'; nested exception is java.lang.IllegalArgumentException: No enum constant FormatEnum.avro
Setting
spring.jackson.mapper.ACCEPT_CASE_INSENSITIVE_ENUMS = true
has no effect (and is true in code).
Looks like it just doesn't use Jackson to deserialize enum!
Answering the part for serializing Enum case insensitive
The reason why setting spring.jackson.mapper.ACCEPT_CASE_INSENSITIVE_ENUMS=true is not working can be found in Konstantin Zyubin's answer.
And inspired from above answer, we can have a more generic approach to handle case insensitive enums in request parameter.
Converter class
import org.springframework.core.convert.converter.Converter;
public class CaseInsensitiveEnumConverter<T extends Enum<T>> implements Converter<String, T> {
private Class<T> enumClass;
public CaseInsensitiveEnumConverter(Class<T> enumClass) {
this.enumClass = enumClass;
}
#Override
public T convert(String from) {
return T.valueOf(enumClass, from.toUpperCase());
}
}
Add in configuration
import com.example.enums.EnumA;
import com.example.enums.EnumB;
import com.example.enums.FormatEnum;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
List<Class<? extends Enum>> enums = List.of(EnumA.class, EnumB.class, FormatEnum.class);
enums.forEach(enumClass -> registry.addConverter(String.class, enumClass,
new CaseInsensitiveEnumConverter<>(enumClass)));
}
}
Related: Jackson databind enum case insensitive

How to implement multiton pattern using Spring Framework

How does one implement multiton pattern using Spring Framework's facilities?
https://en.wikipedia.org/wiki/Multiton_pattern
I want to write a factory which takes a pair of client and supplier as arguments. The factory should always return a bean of type T. For a given pair of client and supplier, the instance of T return should be a singleton, but for a different pair of client and supplier, it will be a different instance of T. Please suggest a way to implement this without implementing boilerplate code that Spring may already provide.
Interface ClientSdk {
sendRequestToClient();
}
class ClientASdk implements ClientSdk {
}
class ClientBSdk implements ClientSdk {
}
enum Client {
ClientA,
ClientB;
}
enum Supplier {
SupplierA,
SupplierB;
}
class ClientSupplier {
private Client client;
private Supplier supplier;
}
class SdkFactory {
public ClientSdk getClientSdk(ClientSupplier clientSupplier) {
//For a given ClientSupplier, always return the same
//ClientSupplier instance
}
}
#Service
class ClientRequestService {
public sendRequestToClient(ClientSupplier clientSupplier) {
ClientSdk clientSdk = SdkFactory.getSdk(clientSupplier);
clientSdk.sendRequestToClient();
}
}
Here's a solution to your problem. It does make SdkFactory a bean as #crizzis suggests, but it also creates bean instances for each ClientSdk instance so that each of them can be autowired or otherwise helped out by Spring. Note that I added an ident() method to the ClientSdk interface just to show that the MyClientSdk beans have in fact been autowired with the Spring Environment:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
interface ClientSdk {
void sendRequestToClient();
}
// This class is instantiated via a #Bean method inside SdkFactory. Since it is annotated as a Prototype bean,
// multiple instances of this class can be created as beans.
class MyClientSdk implements ClientSdk {
#Autowired
Environment environment;
private final String clientSupplier;
MyClientSdk(String clientSupplier) {
this.clientSupplier = clientSupplier;
System.out.printf("### Created MyClientSdk for: %s\n", clientSupplier);
}
public void sendRequestToClient() {
System.out.printf("Sending request for client: %s\n", clientSupplier);
System.out.printf("CS: %s Environment Prop: %s\n", clientSupplier, environment.getProperty("spring.application.name"));
}
}
#Component
class SdkFactory implements BeanFactoryAware {
private Map<String, ClientSdk> sdks = new HashMap<>();
private BeanFactory beanFactory;
// Here's the key logic to insure that we get just one instance of ClientSdk per clientSupplier value.
ClientSdk getClientSdk(String clientSupplier) {
if (!sdks.containsKey(clientSupplier))
sdks.put(clientSupplier, beanFactory.getBean(ClientSdk.class, clientSupplier));
return sdks.get(clientSupplier);
}
// This is probably the most important bit. This creates a Spring Bean unique to a particular 'clientSupplier'
// value, but only does so when requested so that the factory can control when these beans are created, creating
// only one per a particular `clientSupplier` value.
#Bean
#Scope("prototype")
ClientSdk createSdk(String clientSupplier) {
return new MyClientSdk(clientSupplier);
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
#Service
class ClientRequestService {
#Autowired
SdkFactory factory;
public void sendRequestToClient(String clientSupplier) {
ClientSdk clientSdk = factory.getClientSdk(clientSupplier);
clientSdk.sendRequestToClient();
}
}
#SpringBootApplication
public class HarmonyTestApp implements CommandLineRunner {
#Autowired
ClientRequestService service;
public static void main(String[] args) {
try {
ApplicationContext applicationContext = new SpringApplication(new Class<?>[]{HarmonyTestApp.class}).run(args);
} catch (Throwable e) {
e.printStackTrace();
}
}
#Override
public void run(String... args) {
service.sendRequestToClient("client1");
service.sendRequestToClient("client2");
service.sendRequestToClient("client1");
service.sendRequestToClient("client1");
service.sendRequestToClient("client2");
}
}
Result:
### Created MyClientSdk for: client1
Sending request for client: client1
CS: client1 Environment Prop: TestApp
### Created MyClientSdk for: client2
Sending request for client: client2
CS: client2 Environment Prop: TestApp
Sending request for client: client1
CS: client1 Environment Prop: TestApp
Sending request for client: client1
CS: client1 Environment Prop: TestApp
Sending request for client: client2
CS: client2 Environment Prop: TestApp
Note that per the output, each of client1's and client2's ClientSdk objects are only created once, even though they're used multiple times. Also notice that since the call to ident() in sendRequestToClient prints the value of a property obtained by an autowired Environment instance, autowiring of each ClientSdk object has worked.
I do realize that I used a String instead of a ClientSupplier object as the identifying key for each ClientSdk object. I did that just to keep the example as simple as I could. I expect you can expand the example to replace the clientSupplier String with an instance of ClientSupplier and somehow use that object as the key/identifier to insure that just one ClientSdk instance is created per ClientSupplier. That detail isn't really germain to the basic idea here.
Also, please note that the question itself changed while I was working on my implementation. Given that there are now exactly two subclasses of ClientSdk, you could simply make those regular #Component Spring beans. Having a small static number of those makes this problem less interesting. The technique I demonstrate here allows for an unlimited number of bean instances of ClientSdk class without having to define a unique class for each of them. This requires that Spring create arbitrary instances of them based on runtime information. This was what the original form of the question seemed to be asking for.

Generic JPA repository for runtime generated entities

In my scenario im generating hibernate entity classes at runtime under "com.mrg.domain" package. And in my generic restcontroller i can create instances of these entities according to #PathVariable. Below code works fine for this..
#RequestMapping( value = "/{entity}", method = RequestMethod.POST)
public #ResponseBody RestResponse createEntity(#PathVariable String entity, #RequestBody String requestBody) {
Object model = null;
ObjectMapper mapper = new ObjectMapper();
try {
// ex : if {entitiy} param is equal "post" modelName will be "Post"
String modelName = Character.toUpperCase(entity.charAt(0)) + entity.substring(1);
// Creating a new instance according to modelName
Class<?> clazz = Class.forName("com.mrg.domain." + modelName);
model = clazz.newInstance();
// Converting #RequestBody json String to domain object..
model = mapper.readValue(requestBody, clazz);
} catch(Exception ex){
// TODO handle exceptions & inform user..
}
return new RestResponse(model.toString());
}
Now the next step i am trying to implement is a generic jpa repository(something like below) so that i can persist runtime generated models without implementing repositories for each entity. But couldn't find a solution yet.
#Repository
public interface GenericRepository<T> extends PagingAndSortingRepository<T, Long>{ }
Below topic and many other topics implemented generic repositories but also repositories per entities that uses generic repo. Since i have runtime generated entities repo implementation per entity doesnt work for me..
How to make generic jpa repository? Should I do this? Why?
Any suggestion or a way for achieving this? I'm new to generics and reflection so if what im trying to accomplish is not possible, tell me reasons and i would be appreciate..
Thanks and regards,
You could use this pattern. This one uses EJB but can be used in Spring etc.
#Stateless
public abstract class AbstractRepository<T> {
#PersistenceContext
protected EntityManager em;
public abstract Class<T> getActualClass();
public T getSingleResult(Map<String, String> params) {
// build querytext based on params
TypedQuery<T> query = em.createQuery(queryText.toString(), getActualClass());
............
}
}
Now for the implementation class:
#Stateless
public class InputStreamRepository extends AbstractRepository<InputDataStream> {
#Override
public Class<InputDataStream> getActualClass() {
return InputDataStream.class;
}
}
The getActualClass method will give you the Entity's class impl info.
I had a react application where different data is defined in JSON and in the server side, I need to store this in the DB. My initial approach was to create entities , repositories and controller for all of this seperately. But another possible approach for CRUD operation is with MongoDB & JPA. Here is the idea.
import java.util.List;
import org.bson.Document;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/generic")
#CrossOrigin(origins = { "*" })
public class GenericController {
#Autowired
private MongoTemplate mongoTemplate;
#PostMapping
public ResponseEntity<Document> addData(#RequestBody String data) {
JSONObject jsonObject = new JSONObject(data);
String documentName = jsonObject.getString("documentName");
Document doc = Document.parse(data);
Document insertedDoc = mongoTemplate.insert(doc, documentName);
return new ResponseEntity<>(insertedDoc, HttpStatus.CREATED);
}
#GetMapping("/{documentName}")
public List<Document> getData(#PathVariable String documentName) {
List<Document> allData = mongoTemplate.findAll(Document.class, documentName);
return allData;
}
}

Unresolved Placeholder Validation for Spring Boot Configuration Properties

Given some application configuration with an unresolvable placeholder, like the following application.yml
my:
thing: ${missing-placeholder}/whatever
When I use #Value annotations, the placeholders in the configuration file are validated, so in this case:
package com.test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class PropValues {
#Value("${my.thing}") String thing;
public String getThing() { return thing; }
}
I get an IllegalArgumentException: Could not resolve placeholder 'missing-placeholder' in value "${missing-placeholder}/whatever". This is because the value is being set directly by AbstractBeanFactory.resolveEmbeddedValue and there is nothing to catch the exception thrown by PropertyPlaceholderHelper.parseStringValue
However, looking to move to #ConfigurationProperties style I noticed that this validation is missing, for example in this case:
package com.test;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
#ConfigurationProperties(prefix = "my")
public class Props {
private String thing;
public String getThing() { return thing; }
public void setThing(String thing) { this.thing = thing; }
}
there is no exception. I can see PropertySourcesPropertyValues.getEnumerableProperty catches the exception with the comment // Probably could not resolve placeholders, ignore it here and gathers the invalid value into its internal map. Subsequent data binding does not check for unresolved placeholders.
I checked that simply applying the #Validated and #Valid annotations to the class and field do not help.
Is there any way to preserve the behaviour of throwing an exception on unresolved placeholders with ConfigurationProperties binding?
Apparently there are no better solutions. At least this is kind of nicer than afterPropertiesSet().
#Data
#Validated // enables javax.validation JSR-303
#ConfigurationProperties("my.config")
public static class ConfigProperties {
// with #ConfigurationProperties (differently than #Value) there is no exception if a placeholder is NOT RESOLVED. So manual validation is required!
#Pattern(regexp = ".*\$\{.*", message = "unresolved placeholder")
private String uri;
// ...
}
UPDATE: I got the regex wrong the first time. It as to match the entire input (not just java.util.regex.Matcher#find()).
The correct regex to pass in #Pattern annotation is ^(?!\\$\\{).+
#Validated
#ConfigurationProperties("my.config")
public class ConfigProperties {
#Pattern(regexp = "^(?!\\$\\{).+", message = "unresolved placeholder")
private String uri;
// ...
}
I had the same issue exactly 10 minutes ago!
Try to add this bean in your configuration:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
return propertySourcesPlaceholderConfigurer;
}

Spring JsonExceptionMapper ExceptionMapper handling.

I am getting following error in my weblogic console when i am starting my server.
SEVERE: Missing dependency for constructor
public com.test.mine.exception.JsonExceptionMapper(java.lang.String,com.fasterxml.jackson.core.JsonLocation) at parameter index 0
SEVERE: Missing dependency for constructor public com.test.mine.exception.JsonExceptionMapper(java.lang.String,com.fasterxml.jackson.core.JsonLocation) at parameter index 1
Below is my java code.
package com.test.mine.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonParseException;
#Provider
#Service
public class JsonExceptionMapper extends JsonParseException implements ExceptionMapper {
public JsonExceptionMapper(String msg, JsonLocation loc) {
super(msg, loc);
// TODO Auto-generated constructor stub
}
private static final Logger LOGGER = LoggerFactory.getLogger(JsonExceptionMapper.class);
protected Logger getLogger() {
return LOGGER;
}
public Status getStatus(JsonParseException thr) {
return Status.BAD_REQUEST;
}
#Override
public Response toResponse(Throwable arg0) {
// TODO Auto-generated method stub
return Response.status(Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).build();
}
}
The annotation #Service tells spring to create a singleton of the annotated class. At startup spring tries to create that instance and to provide the required constructor args String msg, JsonLocation loc which it does not find, so the exception.
JsonExceptionMapper does not look like a service, and it should not be a singleton. Instead it must be created whenever an exception is created.
I have never worked with that class, so sorry, cannot give you any advice on how to do that.
I bumped into a similar problem while configuring swagger to work with Jersey. After searching various forums found that Jersey scanning require a constructor without parameters. I added a a constructor and it worked for me.

Resources