Struts2 setter method for POST parameter with dash - java

I have mailgun.com routes to forward email POST to my Struts2 Action. The mailgun POST request content dashes in some parameter names: sender, subject (OK) but how can I map setter method in my Struts2 Action for POST parameter: body-plain (contain dash), body-html, message-headers, Content-Type ?
I have try this but not work bodyPlain=null:
public class MyAction extends ActionSupport {
private String sender;
private String subject;
private String bodyPlain;
public String execute() {
LOG.info(sender);
LOG.info(subject);
LOG.info(bodyPlain);
return SUCCESS;
}
public void setSender(String sender) {
this.sender = sender;
}
public void setSubject(String subject) {
this.subject = subject;
}
public void setBodyPlain(String bodyPlain) {
this.bodyPlain = bodyPlain;
}
}

I can`t change mailgun service code. This is my code to deal with Struts2 naming conventions. If its have more clean solution for this problem let me know.
public class MyAction extends ActionSupport implements ParameterAware {
private String sender;
private String subject;
private String bodyPlain;
public String execute() {
LOG.info(sender);
LOG.info(subject);
LOG.info(bodyPlain);
return SUCCESS;
}
public void setSender(String sender) {
this.sender = sender;
}
public void setSubject(String subject) {
this.subject = subject;
}
#Override
public void setParameters(Map<String, String[]> parameters) {
if (parameters != null) {
for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
String key = entry.getKey();
String[] value = entry.getValue();
if(key.equals("body-plain") && value.length > 0) this.bodyPlain = value[0];
LOG.info("key:" + key + " value:" + Arrays.toString(value));
}
}
}
}

Then you need to rename body-plain to bodyPlain to map parameters to the action fields. Java and naming conventions use camelCased instance variables for classes. And this is an obligatory rule to map request parameters to the action class.
You can translate/rename them on any level, filter, url-rewrite rule, interceptor, etc. Any way parameters are passed to the ActionContext that is a map, then traversing the map and remove dashes from the key names would be easy if you try
Map<String, Object[]> parameters = ActionContext.getContext().getParameters();

Related

Spring Enum list empty in #RequestBody

I have the below enum with two values, and i have a search api with many fields, One of these fields is a list of StatusEnum. So i created a dto that contains this field.
The problem when i send the data the list status is always empty
json exp: {"status":["En activité"],"startDate":null,"endDate":null}
public enum StatusEnum {
INACTIVITY, ENDACTIVITY;
private static Map<String, StatusEnum > namesMap = new HashMap<>(2);
static {
namesMap.put("En activité", INACTIVITY);
namesMap.put("En fin d'activité", ENDACTIVITY);
}
#JsonCreator
public static StatusEnum forValue(String value) {
return namesMap.get(StringUtils.lowerCase(value));
}
#JsonValue
public String toValue() {
for (Entry<String, StatusEnum > entry : namesMap.entrySet()) {
if (entry.getValue() == this)
return entry.getKey();
}
return null;
}
}
#PostMapping
public ResponseEntity<List<Object>> search(#RequestBody SearchDTO search) { }
public class SearchDTO {
private Date startDate;
private Date endDate
private List<StatusEnum> status;
//getter and setter
}
#JsonCreator
public static StatusEnum forValue(String value) {
return namesMap.get(StringUtils.lowerCase(value));
}
Problem is the usage of #lowerCase in forValue!
Your keys in your map aren't lower-cased. That's why namesMap.get can't find anything.

Deserialise a POJO in Kafka Streams

My Kafka topic has messages of this format
user1,subject1,80|user1,subject2,90
user2,subject1,70|user2,subject2,100
and so on.
I have created User POJO as below.
class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = -253687203767610477L;
private String userId;
private String subject;
private String marks;
public User(String userId, String subject, String marks) {
super();
this.userId = userId;
this.subject = subject;
this.marks = marks;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getMarks() {
return marks;
}
public void setMarks(String marks) {
this.marks = marks;
}
}
Further I have created default key value serialization
streamProperties.put(
StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
streamProperties.put(
StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
I am trying to find count by userID as follows. Also I need User object to perform some other functionalities.
KTable<String, Long> wordCount = streamInput
.flatMap(new KeyValueMapper<String, String, Iterable<KeyValue<String,User>>>() {
#Override
public Iterable<KeyValue<String, User>> apply(String key, String value) {
String[] userObjects = value.split("|");
List<KeyValue<String, User>> userList = new LinkedList<>();
for(String userObject: userObjects) {
String[] userData = userObject.split(",");
userList.add(KeyValue.pair(userData[0],
new User(userData[0],userData[1],userData[2])));
}
return userList;
}
})
.groupByKey()
.count();
I am getting the below error
Caused by: org.apache.kafka.streams.errors.StreamsException: A serializer (key: org.apache.kafka.common.serialization.StringSerializer / value: org.apache.kafka.common.serialization.StringSerializer) is not compatible to the actual key or value type (key type: java.lang.String / value type: com.example.testing.dao.User). Change the default Serdes in StreamConfig or provide correct Serdes via method parameters.
I think I need to provide correct Serde for User Class.
The problem is with Value Serdes.
There are two version of function groupBy:
KStream::KGroupedStream<K, V> groupByKey();
KStream::KGroupedStream<K, V> groupByKey(final Grouped<K, V> grouped);
First version under the hood call second with Grouped with default Serdes (In your case it was for key and value StringSerde
Your flatMap map message to KeyValue<String, User> type so value was of type User.
Solution in your case would be instead using groupByKey() call groupByKey(Grouped.with(keySerde, valSerde));, with proper Serdes.

How to tell Swagger an attribute is a Map

I am developing a restful service with Jersey. However, I am using Swagger for documentation. My Model has a property with Type of Map. Swagger shows that this attribute as an Object (not a specific type). So how can I tell Swagger that this property is from type Map ?
public class Model {
private String name;
private Map<Integer, String> myMap;
public Model(){
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<Integer, String> getMyMap() {
return myMap;
}
public void setMyMap(Map<Integer, String> myMap) {
this.myMap = myMap;
}
}
The restful service:
#POST
#Path("/createBundle")
#Consumes({MediaType.APPLICATION_JSON})
#ApiOperation(value = "Create Bundle ",
notes = "",
response = Model.class)
public Model createBundle(Bundle bundle){
return new Model();
}
I need Swagger to show it as type of Map<Integer, String>.
Swagger shows the documentation as this image.
You can set a response type in the #ApiOperation annotation:
#ApiOperation(value = "Find thingies as Map",
notes = "Multiple thingies can be returned, <code>id</code> is the ID field",
response = java.util.Map.class)

How to serialize java objects as field.path = field.value

I have the following model classes:
package com.ab.model;
import java.util.List;
public class Request {
public Request(String requestType, Body body, List<String> emails) {
this.requestType = requestType;
this.body =body;
this.emails = emails;
}
private String requestType;
private Body body;
private List<String> emails;
public String getRequestType() {
return requestType;
}
public void setRequestType(String requestType) {
this.requestType = requestType;
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
public List<String> getEmails() {
return emails;
}
public void setEmails(List<String> emails) {
this.emails = emails;
}
}
class Body {
private String content;
private List<Header> headers;
public Body(String content, List<Header> headers) {
this.content = content;
this.headers = headers;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public List<Header> getHeaders() {
return headers;
}
public void setHeaders(List<Header> headers) {
this.headers = headers;
}
}
class Header {
private String headerName;
public Header (String headerName) {
this.headerName = headerName;
}
public String getHeaderName() {
return headerName;
}
public void setHeaderName(String headerName) {
this.headerName = headerName;
}
}
And the following instance of the Request class:
Request request = new Request(
"get",
new Body("abcdefg",
Arrays.asList(new Header("header_one"))),
Arrays.asList("a#a.com", "b#b.com"));
Do you know any library or algorithm that can serialize the request object into the following string?
requestType = "get"
body.content = "abcdefg"
body.headers[0].headerName = "header_one"
emails[0] = "a#a.com"
emails[1] = "b#b.com"
I know I can serialize it as json, xml, etc, but these don't fit my use case.
Basically I need a serialization like:
field.nestedField.reallyNestedField = "its primitive value"
As a next step, I am planning to read the generated string and generate arbitrary data for each field/nestedField then deserialize it back using PropertyUtils from Apache e.g.:
PropertyUtils.setProperty(requestObject, "requestType", "random type");
PropertyUtils.setProperty(requestObject, "body.content", "random content");
//...
Many thanks!
Andrei
What about overriding your toString() default methods to read and output your member variables as text. You can use super to refer to your SuperClass and it's members.
PS: You don't have default constructors in your classes! In case you have constructors with your argument list it is suggested to include your no-argument default constructor in your class! Especially in case you are implementing some logic related to serialisation / deserialisation!
You can iterate and recurse on the class/object properties using Commons PropertyUtils.
Depending on how complex your implementation is, you might need to do some type checking for primitive/wrapper/collection types (the below leverages Commons ClassUtils).
public static List<String> getPropertyDescriptorPaths(Class<?> clazz) {
return getPropertyDescriptorPaths("", clazz);
}
private static List<String> getPropertyDescriptorPaths(String prefix, Class<?> clazz) {
List<String> paths = new ArrayList<>();
PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(clazz);
for (PropertyDescriptor pd : descriptors) {
if (isSimpleType(pd.getPropertyType())) {
paths.add(prefix + pd.getName());
} else if (!pd.getName().equals("class")) {
paths.addAll(getPropertyDescriptorPaths(pd.getName() + ".", pd.getPropertyType()));
}
}
return paths;
}
private static boolean isSimpleType(Class<?> clazz) {
return ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.equals(String.class) || isCollectionOrArray(clazz);
}
private static boolean isCollectionOrArray(Class<?> clazz) {
return isCollection(clazz) || clazz.isArray();
}
private static final List<Class<?>> COLLECTION_TYPES = Arrays.asList(new Class<?>[] { List.class, Map.class, Set.class });
private static boolean isCollection(Class<?> clazz) {
for (Class<?> eachClass : COLLECTION_TYPES) {
if (eachClass.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
The condition for comparing property name to class is because each object has a getClass() method, and we don't care about that.
Using this with your classes, we get the result:
System.out.println(getPropertyDescriptorPaths(Request.class));
// [emails, requestType, body.headers, body.content]

using OVal validation framework in Java

I use OVal framework for validate business objects in my java projects.
please see validation class :
public class ObjectValidation implements ObjectValidatable {
private IValidator validator;
private Map<String, String> errorMessages;
public ObjectValidation() {
validator = new Validator();
}
public boolean isValid() {
if (validate().size() > 0)
return false;
return true;
}
public List<ConstraintViolation> validate() {
return validator.validate(this);
}
public Map<String, String> getErrorMessages()
{
if(this.isValid()) return null;
errorMessages = new HashMap<String, String>();
for(ConstraintViolation cv : this.validate())
errorMessages.put(cv.???, cv.getMessage());
return errorMessages;
}
}
AND
public class Account extends DomainObject {
#NotNull
#NotEmpty
#NotBlank
#Length(max = 5)
private String userName; // How Get This ???
private String password;
private int securityId;
private String securityAnswer;
...
}
I have getErrorMessages that return Map
I want it return like this userName-must be not null
second section "must be not null" can get with cv.getMessage()
but first section that have validation annotation is my question
How get userName or another fields that have validation annotation ???
Have a look at the ConstraintViolation.getCauses() and the ConstraintViolation.getContext() method. They should provide what you need.

Categories