I am trying to setup my own JSF tag libary. So I created a composite component with an backing interfaces as a blueprint to build a backing bean for this component.
public interface CompLogin {
String getUsername();
void setUsername(String username);
String getPassword();
void setPassword(String password);
String validateLogin();
default String getPasswordWatermark() {
return "Passwort";
}
default String getUsernameWatermark() {
return "Loginname:";
}
default String getLoginButtonValue() {
return "Login";
}
}
So I have Password, Username and an Validate method for a Login site. Additionally O want serve some default namings for Inputtext watermarks and the Button. If the implementing person want to change it, he could.
I implemented this interface inside a Backing bean of a real application using my own JSF tag.
#Named
#RequestScoped
public class Login implements Serializable, CompLogin {
private String username;
private String password;
#Override
public String getUsername() {
return username;
}
#Override
public void setUsername(String username) {
this.username = username;
}
#Override
public String getPassword() {
return password;
}
#Override
public void setPassword(String password) {
this.password = password;
}
#Override
public String validateLogin() {
System.out.println(username + " " + password);
return null;
}
}
I tought it could work this way. But I get the error: 'Caused by: javax.el.PropertyNotFoundException: The class 'Login' does not have the property 'usernameWatermark'.'
It seems like the default implementation of the interface is not inherited in my implementing bean. Why and what could I do to serve default implementations for my components?
Edit:
I tried the following to ommit a missunderstanding of the interface default method conecpt. I took my interface and der implementing class in a normal java project tried to access the getLoginButtonValue thru the Login class.
public class Main {
public static void main(String[] args) {
Login log = new Login();
System.out.println(log.getLoginButtonValue());
}
}
Works very well. The correct String got printed out. There is no need to implement the default methods to access them. So where is the problem? Maybe there is something like cdi, bean-resolver or somthing else not aware of this java 8 concept?
Working with Apache EL this works by calling the default method by its full name. Try to use it this way in your code:
#{login.getUsernameWatermark()}
The problem is likely caused by EL relying on reflection to find the appropriate accessor methods, but doing it in a way that fails for default methods.
Consider implementing a custom ELResolver similar to what they did here.
Related
I user a CRUDRepository in my spring data redis project to persist a redis hash in my redis cluster. i have rest api written to persist and get thte values of the data. this works fine.
however my entity annotated with RedisHash is being saved as a set / and i am not able to look inside the value using redis cli.
how do i look inside a set data type(without popping) in redis cli
i looked at redis commands page https://redis.io/commands#set
i only get operations which can pop value . i neeed to simply peek
EDIT:
to make things clearer, i am using spring crudrepo to save the user entity into redis data store. the user entity gets saved as a set data type.
when i query back the user details, i can see entire details of the user
{
userName: "somak",
userSurName: "dattta",
age: 23,
zipCode: "ah56h"
}
i essentially want to do the same using redis cli... but all i get is
127.0.0.1:6379> smembers user
1) "somak"
how do i look inside the somak object.
#RestController
#RequestMapping("/immem/core/user")
public class UserController {
#Autowired
private UserRepository userRepository;
#RequestMapping(path = "/save", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(HttpStatus.OK)
public void saveUserDetails() {
User user = new User();
user.setAge(23);
user.setUserName("somak");
user.setUserSurName("dattta");
user.setZipCode("ah56h");
userRepository.save(user);
}
#RequestMapping(path="/get/{username}", method = RequestMethod.GET, produces = "application/json")
public User getUserDetails(#PathVariable("username") String userName) {
return userRepository.findById(userName).get();
}
}
#Repository
public interface UserRepository extends CrudRepository<User, String>{
}
#RedisHash("user")
public class User {
private #Id String userName;
private #Indexed String userSurName;
private #Indexed int age;
private String zipCode;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSurName() {
return userSurName;
}
public void setUserSurName(String userSurName) {
this.userSurName = userSurName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
}
I don't understant your descr with your problem, but I understand your title.
In redis set, the member is always string type.
I hope you can offer more info about UserRepository.save:
User user = new User();
user.setAge(23);
user.setUserName("somak");
user.setUserSurName("dattta");
user.setZipCode("ah56h");
userRepository.save(user);
And you can check your redis data and check data type when rest api invoked.
In our web app create and update forms have a size validation. For instance:
#Size(min = 4, max = 20)
private String mobile;
As seen the field is not required. But at the front-end user wants to clear field. Then form validation fails because of length restriction. Incoming data is an empty string instead of null. So minimum length validation restricts the input.
Therefore I start to search a solution to convert empty strings to null values. I found a #InitBinder and StringTrimmerEditor solution but our system uses #ResponseBody approach. So It doesn't fit.
Adding #JsonDeserialize(using = CustomTrimDeserializer.class) annotation or writing a custom setter for every string field is not DRY solution.
I just want to add app wide custom deserializer for String fields.
I finally examine the JsonComponentModule class and noticed spring is looking for the JsonComponent annotation for deserializer registration.
This is a one file spring boot project for solution
#RestController
#SpringBootApplication
public class CheckNullApplication {
public static void main(String[] args) {
SpringApplication.run(CheckNullApplication.class, args);
}
#PostMapping("/check-null")
public boolean checkNull(#RequestBody final HelloForm form) {
return form.getName() == null;
}
public static class HelloForm {
private String name;
public String getName() { return name; }
public void setName(final String name) { this.name = name;}
}
#JsonComponent
public static class StringTrimmerDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String result = StringDeserializer.instance.deserialize(p, ctxt);
if (result != null) {
result = result.trim();
if (StringUtils.isEmpty(result)) {
return null;
}
}
return result;
}
}
}
Instead of adding #JsonDeserialize annotation you may want to just register your custom deserializer via Module (for example, SimpleModule), and it will apply to all String valued properties. Something like:
SimpleModule module = new SimpleModule(...);
module.addDeserializer(String.class, new CustomTrimDeserializer());
mapper.registerModule(module);
Create a class as following and annotate with #JsonComponent. Spring boot will pick that up as a component.
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
#JsonComponent
public class WhitSpaceTrimmerDeserializer extends StringDeserializer {
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
final String value = super.deserialize(p, ctxt);
return value!=null?value.trim():null;
}
Bear with my English. I have a simple json,
{
"Hint2": "L"
}
this is the POJO that works.
public class Hints {
#SerializedName("Hint2")
#Expose
private String Hint2;
public void setHint1(Object Hint2) {
this.Hint2 = (Hint2);
}
}
i want to change it to
public class Hints {
#SerializedName("Hint2")
#Expose
public final ObservableField<String> Hint2 = new ObservableField<>();
public void setHint2(String Hint2) {
this.Hint2.set(Hint2);
}
}
both class has same setter method, same #SerializedName annotation tag. only type of Hint2 object is changed. but the latter one throws exception shown below
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at..
so i believe deserialization depends on what kind of variable "Hint2" is.
Is there a way to make it work with ObservableField rather than using String?
The reason i'm trying this is android binding library, which supports binding objects directly to xml files. and the ObservableField automatically updates UI when corresponding value in POJO is changed.
Update:
gson design document has this
Using fields vs getters to indicate Json elements
Some Json libraries use the getters of a type to deduce the Json elements. We chose to use all fields (up the inheritance hierarchy) that are not transient, static, or synthetic. We did this because not all classes are written with suitably named getters. Moreover, getXXX or isXXX might be semantic rather than indicating properties.
However, there are good arguments to support properties as well. We intend to enhance Gson in a latter version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based.
so this indicates that Gson is fields-based. this pretty much answers my question but still waiting if anyone has someway around this.
I came across the same requirements, and resolved it finally, here are the steps:
create the class GsonUtils:
public class GsonUtils {
// code following
//...
}
following code are in this class
write a customized serializer & deserializer:
private static class ObservableFieldSerializerDeserializer implements JsonSerializer>, JsonDeserializer> {
#Override
public JsonElement serialize(ObservableField src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.get());
}
#Override
public ObservableField deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final Type type = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
return new ObservableField((T) GsonUtils.getGson().fromJson(json, type));
}
}
you need to register ObservableField types to Gson:
private static GsonBuilder createGsonBuilder() {
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<ObservableField<String>>(){}.getType(), new ObservableFieldSerializerDeserializer<String>());
...// register more types which are wrapped by ObservableFields
return gsonBuilder;
}
create the Gson which is used by the deserializer
private static final Gson sGson = createGson();
private static Gson createGson() {
return createGsonBuilder().create();
}
// this is used by the deserializer
public static Gson getGson() {
return sGson;
}
that's all, hope it helps
I just ran into what I think is the same issue, and here is a JUnit4 test showing how I solved it with Jackson for a POJO, but of course String would work as well.
public class ObservableDeserializationTest {
private static class ObservableDeserializer extends JsonDeserializer<ObservableField> implements ContextualDeserializer {
private Class<?> mTargetClass;
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
mTargetClass = property.getType().containedType(0).getRawClass();
return this;
}
#Override
public ObservableField deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObservableField result = new ObservableField();
result.set(p.readValueAs(mTargetClass));
return result;
}
}
private static class SomePojo {
public String id;
public String name;
}
private static class ObservableTestClass {
#JsonDeserialize(using = ObservableDeserializer.class)
public ObservableField<SomePojo> testObj = new ObservableField<>();
}
#Test
public void DeserializingAnObservableObjectShouldSetValueCorrectly() {
ObservableTestClass tc = null;
try {
tc = new ObjectMapper().readValue("{\"testObj\":{\"name\":\"TestName\",\"id\":\"TestId\"}}", ObservableTestClass.class);
} catch (IOException e) {
e.printStackTrace();
}
Assert.assertEquals("TestName", tc.testObj.get().name);
Assert.assertEquals("TestId", tc.testObj.get().id);
}
}
The key is the ContextualDeserializer interface that allows extracting the contained class type. Jackson provides several options for registering a custom deserializer, so this is but one way of doing it. Also, it would probably be a good idea to override getNullValue as well in the deserializer if you would use this for real.
For a simple RESTful JSON api implemented in Spring MVC, can I use Bean Validation (JSR-303) to validate the path variables passed into the handler method?
For example:
#RequestMapping(value = "/number/{customerNumber}")
#ResponseBody
public ResponseObject searchByNumber(#PathVariable("customerNumber") String customerNumber) {
...
}
Here, I need to validate the customerNumber variables's length using Bean validation. Is this possible with Spring MVC v3.x.x? If not, what's the best approach for this type of validations?
Thanks.
Spring does not support #javax.validation.Valid on #PathVariable annotated parameters in handler methods. There was an Improvement request, but it is still unresolved.
Your best bet is to just do your custom validation in the handler method body or consider using org.springframework.validation.annotation.Validated as suggested in other answers.
You can use like this:
use org.springframework.validation.annotation.Validated to valid RequestParam or PathVariable.
*
* Variant of JSR-303's {#link javax.validation.Valid}, supporting the
* specification of validation groups. Designed for convenient use with
* Spring's JSR-303 support but not JSR-303 specific.
*
step.1 init ValidationConfig
#Configuration
public class ValidationConfig {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
return processor;
}
}
step.2 Add #Validated to your controller handler class, Like:
#RequestMapping(value = "poo/foo")
#Validated
public class FooController {
...
}
step.3 Add validators to your handler method:
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public ResponseEntity<Foo> delete(
#PathVariable("id") #Size(min = 1) #CustomerValidator int id) throws RestException {
// do something
return new ResponseEntity(HttpStatus.OK);
}
final step. Add exception resolver to your context:
#Component
public class BindExceptionResolver implements HandlerExceptionResolver {
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (ex.getClass().equals(BindException.class)) {
BindException exception = (BindException) ex;
List<FieldError> fieldErrors = exception.getFieldErrors();
return new ModelAndView(new MappingJackson2JsonView(), buildErrorModel(request, response, fieldErrors));
}
}
}
The solution is simple:
#GetMapping(value = {"/", "/{hash:[a-fA-F0-9]{40}}"})
public String request(#PathVariable(value = "hash", required = false) String historyHash)
{
// Accepted requests: either "/" or "/{40 character long hash}"
}
And yes, PathVariables are ment to be validated, like any user input.
Instead of using #PathVariable, you can take advantage of Spring MVC ability to map path variables into a bean:
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/{id}")
public void get(#Valid GetDto dto) {
// dto.getId() is the path variable
}
}
And the bean contains the actual validation rules:
#Data
public class GetDto {
#Min(1) #Max(99)
private long id;
}
Make sure that your path variables ({id}) correspond to the bean fields (id);
#PathVariable is not meant to be validated in order to send back a readable message to the user. As principle a pathVariable should never be invalid. If a pathVariable is invalid the reason can be:
a bug generated a bad url (an href in jsp for example). No #Valid is
needed and no message is needed, just fix the code;
"the user" is manipulating the url.
Again, no #Valid is needed, no meaningful message to the user should
be given.
In both cases just leave an exception bubble up until it is catched by
the usual Spring ExceptionHandlers in order to generate a nice
error page or a meaningful json response indicating the error. In
order to get this result you can do some validation using custom editors.
Create a CustomerNumber class, possibly as immutable (implementing a CharSequence is not needed but allows you to use it basically as if it were a String)
public class CustomerNumber implements CharSequence {
private String customerNumber;
public CustomerNumber(String customerNumber) {
this.customerNumber = customerNumber;
}
#Override
public String toString() {
return customerNumber == null ? null : customerNumber.toString();
}
#Override
public int length() {
return customerNumber.length();
}
#Override
public char charAt(int index) {
return customerNumber.charAt(index);
}
#Override
public CharSequence subSequence(int start, int end) {
return customerNumber.subSequence(start, end);
}
#Override
public boolean equals(Object obj) {
return customerNumber.equals(obj);
}
#Override
public int hashCode() {
return customerNumber.hashCode();
}
}
Create an editor implementing your validation logic (in this case no whitespaces and fixed length, just as an example)
public class CustomerNumberEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.hasText(text) && !StringUtils.containsWhitespace(text) && text.length() == YOUR_LENGTH) {
setValue(new CustomerNumber(text));
} else {
throw new IllegalArgumentException();
// you could also subclass and throw IllegalArgumentException
// in order to manage a more detailed error message
}
}
#Override
public String getAsText() {
return ((CustomerNumber) this.getValue()).toString();
}
}
Register the editor in the Controller
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(CustomerNumber.class, new CustomerNumberEditor());
// ... other editors
}
Change the signature of your controller method accepting CustomerNumber instead of String (whatever your ResponseObject is ...)
#RequestMapping(value = "/number/{customerNumber}")
#ResponseBody
public ResponseObject searchByNumber(#PathVariable("customerNumber") CustomerNumber customerNumber) {
...
}
You can create the answer you want by using the fields in the ConstraintViolationException with the following method;
#ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handlePathVariableError(final ConstraintViolationException exception) {
log.error(exception.getMessage(), exception);
final List<SisSubError> subErrors = new ArrayList<>();
exception.getConstraintViolations().forEach(constraintViolation -> subErrors.add(generateSubError(constraintViolation)));
final SisError error = generateErrorWithSubErrors(VALIDATION_ERROR, HttpStatus.BAD_REQUEST, subErrors);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
You need to added an #Validated annotation to Controller class and any validation annotation before path variable field
Path variable may not be linked with any bean in your system. What do you want to annotate with JSR-303 annotations?
To validate path variable you should use this approach Problem validating #PathVariable url on spring 3 mvc
Actually there is a very simple solution to this. Add or override the same controller method with its request mapping not having the placeholder for the path variable and throw ResponseStatusException from it. Code given below
#RequestMapping(value = "/number")
#ResponseBody
public ResponseObject searchByNumber() {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"customer number missing")
}
Having my ValueObject
UserVO {
long id;
String username;
}
I created custom editor for parsing this object from string id#username
public class UserVOEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
Preconditions.checkArgument(text != null,"Null argument supplied when parsing UserVO");
String[] txtArray = text.split("\\#");
Preconditions.checkArgument(txtArray.length == 2, "Error parsing UserVO. Expected: id#username");
long parsedId = Long.valueOf(txtArray[0]);
String username = txtArray[1];
UserVO uvo = new UserVO();
uvo.setUsername(username);
uvo.setId(parsedId);
this.setValue(uvo);
}
#Override
public String getAsText() {
UserVO uvo = (UserVO) getValue();
return uvo.getId()+'#'+uvo.getUsername();
}
in my controller i register
#InitBinder
public void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(UserVO.class, new UserVOEditor());
}
having in my model object ModelVO
ModelVO {
Set<UserVO> users = new HashSet<UserVO>();
}
after custom editor is invoked all you can see after form submission is
ModelVO {
Set<String> users (linkedHashSet)
}
so when trying to iterate
for(UserVO uvo : myModel.getUser()){ .. }
Im having classCastException .. cannot cast 1234#username (String) to UserVO ..
HOW THIS MAGIC IS POSSIBLE ?
It is not magic, it is because of Generics will be only proved at compile time. So you can put every thing in a Set at runtime, no one will check if you put the correct type in the Set.
What you can try, to make spring a bit more clever, is to put the ModelVO in your command object.
<form:form action="whatEver" method="GET" modelAttribute="modelVO">
#RequestMapping(method = RequestMethod.GET)
public ModelAndView whatEver(#Valid ModelVO modelVO){
...
}