'%X{value}' is not working in log4J print - spring-boot

Following an this code, im using MDC in order to have a unique ID per each request in my Spring-Boot application:
Slf4jMDCFilterConfiguration.java
#Data
#Configuration
public class Slf4jMDCFilterConfiguration {
public static final String DEFAULT_RESPONSE_TOKEN_HEADER = "Response_Token";
public static final String DEFAULT_MDC_UUID_TOKEN_KEY = "Slf4jMDCFilter.UUID";
public static final String DEFAULT_MDC_CLIENT_IP_KEY = "Slf4jMDCFilter.ClientIP";
private String responseHeader = DEFAULT_RESPONSE_TOKEN_HEADER;
private String mdcTokenKey = DEFAULT_MDC_UUID_TOKEN_KEY;
private String mdcClientIpKey = DEFAULT_MDC_CLIENT_IP_KEY;
private String requestHeader = null;
#Bean
public FilterRegistrationBean servletRegistrationBean() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
final Slf4jMDCFilter log4jMDCFilterFilter = new Slf4jMDCFilter(responseHeader, mdcTokenKey, mdcClientIpKey, requestHeader);
registrationBean.setFilter(log4jMDCFilterFilter);
registrationBean.setOrder(2);
return registrationBean;
}
}
Slf4jMDCFilter.java
#Data
#EqualsAndHashCode(callSuper = false)
#Component
public class Slf4jMDCFilter extends OncePerRequestFilter {
private final Logger log = Logger.getLogger(getClass());
private final String responseHeader;
private final String mdcTokenKey;
private final String mdcClientIpKey;
private final String requestHeader;
public Slf4jMDCFilter() {
responseHeader = Slf4jMDCFilterConfiguration.DEFAULT_RESPONSE_TOKEN_HEADER;
mdcTokenKey = Slf4jMDCFilterConfiguration.DEFAULT_MDC_UUID_TOKEN_KEY;
mdcClientIpKey = Slf4jMDCFilterConfiguration.DEFAULT_MDC_CLIENT_IP_KEY;
requestHeader = null;
}
public Slf4jMDCFilter(final String responseHeader, final String mdcTokenKey, final String mdcClientIPKey, final String requestHeader) {
this.responseHeader = responseHeader;
this.mdcTokenKey = mdcTokenKey;
this.mdcClientIpKey = mdcClientIPKey;
this.requestHeader = requestHeader;
}
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain)
throws java.io.IOException, ServletException {
try {
final String token = extractToken(request);
final String clientIP = extractClientIP(request);
MDC.put(mdcClientIpKey, clientIP);
MDC.put(mdcTokenKey, token);
if (!StringUtils.isEmpty(responseHeader)) {
response.addHeader(responseHeader, token);
}
chain.doFilter(request, response);
} finally {
MDC.remove(mdcTokenKey);
MDC.remove(mdcClientIpKey);
}
}
private String extractToken(final HttpServletRequest request) {
final String token;
if (!StringUtils.isEmpty(requestHeader) && !StringUtils.isEmpty(request.getHeader(requestHeader))) {
token = request.getHeader(requestHeader);
} else {
token = UUID.randomUUID().toString().toUpperCase().replace("-", "");
}
return token;
}
private String extractClientIP(final HttpServletRequest request) {
final String clientIP;
if (request.getHeader("X-Forwarded-For") != null) {
clientIP = request.getHeader("X-Forwarded-For").split(",")[0];
} else {
clientIP = request.getRemoteAddr();
}
return clientIP;
}
#Override
protected boolean isAsyncDispatch(final HttpServletRequest request) {
return false;
}
#Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
}
log4j.properties
# Define the properties for file appender
log4j.appender.FILE.layout.conversionPattern=%X{Slf4jMDCFilter.UUID}|[%d{ISO8601}][%p][%t] %C{1} %x - %m%n
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=logs/AmericanWell-FRE.log
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.MaxFileSize=20MB
log4j.appender.FILE.MaxBackupIndex=20
log4j.appender.FILE.append=true
log4j.rootCategory=ALL, rollingFile
#CONSOLE Settings
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.conversionPattern=%X{Slf4jMDCFilter.UUID}|[%d{ISO8601}][%p][%t] %C{1} %x - %m%n
My problem is when i'm trying to use %X{Slf4jMDCFilter.UUID}, in the log file and console i'm getting empty string.
Here's a sample output of my log - the token is being generated but the value is not printed:
|[2019-05-28 14:09:38,919][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 0******************************************************************************* token = null
|[2019-05-28 14:09:38,921][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 1******************************************************************************* token = 2A905F87CB84484B8EC05D6432D39303
|[2019-05-28 14:09:38,921][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 2******************************************************************************* token = 2A905F87CB84484B8EC05D6432D39303
|[2019-05-28 14:09:38,997][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 3******************************************************************************* token = 2A905F87CB84484B8EC05D6432D39303
|[2019-05-28 14:09:38,998][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 4******************************************************************************* token = 2A905F87CB84484B8EC05D6432D39303
|[2019-05-28 14:09:38,998][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 5******************************************************************************* token = null
|[2019-05-28 14:09:38,998][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 6******************************************************************************* token = null
|[2019-05-28 14:09:38,999][INFO][http-nio-8080-exec-1] Slf4jMDCFilter - 7******************************************************************************* token = null
I tried several ways to print it but nothing was working.
Am I missing some configuration here?

I too encountered this. The issue was that there were multiple MDCs in the project.
I was using org.slf4j.MDC to write the context. The Pattern, which uses %X{varname}, was set to use org.apache.log4j.MDC.
I changed my code to use org.apache.log4j.MDC and all was well.

Related

Spring Controller Test: Postman vs JUnit - Field error request rejected value [null]

I'm a beginner on Spring framework, trying to test the Controller.
The funny thing is, using Postman, I got the correct response, but not in JUnit where receive Actual :400 (bad request) instead of Expected :200.
This is due to empty field passengerCount because appears null. The class of the request is different of the response. This latter doesn't have a field for the passenger.
Controller
#Validated
#RestController
#RequestMapping("flights")
public class BusyFlightsController {
CrazyAirDatabase crazyAirService;
#Autowired
public BusyFlightsController(CrazyAirDatabase crazyAirService) {
this.crazyAirService = new CrazyAirDatabase();
}
#RequestMapping(value = "/crazy-air-response", method = RequestMethod.GET, produces = "application/json")
public List<CrazyAirResponse> getCrazyAirResponse(
#Valid CrazyAirRequest crazyAirRequest,
#RequestParam("origin") String origin,
#RequestParam("destination") String destination,
#RequestParam("departureDate") String departureDate,
#RequestParam("returnDate") String returnDate,
#RequestParam("passengerCount") int passengerCount
) {
crazyAirRequest = new CrazyAirRequest(origin, destination, departureDate, returnDate,
passengerCount);
return crazyAirService.getCrazyAirResponse(crazyAirRequest);
}
}
CrazyAirRequest class
public class CrazyAirRequest {
#IATACodeConstraint
private String origin;
#IATACodeConstraint
private String destination;
private String departureDate;
private String returnDate;
private int passengerCount;
public CrazyAirRequest(String origin, String destination, String departureDate,
String returnDate, int passengerCount) {
this.origin = origin;
this.destination = destination;
this.departureDate = departureDate;
this.returnDate = returnDate;
this.passengerCount = passengerCount;
}
// Getters
}
CrazyAirResponse class
public class CrazyAirResponse {
private String airline;
private double price;
private String cabinClass;
private String departureAirportCode;
private String destinationAirportCode;
private String departureDate;
private String arrivalDate;
public CrazyAirResponse(String airline, double price, String cabinClass, String departureAirportCode,
String destinationAirportCode, String departureDate, String arrivalDate) {
this.airline = airline;
this.price = price;
this.cabinClass = cabinClass;
this.departureAirportCode = departureAirportCode;
this.destinationAirportCode = destinationAirportCode;
this.departureDate = departureDate;
this.arrivalDate = arrivalDate;
}
// Getters
}
Repo CrazyAirDatabase
#Component
public class CrazyAirDatabase implements CrazyAirService {
List<CrazyAirResponse> list;
public CrazyAirDatabase() {
list = new ArrayList<>(
Arrays.asList(
new CrazyAirResponse("Ryanair", 125, "E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
}
#Override
public List<CrazyAirResponse> getCrazyAirResponse(CrazyAirRequest request) {
return list.stream()
.filter(t -> t.getDepartureAirportCode().equals(request.getOrigin()) &&
t.getDestinationAirportCode().equals(request.getDestination()) &&
t.getDepartureDate().equals(request.getDepartureDate()) &&
t.getArrivalDate().equals(request.getReturnDate())
)
.collect(Collectors.toList());
}
}
Test
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class BusyFlightsControllerTest {
#Autowired
MockMvc mockMvc;
#Mock
CrazyAirRequest crazyAirRequest;
#InjectMocks
private BusyFlightsController controller;
#Mock
CrazyAirService service;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testino() throws Exception {
crazyAirRequest = new CrazyAirRequest("LHR",
"BRN", "2018-10-08", "2020-10-08", 120);
List<CrazyAirResponse> crazyAirResponse = Arrays.asList(new CrazyAirResponse("Ryanair", 125,
"E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
when(service.getCrazyAirResponse(crazyAirRequest)).thenReturn(crazyAirResponse);
ObjectMapper objectMapper = new ObjectMapper();
String airplane = objectMapper.writeValueAsString(crazyAirResponse);
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
result.andExpect(status().isOk());
}
}
If I put this:
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response?origin=LHR&destination=CTA&departureDate=some&returnDate=some&passengerCount=1")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
Test is passed.
Then, need I perform Postman first, and after to copy and paste the query to pass the test?

Cannot remove attributes in ldap with spring ldap

we need to make a spring boot project that works with spring ldap.
every things is good.But when we remove a member from a group,the member deleted form group (i see it in debug mode in a Setmembers) but, in ldap(Oracle Internet Directory) that member exists!
Please help me!
//Group Entry
#Entry(objectClasses = {"top", "groupOfUniqueNames", "orclGroup"}, base = "cn=Groups")
public final class Group {
#Id
private Name dn;
#Attribute(name = "cn")
private String name;
private String description;
private String displayName;
#Attribute(name = "ou")
private String ou;
#Attribute(name = "uniqueMember")
private Set<Name> members;
public void addMember(Name newMember) {
members.add(newMember);
}
public void removeMember(Name member) {
members.remove(member);
}
//Custom LdapUtils
public class CustomLdapUtils {
private static final String GROUP_BASE_DN = "cn=Groups";
private static final String USER_BASE_DN = "cn=Users";
public Name buildGroupDn(String name) {
return LdapNameBuilder.newInstance(GROUP_BASE_DN)
.add("cn","Charts")
.add("cn",name)
.build();
}
private static final CsutomLdapUtils LDAP_UTILS = new CsutomLdapUtils ();
private CsutomLdapUtils () {
}
public Name buildPersonDn(String name) {
return LdapNameBuilder.newInstance(USER_BASE_DN)
.add("cn", name)
.build();
}
}
//Controller
#DeleteMapping(value = "/memberOfGroup", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> removeMemberFromGroup(#RequestBody Map<String,String> map) throws NamingException {
List<Group> groupToFind = ldapSearchGroupsService.getGroupByCn(map.get("groupName"));
List<User> userToFind = ldapSearchUserService.getAllUserByUserName(map.get("userName"));
if (groupToFind.isEmpty()) {
//TODO : Group no found!
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
for (Group group1 : groupToFind) {
group1.removeMember(userToFind.stream().findAny().get().getDn());
//ldapBindGroupService.deleteMemberFromGroup(group1);
DirContextOperations ctx = ldapTemplate.lookupContext(CustomLdapUtils.getInstance().buildGroupDn(map.get("groupName")));
ctx.removeAttributeValue("uniqueMember",map.get("userName"));
ctx.rebind(CustomLdapUtils.getInstance().buildGroupDn(map.get("groupName")),map.get("groupName"));
ldapTemplate.modifyAttributes(ctx);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}
Is some problem in code? or need some methods?
Finally after several search and debug,i found the problem!
In each ldap env,after every changes,the directory must be commit and apply.
In above code,i implemented that,but not in true way!
Best way is here:
#DeleteMapping(value = "/membersOfGroup", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> removeMemberFromGroup(#RequestBody Map<String,String> map) {
List<Group> groupToFind = ldapSearchGroupsService.getGroupByCn(map.get("groupName"));
List<User> userToFind = ldapSearchUserService.getAllUserByUserName(map.get("userName"));
if (groupToFind.isEmpty()) {
//TODO : Group no found!
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
for (Group group1 : groupToFind) {
group1.removeMember(userToFind.stream().findAny().get().getDn());
DirContextOperations ctx = ldapTemplate.lookupContext(CustomLdapUtils.getInstance().buildGroupDn(map.get("groupName")));
ctx.removeAttributeValue("member",CustomLdapUtils.getInstance().buildPersonDn(map.get("userName")));
//True way
ldapTemplate.update(group1);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

Spring security: Read jwt details

I'm receiving a jwt after a call to /login:
ResponseEntity<Void> responseEntity = restTemplate
.postForEntity(url, entity, Void.class);
String bearer = responseEntity
.getHeaders()
.get("Authorization")
.stream().findFirst().get();
After that, I'm getting a jwt token on Authorization header like:
"Bearer eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1Mzg1NjExNDgsInN1YiI6ImFkbWluIiwiZXhwIjoxNTM5NDI1MTQ4fQ.mr4MdNzNC8h1uq-OF9DeEBGS8AFgkN6ysooptNrvJeyyn6L6TLV1W4hv6osMggNpo_Ee6RqBhwuJu1beA8OFoA"
I would like to read expiration date and other information related with it.
Is there any library to handle it?
Does Spring provide any Helper to treat it?
Obviously, I've the secret key.
You can use jwt library to parse the jwt token you received.
For example, I am using jsonwebtoken to parse the token. Something
like this;
public Claims getClaimsFromToken(String token) throws Exception {
Claims claims;
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
return claims;
}
If you want to get the value of a specific key inside your claims, you
can do it like this;
public Object getClaimsValueFromToken(String token, String key) throws Exception {
Claims claims = getClaimsFromToken(token);
Object value = claims.get(key);
return value;
}
Ideally, you can create your own custom util using jwt library to
be used for parsing the token.
Don't forget also to add the library as a maven dependency;
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
here is the code sample from my project;
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
private static final String AUDIENCE_UNKNOWN = "unknown";
private static final String AUDIENCE_WEB = "web";
private static final String AUDIENCE_MOBILE = "mobile";
private static final String AUDIENCE_TABLET = "tablet";
#Value("${jwt.secret}")
private String secret;
#Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String token) {
final Claims claims = getClaimsFromToken(token);
return claims != null ? claims.getSubject() : null;
}
public Date getCreatedDateFromToken(String token) {
final Claims claims = getClaimsFromToken(token);
return claims != null ? new Date((Long) claims.get(CLAIM_KEY_CREATED)) : null;
}
public Date getExpirationDateFromToken(String token) {
final Claims claims = getClaimsFromToken(token);
return claims != null ? claims.getExpiration() : null;
}
public String getAudienceFromToken(String token) {
final Claims claims = getClaimsFromToken(token);
return claims != null ? (String) claims.get(CLAIM_KEY_AUDIENCE) : null;
}
private Claims getClaimsFromToken(String token) {
return StringUtils.hasText(token) ? Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody() : null;
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
private Boolean isTokenExpired(String token) {
final Date expirationDate = getExpirationDateFromToken(token);
return expirationDate.before(new Date());
}
private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
return (lastPasswordReset != null && created.before(lastPasswordReset));
}
private String generateAudience(Device device) {
String audience = AUDIENCE_UNKNOWN;
if (device.isNormal()) {
audience = AUDIENCE_WEB;
} else if (device.isTablet()) {
audience = AUDIENCE_TABLET;
} else if (device.isMobile()) {
audience = AUDIENCE_MOBILE;
}
return audience;
}
private Boolean ignoreTokenExpiration(String token) {
String audience = getAudienceFromToken(token);
return (AUDIENCE_TABLET.equals(audience) || AUDIENCE_MOBILE.equals(audience));
}
public String generateToken(UserDetails userDetails, Device device) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_AUDIENCE, generateAudience(device));
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
String generateToken(Map<String, Object> claims) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()).signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
final Date created = getCreatedDateFromToken(token);
return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && (!isTokenExpired(token) || ignoreTokenExpiration(token));
}
public String refreshToken(String token) {
String refreshedToken;
try {
final Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
public Boolean validateToken(String token, UserDetails userDetails) {
JwtUser user = (JwtUser) userDetails;
final String username = getUsernameFromToken(token);
final Date created = getCreatedDateFromToken(token);
return (username.equals(user.getUsername()) && !isTokenExpired(token) && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()));
}

Spring RESTFul Client – RestTemplate. Have weird somthing,Please let me know

I'm using ResTemplate to post User object in CLient. But When i use method postForObject then occur Unresolved compilation problem The method postForObject(URI, Object, Class<T>) in the type RestTemplate is not applicable for the arguments (URL, User, Class<User>). I really don't understand ???
Here file RestClientTest.java`
public class RestClientTest {
public static void main(String[] args) throws IOException{
// System.out.println("Rest Response" + loadUser("quypham"));
// URL url = new URL("http://localhost:8080/rest/user/create");
// rt.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// rt.getMessageConverters().add(new StringHttpMessageConverter());
// Map<String,String> vars = new HashMap<String,String>();
RestTemplate rt = new RestTemplate();
User user = new User();
user.setUserName("datpham");
user.setPassWord("12345");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR,1960);
user.setBirthDay(calendar.getTime());
user.setAge(12);
String uri = new String("http://localhost:8080/rest/user/create");
User returns = rt.postForObject(uri, user,User.class);
// createUser(user);
System.out.println("Rest Response" + loadUser("datpham"));
}
Here file UserRestServiceController
#Controller
public class UserRestServiceController {
#Autowired
public UserDao userDao;
#RequestMapping(value = "/rest/user/create",method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public void addUser(#RequestBody User user){
userDao.save(user);
}
I have edited String uri but encounter new following error:
Mar 29, 2016 1:57:43 PM org.springframework.web.client.RestTemplate
handleResponseError WARNING: POST request for
"http://localhost:8080/rest/user/create" resulted in 400 (Bad
Request); invoking error handler Exception in thread "main"
org.springframework.web.client.HttpClientErrorException: 400 Bad
Request at
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at
org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:588)
at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:546)
at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502)
at
org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:330)
at
edu.java.spring.service.client.RestClientTest.main(RestClientTest.java:45)
Here User.java
#Entity
#Table(name = "brotheruser",uniqueConstraints={#UniqueConstraint(columnNames="username")})
#JsonIgnoreProperties(ignoreUnknown = true)
public class User {
// #Enumerated(EnumType.STRING)
// #Column(name = "gender", nullable = false)
//
// public Gender getGender() {
// return gender;
// }
// public void setGender(Gender gender) {
// this.gender = gender;
// }
#Id
#Column(name = "username", unique = true, nullable = false)
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
#Column(name = "password", nullable = false)
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
#JsonSerialize(using = DateSerializer.class)
// #JsonDeserialize(using = DateDeserializer.class)
#Column(name = "birthday", nullable = false)
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
#Column(name="age", nullable = false)
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
private String userName;
private String passWord;
private Date birthDay;
private Integer age;
// private Gender gender;
}
Ok. I can't give the correct answer right now. My guess is thts in your User class.
The thing is with Http 400 errors, that you have to catch a detailed error message on the server side -to know what is exactly! going wrong. Define a globale exception handler in spring with #ControllerAdvide (and this will help you inwith everything you programm). It's not complicated! just copy & paste the two classes into your project and make sure they get executed, and you will get a detailed outup of the error on the console or the http log file if you don't have access to console
hope this helps ..
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.*;
import java.util.ArrayList;
import java.util.List;
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return super.handleNoHandlerFoundException(ex, headers, status, request);
}
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
return super.handleExceptionInternal(ex, body, headers, status, request);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
List<ObjectError> globalErrors = ex.getBindingResult().getGlobalErrors();
List<String> errors = new ArrayList<>(fieldErrors.size() + globalErrors.size());
String error;
for (FieldError fieldError : fieldErrors) {
error = fieldError.getField() + ", " + fieldError.getDefaultMessage();
errors.add(error);
}
for (ObjectError objectError : globalErrors) {
error = objectError.getObjectName() + ", " + objectError.getDefaultMessage();
errors.add(error);
}
RestResponseErrorMessage errorMessage = new RestResponseErrorMessage(errors);
return new ResponseEntity(errorMessage, headers, status);
}
#Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String unsupported = "Unsupported content type: " + ex.getContentType();
String supported = "Supported content types: " + MediaType.toString(ex.getSupportedMediaTypes());
RestResponseErrorMessage errorMessage = new RestResponseErrorMessage(unsupported, supported);
return new ResponseEntity(errorMessage, headers, status);
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Throwable mostSpecificCause = ex.getMostSpecificCause();
RestResponseErrorMessage errorMessage;
if (mostSpecificCause != null) {
String exceptionName = mostSpecificCause.getClass().getName();
String message = mostSpecificCause.getMessage();
errorMessage = new RestResponseErrorMessage(exceptionName, message);
} else {
errorMessage = new RestResponseErrorMessage(ex.getMessage());
}
return new ResponseEntity(errorMessage, headers, status);
}
}
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Created by pk on 08.03.2016.
*/
#XmlRootElement
public class RestResponseErrorMessage {
private List<String> errors;
public RestResponseErrorMessage() {
}
public RestResponseErrorMessage(List<String> errors) {
this.errors = errors;
}
public RestResponseErrorMessage(String error) {
this(Collections.singletonList(error));
}
public RestResponseErrorMessage(String ... errors) {
this(Arrays.asList(errors));
}
public List<String> getErrors() {
return errors;
}
public void setErrors(List<String> errors) {
this.errors = errors;
}
}
I think problem is with URL object. Try creating URI object or simple string like String url = "http://localhost:8080/rest/user/create";
RestTemplate supports URI or String as first parameter for for postForObject method

Spring Rest Issue

I am getting an error while i am trying to test my "testCreateUser" method using Spring RestApi, the uploadNewUser.xml contains the login information about the user and the role.
#Test
public void testCreateUser() throws Exception {
Reader reader = getFileReader("src/test/resources/uploadNewUser.xml");
String input_xml = IOUtils.toString(reader);
byte[] content = input_xml.getBytes();
request.addHeader("Accept", "application/xml");
request.addHeader("Content-Type", "application/xml");
request.setContent(content);
request.setContentType("text/xml");
request.setMethod(RequestMethod.POST.name());
request.setRequestURI("/restapi/users/");
final ModelAndView mav = handle(request, response);
Map<String, Object> map = mav.getModel();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
UserCollection collection = (UserCollection) entry.getValue();
org.springframework.validation.BindingResult.error = com.xxx.dashboard.restapi.GlobalResponse#42a4fd6d
error stack:
java.lang.ClassCastException: com.xxx.dashboard.restapi.GlobalResponse cannot be cast to com.xxx.dashboard.restapi.UserCollection
and i am getting an issue with cannot cast GlobalRespose to UserCollection. can anyone tell me where exactly i am doing is wrong? any help or pointers are most welcome thanks in advance
#Controller("userrestapi")
#RequestMapping(value = { "/restapi/users/", "/restapi/users" })
public class UserRestApi extends AbstractBaseApi {
...
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.CREATED)
public ModelAndView createNewUser(#RequestBody UserCollection userCollection,
#RequestHeader(value = "accept", required = false) String accept,
#RequestHeader(value = "version", required = false) String version) {
try {
OOUser ooUser = userCollection.getUsers().get(0);
Mapper mapper = (Mapper) userVersions.get(Constants.USER_DETAIL_VERSION_MAPPER_KEY);
int userId = usersRestApiService.validateAndCreateNewUser(ooUser, mapper);
List<FilterField> filterFieldList = new ArrayList<FilterField>();
filterFieldList.add(new FilterField("userId", String.valueOf(userId)));
return getUserDetailsForFilter(filterFieldList, accept, version, mapper);
} catch (Exception ex) {
logger.warn("Api exception", ex);
return getModelAndView(accept, "error", getGlobalResponse(ex));
}
the abstractbaseapi contains following
public class AbstractBaseApi {
public static final String XML_VIEW = "apiXmlView";
public static final String JSON_VIEW = "apiJsonView";
public static final String JSON_ACCEPT_HEADER = "application/json";
public static final String JSON_CONTENT_HEADER = "Content-type: application/json";
public static final String XML_CONTENT_HEADER = "Content-type: text/html;charset=utf-8";
public static final int MAX_COUNT = 100;
public static final String XML_REQUEST_ERROR_FORMAT = "<?xml version='1.0' encoding='UTF-8'?><GlobalResponse xmlns='http://www.operative.com/api' xmlns:v2='http://www.operative.com/api/v2' xmlns:v1='http://www.operative.com/api/v1'> <error errorCode='%1$s' text='%2$s'/> </GlobalResponse>";
public static final String JSON_REQUEST_ERROR_FORMAT = "{error:{errorCode:'%1$s',text:'%2$s'}}";
protected final Logger logger = Logger.getLogger(this.getClass());
protected ModelAndView getModelAndView(String accept, String key, Object value) {
String view = XML_VIEW;
if (accept != null && accept.toLowerCase().contains(JSON_ACCEPT_HEADER)) {
view = JSON_VIEW;
}
if (logger.isDebugEnabled()) {
logger.debug("Accept Header:" + accept + " , generating:" + view);
}
return new ModelAndView(view, BindingResult.MODEL_KEY_PREFIX + key, value);
}
Your model contains more than you think.
You are going through your model and looking for your user collection. However, the first encountered object in your map seems to be the GlobalResponse map.
You should probably just get it by name from the model, i.e.
UserCollection collection = (UserCollection) mav.getModel().get("userCollection");
rather than iterating..

Resources